neo4j 9.6.2 → 10.0.0.pre.alpha.1
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 +0 -13
- data/CONTRIBUTORS +4 -0
- data/Gemfile +2 -33
- data/lib/neo4j.rb +6 -2
- data/lib/neo4j/active_base.rb +19 -22
- data/lib/neo4j/active_node/has_n.rb +1 -1
- data/lib/neo4j/active_node/labels.rb +1 -11
- data/lib/neo4j/active_node/node_wrapper.rb +1 -1
- data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +1 -1
- data/lib/neo4j/active_rel/rel_wrapper.rb +2 -2
- data/lib/neo4j/ansi.rb +14 -0
- data/lib/neo4j/core.rb +14 -0
- data/lib/neo4j/core/connection_failed_error.rb +6 -0
- data/lib/neo4j/core/cypher_error.rb +37 -0
- data/lib/neo4j/core/driver.rb +83 -0
- data/lib/neo4j/core/has_uri.rb +63 -0
- data/lib/neo4j/core/instrumentable.rb +36 -0
- data/lib/neo4j/core/label.rb +158 -0
- data/lib/neo4j/core/logging.rb +44 -0
- data/lib/neo4j/core/node.rb +23 -0
- data/lib/neo4j/core/querable.rb +88 -0
- data/lib/neo4j/core/query.rb +487 -0
- data/lib/neo4j/core/query_builder.rb +32 -0
- data/lib/neo4j/core/query_clauses.rb +727 -0
- data/lib/neo4j/core/query_find_in_batches.rb +49 -0
- data/lib/neo4j/core/relationship.rb +13 -0
- data/lib/neo4j/core/responses.rb +50 -0
- data/lib/neo4j/core/result.rb +33 -0
- data/lib/neo4j/core/schema.rb +30 -0
- data/lib/neo4j/core/schema_errors.rb +12 -0
- data/lib/neo4j/core/wrappable.rb +30 -0
- data/lib/neo4j/migration.rb +2 -2
- data/lib/neo4j/migrations/base.rb +1 -1
- data/lib/neo4j/model_schema.rb +2 -2
- data/lib/neo4j/railtie.rb +8 -52
- data/lib/neo4j/schema/operation.rb +1 -1
- data/lib/neo4j/shared.rb +1 -1
- data/lib/neo4j/shared/property.rb +1 -1
- data/lib/neo4j/tasks/migration.rake +5 -4
- data/lib/neo4j/transaction.rb +137 -0
- data/lib/neo4j/version.rb +1 -1
- data/neo4j.gemspec +5 -5
- metadata +59 -26
- data/bin/neo4j-jars +0 -33
- data/lib/neo4j/active_base/session_registry.rb +0 -12
- data/lib/neo4j/session_manager.rb +0 -78
@@ -0,0 +1,32 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Core
|
3
|
+
class QueryBuilder
|
4
|
+
attr_reader :queries
|
5
|
+
|
6
|
+
Query = Struct.new(:cypher, :parameters, :pretty_cypher, :context)
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@queries = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def append(*args)
|
13
|
+
query = case args.map(&:class)
|
14
|
+
when [String], [String, Hash]
|
15
|
+
Query.new(args[0], args[1] || {})
|
16
|
+
when [::Neo4j::Core::Query]
|
17
|
+
args[0]
|
18
|
+
else
|
19
|
+
fail ArgumentError, "Could not determine query from arguments: #{args.inspect}"
|
20
|
+
end
|
21
|
+
|
22
|
+
@queries << query
|
23
|
+
end
|
24
|
+
|
25
|
+
def query
|
26
|
+
# `nil` sessions are just a workaround until
|
27
|
+
# we phase out `Query` objects containing sessions
|
28
|
+
Neo4j::Core::Query.new(session: nil)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,727 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Core
|
3
|
+
module QueryClauses
|
4
|
+
class ArgError < StandardError
|
5
|
+
attr_reader :arg_part
|
6
|
+
def initialize(arg_part = nil)
|
7
|
+
super
|
8
|
+
@arg_part = arg_part
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Clause
|
13
|
+
UNDERSCORE = '_'
|
14
|
+
COMMA_SPACE = ', '
|
15
|
+
AND = ' AND '
|
16
|
+
PRETTY_NEW_LINE = "\n "
|
17
|
+
|
18
|
+
attr_accessor :params, :arg
|
19
|
+
attr_reader :options, :param_vars_added
|
20
|
+
|
21
|
+
def initialize(arg, params, options = {})
|
22
|
+
@arg = arg
|
23
|
+
@options = options
|
24
|
+
@params = params
|
25
|
+
@param_vars_added = []
|
26
|
+
end
|
27
|
+
|
28
|
+
def value
|
29
|
+
return @value if @value
|
30
|
+
|
31
|
+
[String, Symbol, Integer, Hash, NilClass].each do |arg_class|
|
32
|
+
from_method = "from_#{arg_class.name.downcase}"
|
33
|
+
return @value = send(from_method, @arg) if @arg.is_a?(arg_class) && respond_to?(from_method)
|
34
|
+
end
|
35
|
+
|
36
|
+
fail ArgError
|
37
|
+
rescue ArgError => arg_error
|
38
|
+
message = "Invalid argument for #{self.class.keyword}. Full arguments: #{@arg.inspect}"
|
39
|
+
message += " | Invalid part: #{arg_error.arg_part.inspect}" if arg_error.arg_part
|
40
|
+
|
41
|
+
raise ArgumentError, message
|
42
|
+
end
|
43
|
+
|
44
|
+
def from_hash(value)
|
45
|
+
fail ArgError if !respond_to?(:from_key_and_value)
|
46
|
+
|
47
|
+
value.map do |k, v|
|
48
|
+
from_key_and_value k, v
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def from_string(value)
|
53
|
+
value
|
54
|
+
end
|
55
|
+
|
56
|
+
def node_from_key_and_value(key, value, options = {})
|
57
|
+
prefer = options[:prefer] || :var
|
58
|
+
var = var_from_key_and_value(key, value, prefer)
|
59
|
+
label = label_from_key_and_value(key, value, prefer)
|
60
|
+
|
61
|
+
attributes = attributes_from_key_and_value(key, value)
|
62
|
+
|
63
|
+
prefix_value = value
|
64
|
+
if value.is_a?(Hash)
|
65
|
+
prefix_value = (value.keys.join(UNDERSCORE) if value.values.any? { |v| v.is_a?(Hash) })
|
66
|
+
end
|
67
|
+
|
68
|
+
prefix_array = [key, prefix_value].tap(&:compact!).join(UNDERSCORE)
|
69
|
+
formatted_attributes = attributes_string(attributes, "#{prefix_array}#{UNDERSCORE}")
|
70
|
+
"(#{var}#{format_label(label)}#{formatted_attributes})"
|
71
|
+
end
|
72
|
+
|
73
|
+
def var_from_key_and_value(key, value, prefer = :var)
|
74
|
+
case value
|
75
|
+
when String, Symbol, Class, Module, NilClass, Array then key
|
76
|
+
when Hash
|
77
|
+
key if _use_key_for_var?(value, prefer)
|
78
|
+
else
|
79
|
+
fail ArgError, value
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def label_from_key_and_value(key, value, prefer = :var)
|
84
|
+
case value
|
85
|
+
when String, Symbol, Array, NilClass then value
|
86
|
+
when Class, Module then value.name
|
87
|
+
when Hash
|
88
|
+
if value.values.map(&:class) == [Hash]
|
89
|
+
value.first.first
|
90
|
+
elsif !_use_key_for_var?(value, prefer)
|
91
|
+
key
|
92
|
+
end
|
93
|
+
else
|
94
|
+
fail ArgError, value
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def _use_key_for_var?(value, prefer)
|
99
|
+
_nested_value_hash?(value) || prefer == :var
|
100
|
+
end
|
101
|
+
|
102
|
+
def _nested_value_hash?(value)
|
103
|
+
value.values.any? { |v| v.is_a?(Hash) }
|
104
|
+
end
|
105
|
+
|
106
|
+
def attributes_from_key_and_value(_key, value)
|
107
|
+
return nil unless value.is_a?(Hash)
|
108
|
+
|
109
|
+
value.values.map(&:class) == [Hash] ? value.first[1] : value
|
110
|
+
end
|
111
|
+
|
112
|
+
class << self
|
113
|
+
def keyword
|
114
|
+
self::KEYWORD
|
115
|
+
end
|
116
|
+
|
117
|
+
def keyword_downcase
|
118
|
+
keyword.downcase
|
119
|
+
end
|
120
|
+
|
121
|
+
def from_args(args, params, options = {})
|
122
|
+
args.flatten!
|
123
|
+
args.map { |arg| from_arg(arg, params, options) }.tap(&:compact!)
|
124
|
+
end
|
125
|
+
|
126
|
+
def from_arg(arg, params, options = {})
|
127
|
+
new(arg, params, options) if !arg.respond_to?(:empty?) || !arg.empty?
|
128
|
+
end
|
129
|
+
|
130
|
+
def to_cypher(clauses, pretty = false)
|
131
|
+
string = clause_string(clauses, pretty)
|
132
|
+
|
133
|
+
final_keyword = if pretty
|
134
|
+
"#{clause_color}#{keyword}#{ANSI::CLEAR}"
|
135
|
+
else
|
136
|
+
keyword
|
137
|
+
end
|
138
|
+
|
139
|
+
"#{final_keyword} #{string}" if !string.empty?
|
140
|
+
end
|
141
|
+
|
142
|
+
def clause_string(clauses, pretty)
|
143
|
+
join_string = pretty ? clause_join + PRETTY_NEW_LINE : clause_join
|
144
|
+
|
145
|
+
strings = clause_strings(clauses)
|
146
|
+
stripped_string = strings.join(join_string).strip
|
147
|
+
pretty && strings.size > 1 ? PRETTY_NEW_LINE + stripped_string : stripped_string
|
148
|
+
end
|
149
|
+
|
150
|
+
def clause_join
|
151
|
+
''
|
152
|
+
end
|
153
|
+
|
154
|
+
def clause_color
|
155
|
+
ANSI::CYAN
|
156
|
+
end
|
157
|
+
|
158
|
+
def from_key_and_single_value(key, value)
|
159
|
+
value.to_sym == :neo_id ? "ID(#{key})" : "#{key}.#{value}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.paramaterize_key!(key)
|
164
|
+
key.tr_s!('^a-zA-Z0-9', UNDERSCORE)
|
165
|
+
key.gsub!(/^_+|_+$/, '')
|
166
|
+
end
|
167
|
+
|
168
|
+
def add_param(key, value)
|
169
|
+
@param_vars_added << key
|
170
|
+
@params.add_param(key, value)
|
171
|
+
end
|
172
|
+
|
173
|
+
def add_params(keys_and_values)
|
174
|
+
@param_vars_added += keys_and_values.keys
|
175
|
+
@params.add_params(keys_and_values)
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
def key_value_string(key, value, previous_keys = [], is_set = false)
|
181
|
+
param = (previous_keys << key).join(UNDERSCORE)
|
182
|
+
self.class.paramaterize_key!(param)
|
183
|
+
|
184
|
+
if value.is_a?(Range)
|
185
|
+
range_key_value_string(key, value, previous_keys, param)
|
186
|
+
else
|
187
|
+
value = value.first if array_value?(value, is_set) && value.size == 1
|
188
|
+
|
189
|
+
param = add_param(param, value)
|
190
|
+
|
191
|
+
"#{key} #{array_value?(value, is_set) ? 'IN' : '='} {#{param}}"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def range_key_value_string(key, value, previous_keys, param)
|
196
|
+
begin_param, end_param = add_params("#{param}_range_min" => value.begin, "#{param}_range_max" => value.end)
|
197
|
+
"#{key} >= {#{begin_param}} AND #{previous_keys[-2]}.#{key} <#{'=' unless value.exclude_end?} {#{end_param}}"
|
198
|
+
end
|
199
|
+
|
200
|
+
def array_value?(value, is_set)
|
201
|
+
value.is_a?(Array) && !is_set
|
202
|
+
end
|
203
|
+
|
204
|
+
def format_label(label_arg)
|
205
|
+
return label_arg.map { |arg| format_label(arg) }.join if label_arg.is_a?(Array)
|
206
|
+
|
207
|
+
label_arg = label_arg.to_s.strip
|
208
|
+
if !label_arg.empty? && label_arg[0] != ':'
|
209
|
+
label_arg = "`#{label_arg}`" unless label_arg[' ']
|
210
|
+
label_arg = ":#{label_arg}"
|
211
|
+
end
|
212
|
+
label_arg
|
213
|
+
end
|
214
|
+
|
215
|
+
def attributes_string(attributes, prefix = '')
|
216
|
+
return '' if not attributes
|
217
|
+
|
218
|
+
attributes_string = attributes.map do |key, value|
|
219
|
+
if value.to_s =~ /^{.+}$/
|
220
|
+
"#{key}: #{value}"
|
221
|
+
else
|
222
|
+
param_key = "#{prefix}#{key}".gsub(/:+/, '_')
|
223
|
+
param_key = add_param(param_key, value)
|
224
|
+
"#{key}: {#{param_key}}"
|
225
|
+
end
|
226
|
+
end.join(Clause::COMMA_SPACE)
|
227
|
+
|
228
|
+
" {#{attributes_string}}"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
class StartClause < Clause
|
233
|
+
KEYWORD = 'START'
|
234
|
+
|
235
|
+
def from_symbol(value)
|
236
|
+
from_string(value.to_s)
|
237
|
+
end
|
238
|
+
|
239
|
+
def from_key_and_value(key, value)
|
240
|
+
case value
|
241
|
+
when String, Symbol
|
242
|
+
"#{key} = #{value}"
|
243
|
+
else
|
244
|
+
fail ArgError, value
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
class << self
|
249
|
+
def clause_strings(clauses)
|
250
|
+
clauses.map!(&:value)
|
251
|
+
end
|
252
|
+
|
253
|
+
def clause_join
|
254
|
+
Clause::COMMA_SPACE
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
class WhereClause < Clause
|
260
|
+
KEYWORD = 'WHERE'
|
261
|
+
|
262
|
+
PAREN_SURROUND_REGEX = /^\s*\(.+\)\s*$/
|
263
|
+
|
264
|
+
def from_key_and_value(key, value, previous_keys = [])
|
265
|
+
case value
|
266
|
+
when Hash then hash_key_value_string(key, value, previous_keys)
|
267
|
+
when NilClass then "#{key} IS NULL"
|
268
|
+
when Regexp then regexp_key_value_string(key, value, previous_keys)
|
269
|
+
else
|
270
|
+
key_value_string(key, value, previous_keys)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
class << self
|
275
|
+
def clause_strings(clauses)
|
276
|
+
clauses.flat_map do |clause|
|
277
|
+
Array(clause.value).map do |v|
|
278
|
+
(clause.options[:not] ? 'NOT' : '') + (v.to_s.match(PAREN_SURROUND_REGEX) ? v.to_s : "(#{v})")
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def clause_join
|
284
|
+
Clause::AND
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
private
|
289
|
+
|
290
|
+
def hash_key_value_string(key, value, previous_keys)
|
291
|
+
value.map do |k, v|
|
292
|
+
if k.to_sym == :neo_id
|
293
|
+
v = Array(v).map { |item| (item.respond_to?(:neo_id) ? item.neo_id : item).to_i }
|
294
|
+
key_value_string("ID(#{key})", v)
|
295
|
+
else
|
296
|
+
"#{key}.#{from_key_and_value(k, v, previous_keys + [key])}"
|
297
|
+
end
|
298
|
+
end.join(AND)
|
299
|
+
end
|
300
|
+
|
301
|
+
def regexp_key_value_string(key, value, previous_keys)
|
302
|
+
pattern = (value.casefold? ? '(?i)' : '') + value.source
|
303
|
+
|
304
|
+
param = [previous_keys + [key]].join(UNDERSCORE)
|
305
|
+
self.class.paramaterize_key!(param)
|
306
|
+
|
307
|
+
param = add_param(param, pattern)
|
308
|
+
|
309
|
+
"#{key} =~ {#{param}}"
|
310
|
+
end
|
311
|
+
|
312
|
+
class << self
|
313
|
+
ARG_HAS_QUESTION_MARK_REGEX = /(^|\(|\s)\?(\s|\)|$)/
|
314
|
+
|
315
|
+
def from_args(args, params, options = {})
|
316
|
+
query_string, params_arg = args
|
317
|
+
|
318
|
+
if query_string.is_a?(String) && (query_string.match(ARG_HAS_QUESTION_MARK_REGEX) || params_arg.is_a?(Hash))
|
319
|
+
if params_arg.is_a?(Hash)
|
320
|
+
params.add_params(params_arg)
|
321
|
+
else
|
322
|
+
param_var = params.add_params(question_mark_param: params_arg)[0]
|
323
|
+
query_string = query_string.gsub(ARG_HAS_QUESTION_MARK_REGEX, "\\1{#{param_var}}\\2")
|
324
|
+
end
|
325
|
+
|
326
|
+
[from_arg(query_string, params, options)]
|
327
|
+
else
|
328
|
+
super
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
class CallClause < Clause
|
335
|
+
KEYWORD = 'CALL'
|
336
|
+
|
337
|
+
def from_string(value)
|
338
|
+
value
|
339
|
+
end
|
340
|
+
|
341
|
+
class << self
|
342
|
+
def clause_strings(clauses)
|
343
|
+
clauses.map!(&:value)
|
344
|
+
end
|
345
|
+
|
346
|
+
def clause_join
|
347
|
+
" #{KEYWORD} "
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
class MatchClause < Clause
|
353
|
+
KEYWORD = 'MATCH'
|
354
|
+
|
355
|
+
def from_symbol(value)
|
356
|
+
'(' + from_string(value.to_s) + ')'
|
357
|
+
end
|
358
|
+
|
359
|
+
def from_key_and_value(key, value)
|
360
|
+
node_from_key_and_value(key, value)
|
361
|
+
end
|
362
|
+
|
363
|
+
class << self
|
364
|
+
def clause_strings(clauses)
|
365
|
+
clauses.map!(&:value)
|
366
|
+
end
|
367
|
+
|
368
|
+
def clause_join
|
369
|
+
Clause::COMMA_SPACE
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
class OptionalMatchClause < MatchClause
|
375
|
+
KEYWORD = 'OPTIONAL MATCH'
|
376
|
+
end
|
377
|
+
|
378
|
+
class WithClause < Clause
|
379
|
+
KEYWORD = 'WITH'
|
380
|
+
|
381
|
+
def from_symbol(value)
|
382
|
+
from_string(value.to_s)
|
383
|
+
end
|
384
|
+
|
385
|
+
def from_key_and_value(key, value)
|
386
|
+
"#{value} AS #{key}"
|
387
|
+
end
|
388
|
+
|
389
|
+
class << self
|
390
|
+
def clause_strings(clauses)
|
391
|
+
clauses.map!(&:value)
|
392
|
+
end
|
393
|
+
|
394
|
+
def clause_join
|
395
|
+
Clause::COMMA_SPACE
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
class WithDistinctClause < WithClause
|
401
|
+
KEYWORD = 'WITH DISTINCT'
|
402
|
+
end
|
403
|
+
|
404
|
+
class UsingClause < Clause
|
405
|
+
KEYWORD = 'USING'
|
406
|
+
|
407
|
+
class << self
|
408
|
+
def clause_strings(clauses)
|
409
|
+
clauses.map!(&:value)
|
410
|
+
end
|
411
|
+
|
412
|
+
def clause_join
|
413
|
+
" #{keyword} "
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
class CreateClause < Clause
|
419
|
+
KEYWORD = 'CREATE'
|
420
|
+
|
421
|
+
def from_string(value)
|
422
|
+
value
|
423
|
+
end
|
424
|
+
|
425
|
+
def from_symbol(value)
|
426
|
+
"(:#{value})"
|
427
|
+
end
|
428
|
+
|
429
|
+
def from_hash(hash)
|
430
|
+
if hash.values.any? { |value| value.is_a?(Hash) }
|
431
|
+
hash.map do |key, value|
|
432
|
+
from_key_and_value(key, value)
|
433
|
+
end
|
434
|
+
else
|
435
|
+
"(#{attributes_string(hash)})"
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
def from_key_and_value(key, value)
|
440
|
+
node_from_key_and_value(key, value, prefer: :label)
|
441
|
+
end
|
442
|
+
|
443
|
+
class << self
|
444
|
+
def clause_strings(clauses)
|
445
|
+
clauses.map!(&:value)
|
446
|
+
end
|
447
|
+
|
448
|
+
def clause_join
|
449
|
+
', '
|
450
|
+
end
|
451
|
+
|
452
|
+
def clause_color
|
453
|
+
ANSI::GREEN
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
class CreateUniqueClause < CreateClause
|
459
|
+
KEYWORD = 'CREATE UNIQUE'
|
460
|
+
end
|
461
|
+
|
462
|
+
class MergeClause < CreateClause
|
463
|
+
KEYWORD = 'MERGE'
|
464
|
+
|
465
|
+
class << self
|
466
|
+
def clause_color
|
467
|
+
ANSI::MAGENTA
|
468
|
+
end
|
469
|
+
|
470
|
+
def clause_join
|
471
|
+
' MERGE '
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
class DeleteClause < Clause
|
477
|
+
KEYWORD = 'DELETE'
|
478
|
+
|
479
|
+
def from_symbol(value)
|
480
|
+
from_string(value.to_s)
|
481
|
+
end
|
482
|
+
|
483
|
+
class << self
|
484
|
+
def clause_strings(clauses)
|
485
|
+
clauses.map!(&:value)
|
486
|
+
end
|
487
|
+
|
488
|
+
def clause_join
|
489
|
+
Clause::COMMA_SPACE
|
490
|
+
end
|
491
|
+
|
492
|
+
def clause_color
|
493
|
+
ANSI::RED
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
class DetachDeleteClause < DeleteClause
|
499
|
+
KEYWORD = 'DETACH DELETE'
|
500
|
+
end
|
501
|
+
|
502
|
+
class OrderClause < Clause
|
503
|
+
KEYWORD = 'ORDER BY'
|
504
|
+
|
505
|
+
def from_symbol(value)
|
506
|
+
from_string(value.to_s)
|
507
|
+
end
|
508
|
+
|
509
|
+
def from_key_and_value(key, value)
|
510
|
+
case value
|
511
|
+
when String, Symbol
|
512
|
+
self.class.from_key_and_single_value(key, value)
|
513
|
+
when Array
|
514
|
+
value.map do |v|
|
515
|
+
v.is_a?(Hash) ? from_key_and_value(key, v) : self.class.from_key_and_single_value(key, v)
|
516
|
+
end
|
517
|
+
when Hash
|
518
|
+
value.map { |k, v| "#{self.class.from_key_and_single_value(key, k)} #{v.upcase}" }
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
class << self
|
523
|
+
def clause_strings(clauses)
|
524
|
+
clauses.map!(&:value)
|
525
|
+
end
|
526
|
+
|
527
|
+
def clause_join
|
528
|
+
Clause::COMMA_SPACE
|
529
|
+
end
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
class LimitClause < Clause
|
534
|
+
KEYWORD = 'LIMIT'
|
535
|
+
|
536
|
+
def from_string(value)
|
537
|
+
param_var = "#{self.class.keyword_downcase}_#{value}"
|
538
|
+
param_var = add_param(param_var, value.to_i)
|
539
|
+
"{#{param_var}}"
|
540
|
+
end
|
541
|
+
|
542
|
+
def from_integer(value)
|
543
|
+
from_string(value)
|
544
|
+
end
|
545
|
+
|
546
|
+
def from_nilclass(_value)
|
547
|
+
''
|
548
|
+
end
|
549
|
+
|
550
|
+
class << self
|
551
|
+
def clause_strings(clauses)
|
552
|
+
result_clause = clauses.last
|
553
|
+
|
554
|
+
clauses[0..-2].map(&:param_vars_added).flatten.grep(/^limit_\d+$/).each do |var|
|
555
|
+
result_clause.params.remove_param(var)
|
556
|
+
end
|
557
|
+
|
558
|
+
[result_clause.value]
|
559
|
+
end
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
class SkipClause < Clause
|
564
|
+
KEYWORD = 'SKIP'
|
565
|
+
|
566
|
+
def from_string(value)
|
567
|
+
clause_id = "#{self.class.keyword_downcase}_#{value}"
|
568
|
+
clause_id = add_param(clause_id, value.to_i)
|
569
|
+
"{#{clause_id}}"
|
570
|
+
end
|
571
|
+
|
572
|
+
def from_integer(value)
|
573
|
+
clause_id = "#{self.class.keyword_downcase}_#{value}"
|
574
|
+
clause_id = add_param(clause_id, value)
|
575
|
+
"{#{clause_id}}"
|
576
|
+
end
|
577
|
+
|
578
|
+
class << self
|
579
|
+
def clause_strings(clauses)
|
580
|
+
result_clause = clauses.last
|
581
|
+
|
582
|
+
clauses[0..-2].map(&:param_vars_added).flatten.grep(/^skip_\d+$/).each do |var|
|
583
|
+
result_clause.params.remove_param(var)
|
584
|
+
end
|
585
|
+
|
586
|
+
[result_clause.value]
|
587
|
+
end
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
class SetClause < Clause
|
592
|
+
KEYWORD = 'SET'
|
593
|
+
|
594
|
+
def from_key_and_value(key, value)
|
595
|
+
case value
|
596
|
+
when String, Symbol then "#{key}:`#{value}`"
|
597
|
+
when Hash
|
598
|
+
if @options[:set_props]
|
599
|
+
param = add_param("#{key}_set_props", value)
|
600
|
+
"#{key} = {#{param}}"
|
601
|
+
else
|
602
|
+
value.map { |k, v| key_value_string("#{key}.`#{k}`", v, ['setter'], true) }
|
603
|
+
end
|
604
|
+
when Array then value.map { |v| from_key_and_value(key, v) }
|
605
|
+
when NilClass then []
|
606
|
+
else
|
607
|
+
fail ArgError, value
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
class << self
|
612
|
+
def clause_strings(clauses)
|
613
|
+
clauses.map!(&:value)
|
614
|
+
end
|
615
|
+
|
616
|
+
def clause_join
|
617
|
+
Clause::COMMA_SPACE
|
618
|
+
end
|
619
|
+
|
620
|
+
def clause_color
|
621
|
+
ANSI::YELLOW
|
622
|
+
end
|
623
|
+
end
|
624
|
+
end
|
625
|
+
|
626
|
+
class OnCreateSetClause < SetClause
|
627
|
+
KEYWORD = 'ON CREATE SET'
|
628
|
+
|
629
|
+
def initialize(*args)
|
630
|
+
super
|
631
|
+
@options[:set_props] = false
|
632
|
+
end
|
633
|
+
end
|
634
|
+
|
635
|
+
class OnMatchSetClause < OnCreateSetClause
|
636
|
+
KEYWORD = 'ON MATCH SET'
|
637
|
+
end
|
638
|
+
|
639
|
+
class RemoveClause < Clause
|
640
|
+
KEYWORD = 'REMOVE'
|
641
|
+
|
642
|
+
def from_key_and_value(key, value)
|
643
|
+
case value
|
644
|
+
when /^:/
|
645
|
+
"#{key}:`#{value[1..-1]}`"
|
646
|
+
when String
|
647
|
+
"#{key}.#{value}"
|
648
|
+
when Symbol
|
649
|
+
"#{key}:`#{value}`"
|
650
|
+
when Array
|
651
|
+
value.map do |v|
|
652
|
+
from_key_and_value(key, v)
|
653
|
+
end
|
654
|
+
else
|
655
|
+
fail ArgError, value
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
659
|
+
class << self
|
660
|
+
def clause_strings(clauses)
|
661
|
+
clauses.map!(&:value)
|
662
|
+
end
|
663
|
+
|
664
|
+
def clause_join
|
665
|
+
Clause::COMMA_SPACE
|
666
|
+
end
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
class UnwindClause < Clause
|
671
|
+
KEYWORD = 'UNWIND'
|
672
|
+
|
673
|
+
def from_key_and_value(key, value)
|
674
|
+
case value
|
675
|
+
when String, Symbol
|
676
|
+
"#{value} AS #{key}"
|
677
|
+
when Array
|
678
|
+
"#{value.inspect} AS #{key}"
|
679
|
+
else
|
680
|
+
fail ArgError, value
|
681
|
+
end
|
682
|
+
end
|
683
|
+
|
684
|
+
class << self
|
685
|
+
def clause_strings(clauses)
|
686
|
+
clauses.map!(&:value)
|
687
|
+
end
|
688
|
+
|
689
|
+
def clause_join
|
690
|
+
' UNWIND '
|
691
|
+
end
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
class ReturnClause < Clause
|
696
|
+
KEYWORD = 'RETURN'
|
697
|
+
|
698
|
+
def from_symbol(value)
|
699
|
+
from_string(value.to_s)
|
700
|
+
end
|
701
|
+
|
702
|
+
def from_key_and_value(key, value)
|
703
|
+
case value
|
704
|
+
when Array
|
705
|
+
value.map do |v|
|
706
|
+
from_key_and_value(key, v)
|
707
|
+
end.join(Clause::COMMA_SPACE)
|
708
|
+
when String, Symbol
|
709
|
+
self.class.from_key_and_single_value(key, value)
|
710
|
+
else
|
711
|
+
fail ArgError, value
|
712
|
+
end
|
713
|
+
end
|
714
|
+
|
715
|
+
class << self
|
716
|
+
def clause_strings(clauses)
|
717
|
+
clauses.map!(&:value)
|
718
|
+
end
|
719
|
+
|
720
|
+
def clause_join
|
721
|
+
Clause::COMMA_SPACE
|
722
|
+
end
|
723
|
+
end
|
724
|
+
end
|
725
|
+
end
|
726
|
+
end
|
727
|
+
end
|