rails_age 0.6.2 → 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 +7 -7
- data/lib/apache_age/entities/class_methods.rb +2 -2
- data/lib/apache_age/entities/query_builder.rb +67 -33
- 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,21 +35,21 @@ 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-10-30
|
38
39
|
|
39
|
-
|
40
|
+
- **Query Sanitize**:
|
41
|
+
* allow and sanitize query strings with multiple attributes, ie: `Person.where("find.first_name = ? AND find.last_name = ?", 'John', 'Doe')`
|
42
|
+
|
43
|
+
## VERSION 0.6.3 - 2024-10-27
|
40
44
|
|
41
45
|
- **Query Sanitize**:
|
42
|
-
* reject attributes not defined in model
|
43
46
|
* sanitize strings using: id(find) = ?, 23 & find.first_name = ?, 'John'
|
47
|
+
NOTE: this sanitization only works (so far) for strings containing ONE attribute. ie: `Person.where("find.first_name = ?", 'John')` or `Person.where("first_name = ?", 'John')` works but `Person.where("find.first_name = ? AND find.last_name = ?", 'John', 'Doe')` does not yet work
|
44
48
|
|
45
49
|
## VERSION 0.6.2 - 2024-09-30
|
46
50
|
|
47
51
|
- **Query Sanitize**
|
48
|
-
*
|
49
|
-
|
50
|
-
- **TODO**:
|
51
|
-
* reject attributes not defined in model
|
52
|
-
* sanitize strings using: id(find) = ?, 23 & find.first_name = ?, 'John'
|
52
|
+
* hash queries sanitized
|
53
53
|
|
54
54
|
## VERSION 0.6.1 - 2024-09-29
|
55
55
|
|
@@ -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,27 +42,37 @@ module ApacheAge
|
|
41
42
|
self
|
42
43
|
end
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
# we can have: "id(find) = ?", id & "find.name = ?", name
|
47
|
-
# ActiveRecord::Base.sanitize_sql([query_string, v])
|
48
|
-
def where(attributes)
|
49
|
-
return self if attributes.blank?
|
45
|
+
def where(*args)
|
46
|
+
return self if args.blank?
|
50
47
|
|
51
48
|
@where_clauses <<
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
49
|
+
# not able to sanitize the query string in this case: `["first_name = 'Barney'"]`
|
50
|
+
if args.length == 1 && args.first.is_a?(String)
|
51
|
+
raw_query_string = args.first
|
52
|
+
transform_cypher_sql(raw_query_string)
|
53
|
+
|
54
|
+
# Handling & sanitizing parameterized string queries
|
55
|
+
elsif args.length > 1 && args.first.is_a?(String)
|
56
|
+
raw_query_string = args.first
|
57
|
+
# Replace `id = ?` with `id(find) = ?` and `first_name = ?` with `find.first_name = ?`
|
58
|
+
query_string = transform_cypher_sql(raw_query_string)
|
59
|
+
values = args[1..-1]
|
60
|
+
# sanitize sql input values
|
61
|
+
ActiveRecord::Base.sanitize_sql_array([query_string, *values])
|
62
|
+
|
63
|
+
# Hashes are sanitized in the model class
|
64
|
+
# [{:first_name=>"Barney", :last_name=>"Rubble", :gender=>"male"}]
|
65
|
+
elsif args.first.is_a?(Hash)
|
66
|
+
attributes = args.first
|
59
67
|
edge_keys = [:start_id, :start_node, :end_id, :end_node]
|
60
68
|
if edge_keys.any? { |key| attributes.include?(key) }
|
61
|
-
model_class.send(:where_edge_clause, attributes)
|
69
|
+
model_class.send(:where_edge_clause, **attributes)
|
62
70
|
else
|
63
|
-
model_class.send(:where_node_clause, attributes)
|
71
|
+
model_class.send(:where_node_clause, **attributes)
|
64
72
|
end
|
73
|
+
|
74
|
+
else
|
75
|
+
raise ArgumentError, "Invalid arguments for `where` method"
|
65
76
|
end
|
66
77
|
|
67
78
|
self
|
@@ -72,8 +83,6 @@ module ApacheAge
|
|
72
83
|
return self if variables.blank?
|
73
84
|
|
74
85
|
@return_variables = variables
|
75
|
-
# @return_names = variables.empty? ? ['find'] : variables
|
76
|
-
# @return_clause = variables.empty? ? 'find' : "find.#{variables.join(', find.')}"
|
77
86
|
self
|
78
87
|
end
|
79
88
|
|
@@ -159,22 +168,47 @@ module ApacheAge
|
|
159
168
|
$$) AS (#{return_names.join(' agtype, ')} agtype);
|
160
169
|
SQL
|
161
170
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
171
|
+
|
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
|
178
212
|
end
|
179
213
|
end
|
180
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-
|
11
|
+
date: 2024-10-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|