rails_age 0.6.3 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -2
- data/lib/apache_age/entities/query_builder.rb +47 -97
- data/lib/rails_age/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0856a8b6ebd00d824cb655f76712731b5f1034250520ddf90008099863ce70ff'
|
4
|
+
data.tar.gz: 38bfd628af67308dc59ec42eb3c7e89e6f2391c9bc4d84fd27fd24dfba89a4cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7f10fc55865bda5020d850470e103b1e8172e3b6fcf1be8a9fe7ecbac2b8617326cdbd3048bf241b5f5fe5a72a414d67f1f58bc2d6d06df4faa7d426ab49e86
|
7
|
+
data.tar.gz: ba30bcf0dabd7a66e68038d0314379663e1a6058f2b433947d961588cd593b3c318f3eec81ef7691d007029efbafa64d3cc2ba7fe8d92b56f9d51b31a7cf8d7b
|
data/CHANGELOG.md
CHANGED
@@ -35,10 +35,9 @@ breaking change?: namespaces (by default) will use their own schema? (add to dat
|
|
35
35
|
- **Age Path** - nodes and edges combined
|
36
36
|
* add `rails generate apache_age:path_scaffold HasJob employee_role start_node:person end_node:company`
|
37
37
|
|
38
|
-
## VERSION 0.6.4 - 2024-
|
38
|
+
## VERSION 0.6.4 - 2024-10-30
|
39
39
|
|
40
40
|
- **Query Sanitize**:
|
41
|
-
* reject attributes not defined in model (throw error?)
|
42
41
|
* allow and sanitize query strings with multiple attributes, ie: `Person.where("find.first_name = ? AND find.last_name = ?", 'John', 'Doe')`
|
43
42
|
|
44
43
|
## VERSION 0.6.3 - 2024-10-27
|
@@ -29,6 +29,7 @@ module ApacheAge
|
|
29
29
|
@graph_name = graph_name || model_class.age_graph
|
30
30
|
end
|
31
31
|
|
32
|
+
# TODO: allow for multiple graphs
|
32
33
|
# def cypher(graph_name = 'age_schema')
|
33
34
|
# return self if graph_name.blank?
|
34
35
|
|
@@ -41,38 +42,22 @@ module ApacheAge
|
|
41
42
|
self
|
42
43
|
end
|
43
44
|
|
44
|
-
# # TODO: need to handle string inputs too: instead of: \
|
45
|
-
# # "id(find) = #{id}" & "find.name = #{name}"
|
46
|
-
# # we can have: "id(find) = ?", id & "find.name = ?", name
|
47
|
-
# # ActiveRecord::Base.sanitize_sql([query_string, v])
|
48
45
|
def where(*args)
|
49
46
|
return self if args.blank?
|
50
47
|
|
51
48
|
@where_clauses <<
|
52
|
-
# not able to sanitize the query string in this case
|
53
|
-
# ["first_name = 'Barney'"]
|
49
|
+
# not able to sanitize the query string in this case: `["first_name = 'Barney'"]`
|
54
50
|
if args.length == 1 && args.first.is_a?(String)
|
55
|
-
|
56
|
-
|
57
|
-
"id(find) = ?"
|
58
|
-
elsif string_query.include?('id(') || string_query.include?('find.')
|
59
|
-
string_query
|
60
|
-
else
|
61
|
-
"find.#{string_query}"
|
62
|
-
end
|
51
|
+
raw_query_string = args.first
|
52
|
+
transform_cypher_sql(raw_query_string)
|
63
53
|
|
64
54
|
# Handling & sanitizing parameterized string queries
|
65
55
|
elsif args.length > 1 && args.first.is_a?(String)
|
66
56
|
raw_query_string = args.first
|
67
|
-
|
68
|
-
|
69
|
-
"id(find) = ?"
|
70
|
-
elsif raw_query_string.include?('id(') || raw_query_string.include?('find.')
|
71
|
-
raw_query_string
|
72
|
-
else
|
73
|
-
"find.#{raw_query_string}"
|
74
|
-
end
|
57
|
+
# Replace `id = ?` with `id(find) = ?` and `first_name = ?` with `find.first_name = ?`
|
58
|
+
query_string = transform_cypher_sql(raw_query_string)
|
75
59
|
values = args[1..-1]
|
60
|
+
# sanitize sql input values
|
76
61
|
ActiveRecord::Base.sanitize_sql_array([query_string, *values])
|
77
62
|
|
78
63
|
# Hashes are sanitized in the model class
|
@@ -93,71 +78,11 @@ module ApacheAge
|
|
93
78
|
self
|
94
79
|
end
|
95
80
|
|
96
|
-
# # where is sanitized in the model class with hash values
|
97
|
-
# def where(attributes)
|
98
|
-
# return self if attributes.blank?
|
99
|
-
|
100
|
-
# @where_clauses <<
|
101
|
-
# if attributes.is_a?(String)
|
102
|
-
# puts "HANDLE PURE STRING QUERIES"
|
103
|
-
# if attributes.include?('id(') || attributes.include?('find.')
|
104
|
-
# attributes
|
105
|
-
# else
|
106
|
-
# "find.#{attributes}"
|
107
|
-
# end
|
108
|
-
# else
|
109
|
-
# puts "HANDLE HASHES"
|
110
|
-
# pp attributes
|
111
|
-
# edge_keys = [:start_id, :start_node, :end_id, :end_node]
|
112
|
-
# if edge_keys.any? { |key| attributes.include?(key) }
|
113
|
-
# puts "HANDLE EDGE CLAUSES"
|
114
|
-
# model_class.send(:where_edge_clause, attributes)
|
115
|
-
# else
|
116
|
-
# puts "HANDLE NODE CLAUSES"
|
117
|
-
# model_class.send(:where_node_clause, attributes)
|
118
|
-
# end
|
119
|
-
# end
|
120
|
-
|
121
|
-
# self
|
122
|
-
# end
|
123
|
-
|
124
|
-
# # Pre-sanitize where statements
|
125
|
-
# # def where(*args)
|
126
|
-
# # return self if args.blank?
|
127
|
-
|
128
|
-
# # # Handling parameterized query strings with values
|
129
|
-
# # if args.length == 1 && args.first.is_a?(Hash)
|
130
|
-
# # # If a hash of attributes is provided, use the existing logic
|
131
|
-
# # attributes = args.first
|
132
|
-
# # edge_keys = [:start_id, :start_node, :end_id, :end_node]
|
133
|
-
# # if edge_keys.any? { |key| attributes.include?(key) }
|
134
|
-
# # @where_clauses << model_class.send(:where_edge_clause, attributes)
|
135
|
-
# # else
|
136
|
-
# # @where_clauses << model_class.send(:where_node_clause, attributes)
|
137
|
-
# # end
|
138
|
-
# # elsif args.length > 1 && args.first.is_a?(String)
|
139
|
-
# # # If a query string with placeholders and values is provided
|
140
|
-
# # query_string = args.first
|
141
|
-
# # values = args[1..-1]
|
142
|
-
# # sanitized_query = ActiveRecord::Base.send(:sanitize_sql_array, [query_string, *values])
|
143
|
-
# # @where_clauses << sanitized_query
|
144
|
-
# # elsif args.length == 1 && args.first.is_a?(String)
|
145
|
-
# # # If a single string is provided, use it directly (assuming it is already sanitized or trusted)
|
146
|
-
# # @where_clauses << args.first
|
147
|
-
# # else
|
148
|
-
# # raise ArgumentError, "Invalid arguments for `where` method"
|
149
|
-
# # end
|
150
|
-
|
151
|
-
# # self
|
152
|
-
# # end
|
153
|
-
|
154
81
|
# New return method
|
155
82
|
def return(*variables)
|
156
83
|
return self if variables.blank?
|
157
84
|
|
158
85
|
@return_variables = variables
|
159
|
-
# @return_names = variables.empty? ? ['find'] : variables
|
160
|
-
# @return_clause = variables.empty? ? 'find' : "find.#{variables.join(', find.')}"
|
161
86
|
self
|
162
87
|
end
|
163
88
|
|
@@ -243,22 +168,47 @@ module ApacheAge
|
|
243
168
|
$$) AS (#{return_names.join(' agtype, ')} agtype);
|
244
169
|
SQL
|
245
170
|
end
|
246
|
-
# def build_query(_extra_clause = nil)
|
247
|
-
# sanitized_where_sql = where_clauses.any? ? "WHERE #{where_clauses.map { |clause| ActiveRecord::Base.sanitize_sql_like(clause) }.join(' AND ')}" : ''
|
248
|
-
# sanitized_order_by = order_clause.present? ? ActiveRecord::Base.sanitize_sql_like(order_clause) : ''
|
249
|
-
# sanitized_limit_clause = limit_clause.present? ? ActiveRecord::Base.sanitize_sql_like(limit_clause) : ''
|
250
171
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
172
|
+
def transform_cypher_sql(raw_sql_string)
|
173
|
+
# Define the logical operators and order multi-word operators first to avoid partial splits
|
174
|
+
operators = ['=', '>', '<', '<>', '>=', '<=', '=~', 'ENDS WITH', 'CONTAINS', 'STARTS WITH', 'IN', 'IS NULL', 'IS NOT NULL']
|
175
|
+
separators = ["AND NOT", "OR NOT", "AND", "OR", "NOT"]
|
176
|
+
|
177
|
+
# Combine the operators and separators into a regex pattern
|
178
|
+
pattern = /(#{(operators + separators).map { |s| Regexp.escape(s) }.join('|')})/
|
179
|
+
|
180
|
+
# Split the raw_sql_string string based on the pattern for operators and separators
|
181
|
+
parts = raw_sql_string.split(pattern)
|
182
|
+
|
183
|
+
# Process each part to identify and transform the attributes
|
184
|
+
transformed_parts = parts.map do |part|
|
185
|
+
# Skip transformation if part is one of the logical operators or separators
|
186
|
+
next part if operators.include?(part.strip) || separators.include?(part.strip)
|
187
|
+
|
188
|
+
if part.include?(".")
|
189
|
+
part # Keep parts with prefixes as they are
|
190
|
+
elsif part =~ /\s*(\w+)\s*$/
|
191
|
+
attribute = $1
|
192
|
+
if attribute == 'end_id'
|
193
|
+
"id(end_node)"
|
194
|
+
elsif attribute == 'start_id'
|
195
|
+
"id(start_node)"
|
196
|
+
elsif attribute == 'id'
|
197
|
+
"id(find)"
|
198
|
+
# attributes must start with a letter
|
199
|
+
elsif attribute =~ /^[a-z]\w*$/
|
200
|
+
"find.#{attribute}"
|
201
|
+
else
|
202
|
+
attribute
|
203
|
+
end
|
204
|
+
else
|
205
|
+
part
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Reassemble the string with the transformed parts
|
210
|
+
transformed_parts.join(" ").gsub(/\s+/, ' ').strip
|
211
|
+
end
|
262
212
|
end
|
263
213
|
end
|
264
214
|
end
|
data/lib/rails_age/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_age
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bill Tihen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|