rails_age 0.6.3 → 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|