neo4j-core 4.0.7 → 5.0.0.rc.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/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
|