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.
@@ -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(with start match optional_match using where set create create_unique merge on_create_set on_match_set remove unwind delete return order skip limit)
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 { |method| const_get(method.split('_').map(&:capitalize).join + 'Clause') }
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
- module_eval(%{
110
- def #{clause}(*args)
111
- build_deeper_query(#{clause_class}, args)
112
- end}, __FILE__, __LINE__)
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 = columns.map do |column_definition|
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 = partitioned_clauses.map do |clauses|
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
- cypher_string = cypher_parts.compact.join(' ')
253
- cypher_string.strip
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, :clauses, :_params
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
- def break_deeper_query
316
- copy.tap do |new_query|
317
- new_query.add_clauses [nil]
338
+ class PartitionedClauses
339
+ def initialize(clauses)
340
+ @clauses = clauses
341
+ @partitioning = [[]]
318
342
  end
319
- end
320
343
 
321
- def partitioned_clauses
322
- partitioning = [[]]
344
+ include Enumerable
345
+
346
+ def each
347
+ generate_partitioning!
348
+
349
+ @partitioning.each { |partition| yield partition }
350
+ end
323
351
 
324
- @clauses.each do |clause|
325
- if clause.nil? && partitioning.last != []
326
- partitioning << []
327
- else
328
- partitioning.last << clause
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
- partitioning
333
- end
369
+ private
334
370
 
335
- def merge_params
336
- @merge_params ||= @clauses.compact.inject(@_params) { |params, clause| params.merge(clause.params) }
337
- end
371
+ def fresh_partition?
372
+ @partitioning.last == []
373
+ end
338
374
 
339
- def sanitize_params(params)
340
- passthrough_classes = [String, Numeric, Array, Regexp]
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