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
data/lib/neo4j-core/query.rb
CHANGED
@@ -14,6 +14,9 @@ module Neo4j
|
|
14
14
|
class Query
|
15
15
|
include Neo4j::Core::QueryClauses
|
16
16
|
include Neo4j::Core::QueryFindInBatches
|
17
|
+
DEFINED_CLAUSES = {}
|
18
|
+
|
19
|
+
attr_accessor :clauses
|
17
20
|
|
18
21
|
def initialize(options = {})
|
19
22
|
@session = options[:session] || Neo4j::Session.current
|
@@ -99,17 +102,24 @@ module Neo4j
|
|
99
102
|
# DELETE clause
|
100
103
|
# @return [Query]
|
101
104
|
|
102
|
-
METHODS = %w(
|
105
|
+
METHODS = %w(start match optional_match using where create create_unique merge set on_create_set on_match_set remove unwind delete with return order skip limit)
|
106
|
+
BREAK_METHODS = %(with)
|
107
|
+
|
108
|
+
CLAUSIFY_CLAUSE = proc do |method|
|
109
|
+
const_get(method.to_s.split('_').map(&:capitalize).join + 'Clause')
|
110
|
+
end
|
103
111
|
|
104
|
-
CLAUSES = METHODS.map
|
112
|
+
CLAUSES = METHODS.map(&CLAUSIFY_CLAUSE)
|
105
113
|
|
106
114
|
METHODS.each_with_index do |clause, i|
|
107
115
|
clause_class = CLAUSES[i]
|
108
116
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
117
|
+
DEFINED_CLAUSES[clause.to_sym] = clause_class
|
118
|
+
define_method(clause) do |*args|
|
119
|
+
build_deeper_query(clause_class, args).ergo do |result|
|
120
|
+
BREAK_METHODS.include?(clause) ? result.break : result
|
121
|
+
end
|
122
|
+
end
|
113
123
|
end
|
114
124
|
|
115
125
|
alias_method :offset, :skip
|
@@ -152,6 +162,15 @@ module Neo4j
|
|
152
162
|
self
|
153
163
|
end
|
154
164
|
|
165
|
+
def unwrapped
|
166
|
+
@_unwrapped_obj = true
|
167
|
+
self
|
168
|
+
end
|
169
|
+
|
170
|
+
def unwrapped?
|
171
|
+
!!@_unwrapped_obj
|
172
|
+
end
|
173
|
+
|
155
174
|
def response
|
156
175
|
return @response if @response
|
157
176
|
cypher = to_cypher
|
@@ -167,12 +186,18 @@ module Neo4j
|
|
167
186
|
|
168
187
|
include Enumerable
|
169
188
|
|
189
|
+
def count(var = nil)
|
190
|
+
v = var.nil? ? '*' : var
|
191
|
+
pluck("count(#{v})").first
|
192
|
+
end
|
193
|
+
|
170
194
|
def each
|
171
195
|
response = self.response
|
172
196
|
if response.is_a?(Neo4j::Server::CypherResponse)
|
197
|
+
response.unwrapped! if unwrapped?
|
173
198
|
response.to_node_enumeration
|
174
199
|
else
|
175
|
-
Neo4j::Embedded::ResultWrapper.new(response, to_cypher)
|
200
|
+
Neo4j::Embedded::ResultWrapper.new(response, to_cypher, unwrapped?)
|
176
201
|
end.each { |object| yield object }
|
177
202
|
end
|
178
203
|
|
@@ -201,12 +226,12 @@ module Neo4j
|
|
201
226
|
# Query.new.match(n: :Person).return(p: :name}.pluck('p, DISTINCT p.name') # => Array of [node, name] pairs
|
202
227
|
#
|
203
228
|
def pluck(*columns)
|
229
|
+
fail ArgumentError, 'No columns specified for Query#pluck' if columns.size.zero?
|
230
|
+
|
204
231
|
query = return_query(columns)
|
205
232
|
columns = query.response.columns
|
206
233
|
|
207
234
|
case columns.size
|
208
|
-
when 0
|
209
|
-
fail ArgumentError, 'No columns specified for Query#pluck'
|
210
235
|
when 1
|
211
236
|
column = columns[0]
|
212
237
|
query.map { |row| row[column] }
|
@@ -223,15 +248,7 @@ module Neo4j
|
|
223
248
|
query = copy
|
224
249
|
query.remove_clause_class(ReturnClause)
|
225
250
|
|
226
|
-
columns
|
227
|
-
if column_definition.is_a?(Hash)
|
228
|
-
column_definition.map { |k, v| "#{k}.#{v}" }
|
229
|
-
else
|
230
|
-
column_definition
|
231
|
-
end
|
232
|
-
end.flatten.map(&:to_sym)
|
233
|
-
|
234
|
-
query.return(columns)
|
251
|
+
query.return(*columns)
|
235
252
|
end
|
236
253
|
|
237
254
|
# Returns a CYPHER query string from the object query representation
|
@@ -239,22 +256,21 @@ module Neo4j
|
|
239
256
|
# Query.new.match(p: :Person).where(p: {age: 30}) # => "MATCH (p:Person) WHERE p.age = 30
|
240
257
|
#
|
241
258
|
# @return [String] Resulting cypher query string
|
259
|
+
EMPTY = ' '
|
242
260
|
def to_cypher
|
243
|
-
cypher_string =
|
261
|
+
cypher_string = PartitionedClauses.new(@clauses).map do |clauses|
|
244
262
|
clauses_by_class = clauses.group_by(&:class)
|
245
263
|
|
246
264
|
cypher_parts = CLAUSES.map do |clause_class|
|
247
|
-
clauses = clauses_by_class[clause_class]
|
248
|
-
|
249
|
-
clause_class.to_cypher(clauses) if clauses
|
265
|
+
clause_class.to_cypher(clauses) if clauses = clauses_by_class[clause_class]
|
250
266
|
end
|
251
267
|
|
252
|
-
|
253
|
-
|
254
|
-
end.join
|
268
|
+
cypher_parts.compact!
|
269
|
+
cypher_parts.join(EMPTY).tap(&:strip!)
|
270
|
+
end.join EMPTY
|
255
271
|
|
256
272
|
cypher_string = "CYPHER #{@options[:parser]} #{cypher_string}" if @options[:parser]
|
257
|
-
cypher_string.strip
|
273
|
+
cypher_string.tap(&:strip!)
|
258
274
|
end
|
259
275
|
|
260
276
|
# Returns a CYPHER query specifying the union of the callee object's query and the argument's query
|
@@ -289,9 +305,16 @@ module Neo4j
|
|
289
305
|
end
|
290
306
|
end
|
291
307
|
|
308
|
+
def clause?(method)
|
309
|
+
clause_class = DEFINED_CLAUSES[method] || CLAUSIFY_CLAUSE.call(method)
|
310
|
+
clauses.any? do |clause|
|
311
|
+
clause.is_a?(clause_class)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
292
315
|
protected
|
293
316
|
|
294
|
-
attr_accessor :session, :options, :
|
317
|
+
attr_accessor :session, :options, :_params
|
295
318
|
|
296
319
|
def add_clauses(clauses)
|
297
320
|
@clauses += clauses
|
@@ -312,35 +335,68 @@ module Neo4j
|
|
312
335
|
end
|
313
336
|
end
|
314
337
|
|
315
|
-
|
316
|
-
|
317
|
-
|
338
|
+
class PartitionedClauses
|
339
|
+
def initialize(clauses)
|
340
|
+
@clauses = clauses
|
341
|
+
@partitioning = [[]]
|
318
342
|
end
|
319
|
-
end
|
320
343
|
|
321
|
-
|
322
|
-
|
344
|
+
include Enumerable
|
345
|
+
|
346
|
+
def each
|
347
|
+
generate_partitioning!
|
348
|
+
|
349
|
+
@partitioning.each { |partition| yield partition }
|
350
|
+
end
|
323
351
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
352
|
+
def generate_partitioning!
|
353
|
+
@partitioning = [[]]
|
354
|
+
|
355
|
+
@clauses.each do |clause|
|
356
|
+
if clause.nil? && !fresh_partition?
|
357
|
+
@partitioning << []
|
358
|
+
elsif clause_is_order_or_limit_directly_following_with_or_order?(clause)
|
359
|
+
second_to_last << clause
|
360
|
+
elsif clause_is_with_following_order_or_limit?(clause)
|
361
|
+
second_to_last << clause
|
362
|
+
second_to_last.sort_by! { |c| c.is_a?(::Neo4j::Core::QueryClauses::OrderClause) ? 1 : 0 }
|
363
|
+
else
|
364
|
+
@partitioning.last << clause
|
365
|
+
end
|
329
366
|
end
|
330
367
|
end
|
331
368
|
|
332
|
-
|
333
|
-
end
|
369
|
+
private
|
334
370
|
|
335
|
-
|
336
|
-
|
337
|
-
|
371
|
+
def fresh_partition?
|
372
|
+
@partitioning.last == []
|
373
|
+
end
|
338
374
|
|
339
|
-
|
340
|
-
|
341
|
-
params.each do |key, value|
|
342
|
-
params[key] = value.to_s if not passthrough_classes.any? { |klass| value.is_a?(klass) }
|
375
|
+
def second_to_last
|
376
|
+
@partitioning[-2]
|
343
377
|
end
|
378
|
+
|
379
|
+
def clause_is_order_or_limit_directly_following_with_or_order?(clause)
|
380
|
+
self.class.clause_is_order_or_limit?(clause) &&
|
381
|
+
@partitioning[-2] &&
|
382
|
+
(@partitioning[-2].last.is_a?(::Neo4j::Core::QueryClauses::WithClause) ||
|
383
|
+
@partitioning[-2].last.is_a?(::Neo4j::Core::QueryClauses::OrderClause))
|
384
|
+
end
|
385
|
+
|
386
|
+
def clause_is_with_following_order_or_limit?(clause)
|
387
|
+
clause.is_a?(::Neo4j::Core::QueryClauses::WithClause) &&
|
388
|
+
@partitioning[-2] && @partitioning[-2].any? { |c| self.class.clause_is_order_or_limit?(c) }
|
389
|
+
end
|
390
|
+
|
391
|
+
def self.clause_is_order_or_limit?(clause)
|
392
|
+
clause.is_a?(::Neo4j::Core::QueryClauses::OrderClause) ||
|
393
|
+
clause.is_a?(::Neo4j::Core::QueryClauses::LimitClause)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
def merge_params
|
398
|
+
@clauses.compact!
|
399
|
+
@merge_params ||= @clauses.inject(@_params) { |params, clause| params.merge!(clause.params) }
|
344
400
|
end
|
345
401
|
end
|
346
402
|
end
|