neo4j-core 4.0.7 → 5.0.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ext/kernel.rb +9 -0
- data/lib/neo4j/label.rb +2 -1
- data/lib/neo4j/node.rb +8 -11
- data/lib/neo4j/property_container.rb +2 -7
- data/lib/neo4j/property_validator.rb +1 -1
- data/lib/neo4j/session.rb +24 -11
- data/lib/neo4j/tasks/config_server.rb +4 -1
- data/lib/neo4j/tasks/neo4j_server.rake +86 -109
- data/lib/neo4j/transaction.rb +17 -16
- data/lib/neo4j-core/cypher_translator.rb +1 -1
- data/lib/neo4j-core/query.rb +103 -47
- data/lib/neo4j-core/query_clauses.rb +177 -109
- data/lib/neo4j-core/query_find_in_batches.rb +19 -11
- data/lib/neo4j-core/version.rb +1 -1
- data/lib/neo4j-core.rb +3 -0
- data/lib/neo4j-embedded/cypher_response.rb +20 -5
- data/lib/neo4j-embedded/embedded_node.rb +26 -28
- data/lib/neo4j-embedded/embedded_session.rb +7 -6
- data/lib/neo4j-embedded/embedded_transaction.rb +2 -2
- data/lib/neo4j-embedded/label.rb +65 -0
- data/lib/neo4j-embedded/property.rb +5 -5
- data/lib/neo4j-embedded/to_java.rb +7 -13
- data/lib/neo4j-embedded.rb +1 -0
- data/lib/neo4j-server/cypher_node.rb +57 -67
- data/lib/neo4j-server/cypher_node_uncommited.rb +1 -1
- data/lib/neo4j-server/cypher_relationship.rb +10 -6
- data/lib/neo4j-server/cypher_response.rb +87 -51
- data/lib/neo4j-server/cypher_session.rb +80 -93
- data/lib/neo4j-server/cypher_transaction.rb +42 -33
- data/lib/neo4j-server/label.rb +40 -0
- data/lib/neo4j-server/resource.rb +11 -12
- data/lib/neo4j-server.rb +2 -0
- data/neo4j-core.gemspec +4 -1
- metadata +50 -6
- data/lib/neo4j-core/graph_json.rb +0 -35
@@ -12,8 +12,11 @@ module Neo4j
|
|
12
12
|
|
13
13
|
class Clause
|
14
14
|
include CypherTranslator
|
15
|
+
UNDERSCORE = '_'
|
16
|
+
COMMA_SPACE = ', '
|
17
|
+
AND = ' AND '
|
15
18
|
|
16
|
-
|
19
|
+
attr_accessor :params, :arg
|
17
20
|
|
18
21
|
def initialize(arg, options = {})
|
19
22
|
@arg = arg
|
@@ -54,19 +57,23 @@ module Neo4j
|
|
54
57
|
label = label_from_key_and_value(key, value, options[:prefer] || :var)
|
55
58
|
attributes = attributes_from_key_and_value(key, value)
|
56
59
|
|
57
|
-
|
60
|
+
prefix_value = value
|
61
|
+
if value.is_a?(Hash)
|
62
|
+
prefix_value = if value.values.any? { |v| v.is_a?(Hash) }
|
63
|
+
value.keys.join(UNDERSCORE)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
prefix_array = [key, prefix_value].tap(&:compact!).join(UNDERSCORE)
|
68
|
+
formatted_attributes = attributes_string(attributes, "#{prefix_array}#{UNDERSCORE}")
|
69
|
+
"(#{var}#{format_label(label)}#{formatted_attributes})"
|
58
70
|
end
|
59
71
|
|
60
72
|
def var_from_key_and_value(key, value, prefer = :var)
|
61
73
|
case value
|
62
|
-
when String, Symbol, Class, Module
|
63
|
-
key
|
74
|
+
when String, Symbol, Class, Module, NilClass, Array then key
|
64
75
|
when Hash
|
65
|
-
if value
|
66
|
-
key if prefer == :var
|
67
|
-
else
|
68
|
-
key
|
69
|
-
end
|
76
|
+
key if _use_key_for_var?(value, prefer)
|
70
77
|
else
|
71
78
|
fail ArgError, value
|
72
79
|
end
|
@@ -74,22 +81,29 @@ module Neo4j
|
|
74
81
|
|
75
82
|
def label_from_key_and_value(key, value, prefer = :var)
|
76
83
|
case value
|
77
|
-
when String, Symbol
|
78
|
-
|
79
|
-
when Class, Module
|
80
|
-
defined?(value::CYPHER_LABEL) ? value::CYPHER_LABEL : value.name
|
84
|
+
when String, Symbol, Array, NilClass then value
|
85
|
+
when Class, Module then value.name
|
81
86
|
when Hash
|
82
87
|
if value.values.map(&:class) == [Hash]
|
83
88
|
value.first.first
|
84
89
|
else
|
85
|
-
key if
|
90
|
+
key if !_use_key_for_var?(value, prefer)
|
86
91
|
end
|
87
92
|
else
|
88
93
|
fail ArgError, value
|
89
94
|
end
|
90
95
|
end
|
91
96
|
|
92
|
-
def
|
97
|
+
def _use_key_for_var?(value, prefer)
|
98
|
+
_nested_value_hash?(value) || prefer == :var
|
99
|
+
end
|
100
|
+
|
101
|
+
def _nested_value_hash?(value)
|
102
|
+
value.values.any? { |v| v.is_a?(Hash) }
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def attributes_from_key_and_value(_key, value)
|
93
107
|
return nil unless value.is_a?(Hash)
|
94
108
|
|
95
109
|
if value.values.map(&:class) == [Hash]
|
@@ -100,28 +114,41 @@ module Neo4j
|
|
100
114
|
end
|
101
115
|
|
102
116
|
class << self
|
103
|
-
|
117
|
+
def keyword
|
118
|
+
self::KEYWORD
|
119
|
+
end
|
120
|
+
|
121
|
+
def keyword_downcase
|
122
|
+
keyword.downcase
|
123
|
+
end
|
104
124
|
|
105
125
|
def from_args(args, options = {})
|
106
|
-
args.flatten
|
107
|
-
|
108
|
-
|
126
|
+
args.flatten!
|
127
|
+
args.map { |arg| from_arg(arg, options) }.tap(&:compact!)
|
128
|
+
end
|
129
|
+
|
130
|
+
def from_arg(arg, options = {})
|
131
|
+
new(arg, options) if !arg.respond_to?(:empty?) || !arg.empty?
|
109
132
|
end
|
110
133
|
|
111
134
|
def to_cypher(clauses)
|
135
|
+
@question_mark_param_index = 1
|
136
|
+
|
112
137
|
string = clause_string(clauses)
|
113
138
|
string.strip!
|
114
139
|
|
115
|
-
"#{
|
140
|
+
"#{keyword} #{string}" if string.size > 0
|
116
141
|
end
|
117
142
|
end
|
118
143
|
|
119
144
|
private
|
120
145
|
|
121
146
|
def key_value_string(key, value, previous_keys = [], force_equals = false)
|
122
|
-
param = (previous_keys << key).join(
|
123
|
-
param.
|
147
|
+
param = (previous_keys << key).join(UNDERSCORE)
|
148
|
+
param.tr_s!('^a-zA-Z0-9', UNDERSCORE)
|
124
149
|
param.gsub!(/^_+|_+$/, '')
|
150
|
+
|
151
|
+
value = value.first if value.is_a?(Array) && value.size == 1
|
125
152
|
@params[param.to_sym] = value
|
126
153
|
|
127
154
|
if !value.is_a?(Array) || force_equals
|
@@ -131,34 +158,39 @@ module Neo4j
|
|
131
158
|
end
|
132
159
|
end
|
133
160
|
|
134
|
-
def format_label(
|
135
|
-
|
136
|
-
|
137
|
-
if !label_string.empty? && label_string[0] != ':'
|
138
|
-
label_string = "`#{label_string}`" unless label_string.match(' ')
|
139
|
-
label_string = ":#{label_string}"
|
161
|
+
def format_label(label_arg)
|
162
|
+
if label_arg.is_a?(Array)
|
163
|
+
return label_arg.map { |arg| format_label(arg) }.join
|
140
164
|
end
|
141
|
-
|
165
|
+
|
166
|
+
label_arg = label_arg.to_s
|
167
|
+
label_arg.strip!
|
168
|
+
if !label_arg.empty? && label_arg[0] != ':'
|
169
|
+
label_arg = "`#{label_arg}`" unless label_arg.match(' ')
|
170
|
+
label_arg = ":#{label_arg}"
|
171
|
+
end
|
172
|
+
label_arg
|
142
173
|
end
|
143
174
|
|
144
|
-
def attributes_string(attributes)
|
175
|
+
def attributes_string(attributes, prefix = '')
|
145
176
|
return '' if not attributes
|
146
177
|
|
147
178
|
attributes_string = attributes.map do |key, value|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
179
|
+
if value.to_s.match(/^{.+}$/)
|
180
|
+
"#{key}: #{value}"
|
181
|
+
else
|
182
|
+
param_key = "#{prefix}#{key}".gsub('::', '_')
|
183
|
+
@params[param_key.to_sym] = value
|
184
|
+
"#{key}: {#{param_key}}"
|
185
|
+
end
|
186
|
+
end.join(Clause::COMMA_SPACE)
|
155
187
|
|
156
188
|
" {#{attributes_string}}"
|
157
189
|
end
|
158
190
|
end
|
159
191
|
|
160
192
|
class StartClause < Clause
|
161
|
-
|
193
|
+
KEYWORD = 'START'
|
162
194
|
|
163
195
|
def from_symbol(value)
|
164
196
|
from_string(value.to_s)
|
@@ -175,33 +207,20 @@ module Neo4j
|
|
175
207
|
|
176
208
|
class << self
|
177
209
|
def clause_string(clauses)
|
178
|
-
clauses.map!(&:value).join(
|
210
|
+
clauses.map!(&:value).join(Clause::COMMA_SPACE)
|
179
211
|
end
|
180
212
|
end
|
181
213
|
end
|
182
214
|
|
183
215
|
class WhereClause < Clause
|
184
|
-
|
216
|
+
KEYWORD = 'WHERE'
|
185
217
|
|
186
218
|
def from_key_and_value(key, value, previous_keys = [])
|
187
219
|
case value
|
188
|
-
when Hash
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
@params[clause_id] = v.to_i
|
193
|
-
"ID(#{key}) = {#{clause_id}}"
|
194
|
-
else
|
195
|
-
"#{key}.#{from_key_and_value(k, v, previous_keys + [key])}"
|
196
|
-
end
|
197
|
-
end.join(' AND ')
|
198
|
-
when NilClass
|
199
|
-
"#{key} IS NULL"
|
200
|
-
when Regexp
|
201
|
-
pattern = (value.casefold? ? '(?i)' : '') + value.source
|
202
|
-
"#{key} =~ #{escape_value(pattern.gsub(/\\/, '\\\\\\'))}"
|
203
|
-
when Array
|
204
|
-
key_value_string(key, value, previous_keys)
|
220
|
+
when Hash then hash_key_value_string(key, value, previous_keys)
|
221
|
+
when NilClass then "#{key} IS NULL"
|
222
|
+
when Regexp then regexp_key_value_string(key, value)
|
223
|
+
when Array then key_value_string(key, value, previous_keys)
|
205
224
|
else
|
206
225
|
key_value_string(key, value, previous_keys)
|
207
226
|
end
|
@@ -209,14 +228,62 @@ module Neo4j
|
|
209
228
|
|
210
229
|
class << self
|
211
230
|
def clause_string(clauses)
|
212
|
-
clauses.map(&:value).flatten.map {|value| "(#{value})" }.join(
|
231
|
+
clauses.map!(&:value).tap(&:flatten!).map! { |value| "(#{value})" }.join(Clause::AND)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
private
|
236
|
+
|
237
|
+
def hash_key_value_string(key, value, previous_keys)
|
238
|
+
value.map do |k, v|
|
239
|
+
if k.to_sym == :neo_id
|
240
|
+
v = Array(v).map { |item| (item.respond_to?(:neo_id) ? item.neo_id : item).to_i }
|
241
|
+
key_value_string("ID(#{key})", v)
|
242
|
+
else
|
243
|
+
"#{key}.#{from_key_and_value(k, v, previous_keys + [key])}"
|
244
|
+
end
|
245
|
+
end.join(AND)
|
246
|
+
end
|
247
|
+
|
248
|
+
def regexp_key_value_string(key, value)
|
249
|
+
pattern = (value.casefold? ? '(?i)' : '') + value.source
|
250
|
+
"#{key} =~ #{escape_value(pattern.gsub(/\\/, '\\\\\\'))}"
|
251
|
+
end
|
252
|
+
|
253
|
+
class << self
|
254
|
+
ARG_HAS_QUESTION_MARK_REGEX = /(^|\s)\?(\s|$)/
|
255
|
+
|
256
|
+
def from_args(args, options = {})
|
257
|
+
query_string, params = args
|
258
|
+
|
259
|
+
if query_string.is_a?(String) && (query_string.match(ARG_HAS_QUESTION_MARK_REGEX) || params.is_a?(Hash))
|
260
|
+
if !params.is_a?(Hash)
|
261
|
+
question_mark_params_param = self.question_mark_params_param
|
262
|
+
query_string.gsub!(ARG_HAS_QUESTION_MARK_REGEX, "\\1{#{question_mark_params_param}}\\2")
|
263
|
+
params = {question_mark_params_param.to_sym => params}
|
264
|
+
end
|
265
|
+
|
266
|
+
clause = from_arg(query_string, options).tap do |clause|
|
267
|
+
clause.params.merge!(params)
|
268
|
+
end
|
269
|
+
|
270
|
+
[clause]
|
271
|
+
else
|
272
|
+
super
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def question_mark_params_param
|
277
|
+
result = "question_mark_param#{@question_mark_param_index}"
|
278
|
+
@question_mark_param_index += 1
|
279
|
+
result
|
213
280
|
end
|
214
281
|
end
|
215
282
|
end
|
216
283
|
|
217
284
|
|
218
285
|
class MatchClause < Clause
|
219
|
-
|
286
|
+
KEYWORD = 'MATCH'
|
220
287
|
|
221
288
|
def from_symbol(value)
|
222
289
|
from_string(value.to_s)
|
@@ -228,17 +295,17 @@ module Neo4j
|
|
228
295
|
|
229
296
|
class << self
|
230
297
|
def clause_string(clauses)
|
231
|
-
clauses.map!(&:value).join(
|
298
|
+
clauses.map!(&:value).join(Clause::COMMA_SPACE)
|
232
299
|
end
|
233
300
|
end
|
234
301
|
end
|
235
302
|
|
236
303
|
class OptionalMatchClause < MatchClause
|
237
|
-
|
304
|
+
KEYWORD = 'OPTIONAL MATCH'
|
238
305
|
end
|
239
306
|
|
240
307
|
class WithClause < Clause
|
241
|
-
|
308
|
+
KEYWORD = 'WITH'
|
242
309
|
|
243
310
|
def from_symbol(value)
|
244
311
|
from_string(value.to_s)
|
@@ -250,23 +317,23 @@ module Neo4j
|
|
250
317
|
|
251
318
|
class << self
|
252
319
|
def clause_string(clauses)
|
253
|
-
clauses.map!(&:value).join(
|
320
|
+
clauses.map!(&:value).join(Clause::COMMA_SPACE)
|
254
321
|
end
|
255
322
|
end
|
256
323
|
end
|
257
324
|
|
258
325
|
class UsingClause < Clause
|
259
|
-
|
326
|
+
KEYWORD = 'USING'
|
260
327
|
|
261
328
|
class << self
|
262
329
|
def clause_string(clauses)
|
263
|
-
clauses.map!(&:value).join(" #{
|
330
|
+
clauses.map!(&:value).join(" #{keyword} ")
|
264
331
|
end
|
265
332
|
end
|
266
333
|
end
|
267
334
|
|
268
335
|
class CreateClause < Clause
|
269
|
-
|
336
|
+
KEYWORD = 'CREATE'
|
270
337
|
|
271
338
|
def from_string(value)
|
272
339
|
value
|
@@ -298,15 +365,15 @@ module Neo4j
|
|
298
365
|
end
|
299
366
|
|
300
367
|
class CreateUniqueClause < CreateClause
|
301
|
-
|
368
|
+
KEYWORD = 'CREATE UNIQUE'
|
302
369
|
end
|
303
370
|
|
304
371
|
class MergeClause < CreateClause
|
305
|
-
|
372
|
+
KEYWORD = 'MERGE'
|
306
373
|
end
|
307
374
|
|
308
375
|
class DeleteClause < Clause
|
309
|
-
|
376
|
+
KEYWORD = 'DELETE'
|
310
377
|
|
311
378
|
def from_symbol(value)
|
312
379
|
from_string(value.to_s)
|
@@ -314,13 +381,13 @@ module Neo4j
|
|
314
381
|
|
315
382
|
class << self
|
316
383
|
def clause_string(clauses)
|
317
|
-
clauses.map!(&:value).join(
|
384
|
+
clauses.map!(&:value).join(Clause::COMMA_SPACE)
|
318
385
|
end
|
319
386
|
end
|
320
387
|
end
|
321
388
|
|
322
389
|
class OrderClause < Clause
|
323
|
-
|
390
|
+
KEYWORD = 'ORDER BY'
|
324
391
|
|
325
392
|
def from_symbol(value)
|
326
393
|
from_string(value.to_s)
|
@@ -332,38 +399,32 @@ module Neo4j
|
|
332
399
|
"#{key}.#{value}"
|
333
400
|
when Array
|
334
401
|
value.map do |v|
|
335
|
-
|
336
|
-
from_key_and_value(key, v)
|
337
|
-
else
|
338
|
-
"#{key}.#{v}"
|
339
|
-
end
|
402
|
+
v.is_a?(Hash) ? from_key_and_value(key, v) : "#{key}.#{v}"
|
340
403
|
end
|
341
404
|
when Hash
|
342
|
-
value.map
|
343
|
-
"#{key}.#{k} #{v.upcase}"
|
344
|
-
end
|
405
|
+
value.map { |k, v| "#{key}.#{k} #{v.upcase}" }
|
345
406
|
end
|
346
407
|
end
|
347
408
|
|
348
409
|
class << self
|
349
410
|
def clause_string(clauses)
|
350
|
-
clauses.map!(&:value).join(
|
411
|
+
clauses.map!(&:value).join(Clause::COMMA_SPACE)
|
351
412
|
end
|
352
413
|
end
|
353
414
|
end
|
354
415
|
|
355
416
|
class LimitClause < Clause
|
356
|
-
|
417
|
+
KEYWORD = 'LIMIT'
|
357
418
|
|
358
419
|
def from_string(value)
|
359
|
-
clause_id = "#{self.class.
|
360
|
-
@params[clause_id] = value.to_i
|
420
|
+
clause_id = "#{self.class.keyword_downcase}_#{value}"
|
421
|
+
@params[clause_id.to_sym] = value.to_i
|
361
422
|
"{#{clause_id}}"
|
362
423
|
end
|
363
424
|
|
364
425
|
def from_integer(value)
|
365
|
-
clause_id = "#{self.class.
|
366
|
-
@params[clause_id] = value
|
426
|
+
clause_id = "#{self.class.keyword_downcase}_#{value}"
|
427
|
+
@params[clause_id.to_sym] = value
|
367
428
|
"{#{clause_id}}"
|
368
429
|
end
|
369
430
|
|
@@ -375,17 +436,17 @@ module Neo4j
|
|
375
436
|
end
|
376
437
|
|
377
438
|
class SkipClause < Clause
|
378
|
-
|
439
|
+
KEYWORD = 'SKIP'
|
379
440
|
|
380
441
|
def from_string(value)
|
381
|
-
clause_id = "#{self.class.
|
382
|
-
@params[clause_id] = value.to_i
|
442
|
+
clause_id = "#{self.class.keyword_downcase}_#{value}"
|
443
|
+
@params[clause_id.to_sym] = value.to_i
|
383
444
|
"{#{clause_id}}"
|
384
445
|
end
|
385
446
|
|
386
447
|
def from_integer(value)
|
387
|
-
clause_id = "#{self.class.
|
388
|
-
@params[clause_id] = value
|
448
|
+
clause_id = "#{self.class.keyword_downcase}_#{value}"
|
449
|
+
@params[clause_id.to_sym] = value
|
389
450
|
"{#{clause_id}}"
|
390
451
|
end
|
391
452
|
|
@@ -397,21 +458,20 @@ module Neo4j
|
|
397
458
|
end
|
398
459
|
|
399
460
|
class SetClause < Clause
|
400
|
-
|
461
|
+
KEYWORD = 'SET'
|
401
462
|
|
402
463
|
def from_key_and_value(key, value)
|
403
464
|
case value
|
404
|
-
when String, Symbol
|
405
|
-
"#{key} = #{value}"
|
465
|
+
when String, Symbol then "#{key}:`#{value}`"
|
406
466
|
when Hash
|
407
467
|
if @options[:set_props]
|
408
|
-
attribute_string = value.map { |k, v| "#{k}: #{v.inspect}" }.join(
|
468
|
+
attribute_string = value.map { |k, v| "#{k}: #{v.inspect}" }.join(Clause::COMMA_SPACE)
|
409
469
|
"#{key} = {#{attribute_string}}"
|
410
470
|
else
|
411
|
-
value.map
|
412
|
-
key_value_string("#{key}.`#{k}`", v, ['setter'], true)
|
413
|
-
end
|
471
|
+
value.map { |k, v| key_value_string("#{key}.`#{k}`", v, ['setter'], true) }
|
414
472
|
end
|
473
|
+
when Array then value.map { |v| from_key_and_value(key, v) }
|
474
|
+
when NilClass then []
|
415
475
|
else
|
416
476
|
fail ArgError, value
|
417
477
|
end
|
@@ -419,13 +479,13 @@ module Neo4j
|
|
419
479
|
|
420
480
|
class << self
|
421
481
|
def clause_string(clauses)
|
422
|
-
clauses.map!(&:value).join(
|
482
|
+
clauses.map!(&:value).join(Clause::COMMA_SPACE)
|
423
483
|
end
|
424
484
|
end
|
425
485
|
end
|
426
486
|
|
427
487
|
class OnCreateSetClause < SetClause
|
428
|
-
|
488
|
+
KEYWORD = 'ON CREATE SET'
|
429
489
|
|
430
490
|
def initialize(*args)
|
431
491
|
super
|
@@ -434,20 +494,24 @@ module Neo4j
|
|
434
494
|
end
|
435
495
|
|
436
496
|
class OnMatchSetClause < OnCreateSetClause
|
437
|
-
|
497
|
+
KEYWORD = 'ON MATCH SET'
|
438
498
|
end
|
439
499
|
|
440
500
|
class RemoveClause < Clause
|
441
|
-
|
501
|
+
KEYWORD = 'REMOVE'
|
442
502
|
|
443
503
|
def from_key_and_value(key, value)
|
444
504
|
case value
|
445
505
|
when /^:/
|
446
|
-
"#{key}
|
506
|
+
"#{key}:`#{value[1..-1]}`"
|
447
507
|
when String
|
448
508
|
"#{key}.#{value}"
|
449
509
|
when Symbol
|
450
|
-
"#{key}
|
510
|
+
"#{key}:`#{value}`"
|
511
|
+
when Array
|
512
|
+
value.map do |v|
|
513
|
+
from_key_and_value(key, v)
|
514
|
+
end
|
451
515
|
else
|
452
516
|
fail ArgError, value
|
453
517
|
end
|
@@ -455,13 +519,13 @@ module Neo4j
|
|
455
519
|
|
456
520
|
class << self
|
457
521
|
def clause_string(clauses)
|
458
|
-
clauses.map!(&:value).join(
|
522
|
+
clauses.map!(&:value).join(Clause::COMMA_SPACE)
|
459
523
|
end
|
460
524
|
end
|
461
525
|
end
|
462
526
|
|
463
527
|
class UnwindClause < Clause
|
464
|
-
|
528
|
+
KEYWORD = 'UNWIND'
|
465
529
|
|
466
530
|
def from_key_and_value(key, value)
|
467
531
|
case value
|
@@ -482,7 +546,7 @@ module Neo4j
|
|
482
546
|
end
|
483
547
|
|
484
548
|
class ReturnClause < Clause
|
485
|
-
|
549
|
+
KEYWORD = 'RETURN'
|
486
550
|
|
487
551
|
def from_symbol(value)
|
488
552
|
from_string(value.to_s)
|
@@ -493,9 +557,13 @@ module Neo4j
|
|
493
557
|
when Array
|
494
558
|
value.map do |v|
|
495
559
|
from_key_and_value(key, v)
|
496
|
-
end.join(
|
560
|
+
end.join(Clause::COMMA_SPACE)
|
497
561
|
when String, Symbol
|
498
|
-
|
562
|
+
if value.to_sym == :neo_id
|
563
|
+
"ID(#{key})"
|
564
|
+
else
|
565
|
+
"#{key}.#{value}"
|
566
|
+
end
|
499
567
|
else
|
500
568
|
fail ArgError, value
|
501
569
|
end
|
@@ -503,7 +571,7 @@ module Neo4j
|
|
503
571
|
|
504
572
|
class << self
|
505
573
|
def clause_string(clauses)
|
506
|
-
clauses.map!(&:value).join(
|
574
|
+
clauses.map!(&:value).join(Clause::COMMA_SPACE)
|
507
575
|
end
|
508
576
|
end
|
509
577
|
end
|
@@ -2,8 +2,7 @@ module Neo4j
|
|
2
2
|
module Core
|
3
3
|
module QueryFindInBatches
|
4
4
|
def find_in_batches(node_var, prop_var, options = {})
|
5
|
-
|
6
|
-
fail ArgumentError, "Invalid keys: #{invalid_keys.join(', ')}" if not invalid_keys.empty?
|
5
|
+
validate_find_in_batches_options!(options)
|
7
6
|
|
8
7
|
batch_size = options.delete(:batch_size) || 1000
|
9
8
|
|
@@ -13,15 +12,7 @@ module Neo4j
|
|
13
12
|
|
14
13
|
while records.any?
|
15
14
|
records_size = records.size
|
16
|
-
primary_key_offset =
|
17
|
-
records.last.send(node_var).send(prop_var)
|
18
|
-
rescue NoMethodError
|
19
|
-
begin
|
20
|
-
records.last.send(node_var)[prop_var.to_sym]
|
21
|
-
rescue NoMethodError
|
22
|
-
records.last.send("#{node_var}.#{prop_var}") # In case we're explicitly returning it
|
23
|
-
end
|
24
|
-
end
|
15
|
+
primary_key_offset = primary_key_offset(records.last, node_var, prop_var)
|
25
16
|
|
26
17
|
yield records
|
27
18
|
|
@@ -36,6 +27,23 @@ module Neo4j
|
|
36
27
|
batch.each { |result| yield result }
|
37
28
|
end
|
38
29
|
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def validate_find_in_batches_options!(options)
|
34
|
+
invalid_keys = options.keys.map(&:to_sym) - [:batch_size]
|
35
|
+
fail ArgumentError, "Invalid keys: #{invalid_keys.join(', ')}" if not invalid_keys.empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
def primary_key_offset(last_record, node_var, prop_var)
|
39
|
+
last_record.send(node_var).send(prop_var)
|
40
|
+
rescue NoMethodError
|
41
|
+
begin
|
42
|
+
last_record.send(node_var)[prop_var.to_sym]
|
43
|
+
rescue NoMethodError
|
44
|
+
last_record.send("#{node_var}.#{prop_var}") # In case we're explicitly returning it
|
45
|
+
end
|
46
|
+
end
|
39
47
|
end
|
40
48
|
end
|
41
49
|
end
|
data/lib/neo4j-core/version.rb
CHANGED
data/lib/neo4j-core.rb
CHANGED
@@ -12,26 +12,31 @@ module Neo4j
|
|
12
12
|
include Enumerable
|
13
13
|
|
14
14
|
# @return the original result from the Neo4j Cypher Engine, once forward read only !
|
15
|
-
attr_reader :source
|
15
|
+
attr_reader :source, :unwrapped
|
16
16
|
|
17
|
-
def initialize(source, query)
|
17
|
+
def initialize(source, query, unwrapped = nil)
|
18
18
|
@source = source
|
19
|
-
@struct = Struct.new(*source.columns.to_a.map(&:to_sym))
|
19
|
+
@struct = Struct.new(*source.columns.to_a.map!(&:to_sym)) unless source.columns.empty?
|
20
20
|
@unread = true
|
21
21
|
@query = query
|
22
|
+
@unwrapped = unwrapped
|
22
23
|
end
|
23
24
|
|
24
25
|
def to_s
|
25
26
|
@query
|
26
27
|
end
|
27
28
|
|
29
|
+
def unwrapped?
|
30
|
+
!!unwrapped
|
31
|
+
end
|
32
|
+
|
28
33
|
def inspect
|
29
34
|
"Enumerable query: '#{@query}'"
|
30
35
|
end
|
31
36
|
|
32
37
|
# @return [Array<Symbol>] the columns in the query result
|
33
38
|
def columns
|
34
|
-
@source.columns.map(&:to_sym)
|
39
|
+
@source.columns.map!(&:to_sym)
|
35
40
|
end
|
36
41
|
|
37
42
|
def each
|
@@ -40,13 +45,23 @@ module Neo4j
|
|
40
45
|
if block_given?
|
41
46
|
@source.each do |row|
|
42
47
|
yield(row.each_with_object(@struct.new) do |(column, value), result|
|
43
|
-
result[column.to_sym] = (value
|
48
|
+
result[column.to_sym] = unwrap(value)
|
44
49
|
end)
|
45
50
|
end
|
46
51
|
else
|
47
52
|
Enumerator.new(self)
|
48
53
|
end
|
49
54
|
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def unwrap(value)
|
59
|
+
if !value.nil? && value.respond_to?(:to_a)
|
60
|
+
value.map { |v| unwrap(v) }
|
61
|
+
else
|
62
|
+
(!value.respond_to?(:wrapper) || unwrapped?) ? value : value.wrapper
|
63
|
+
end
|
64
|
+
end
|
50
65
|
end
|
51
66
|
end
|
52
67
|
end
|