activegraph 10.0.0.pre.alpha.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1989 -0
  3. data/CONTRIBUTORS +12 -0
  4. data/Gemfile +24 -0
  5. data/README.md +107 -0
  6. data/bin/rake +17 -0
  7. data/config/locales/en.yml +5 -0
  8. data/config/neo4j/add_classnames.yml +1 -0
  9. data/config/neo4j/config.yml +38 -0
  10. data/lib/neo4j.rb +116 -0
  11. data/lib/neo4j/active_base.rb +89 -0
  12. data/lib/neo4j/active_node.rb +108 -0
  13. data/lib/neo4j/active_node/callbacks.rb +8 -0
  14. data/lib/neo4j/active_node/dependent.rb +11 -0
  15. data/lib/neo4j/active_node/dependent/association_methods.rb +49 -0
  16. data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +51 -0
  17. data/lib/neo4j/active_node/enum.rb +26 -0
  18. data/lib/neo4j/active_node/has_n.rb +612 -0
  19. data/lib/neo4j/active_node/has_n/association.rb +278 -0
  20. data/lib/neo4j/active_node/has_n/association/rel_factory.rb +61 -0
  21. data/lib/neo4j/active_node/has_n/association/rel_wrapper.rb +23 -0
  22. data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +108 -0
  23. data/lib/neo4j/active_node/id_property.rb +224 -0
  24. data/lib/neo4j/active_node/id_property/accessor.rb +62 -0
  25. data/lib/neo4j/active_node/initialize.rb +21 -0
  26. data/lib/neo4j/active_node/labels.rb +207 -0
  27. data/lib/neo4j/active_node/labels/index.rb +37 -0
  28. data/lib/neo4j/active_node/labels/reloading.rb +21 -0
  29. data/lib/neo4j/active_node/node_list_formatter.rb +13 -0
  30. data/lib/neo4j/active_node/node_wrapper.rb +54 -0
  31. data/lib/neo4j/active_node/orm_adapter.rb +82 -0
  32. data/lib/neo4j/active_node/persistence.rb +187 -0
  33. data/lib/neo4j/active_node/property.rb +60 -0
  34. data/lib/neo4j/active_node/query.rb +76 -0
  35. data/lib/neo4j/active_node/query/query_proxy.rb +374 -0
  36. data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +177 -0
  37. data/lib/neo4j/active_node/query/query_proxy_eager_loading/association_tree.rb +75 -0
  38. data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +110 -0
  39. data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +19 -0
  40. data/lib/neo4j/active_node/query/query_proxy_link.rb +139 -0
  41. data/lib/neo4j/active_node/query/query_proxy_methods.rb +302 -0
  42. data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +86 -0
  43. data/lib/neo4j/active_node/query_methods.rb +68 -0
  44. data/lib/neo4j/active_node/reflection.rb +86 -0
  45. data/lib/neo4j/active_node/rels.rb +11 -0
  46. data/lib/neo4j/active_node/scope.rb +166 -0
  47. data/lib/neo4j/active_node/unpersisted.rb +48 -0
  48. data/lib/neo4j/active_node/validations.rb +59 -0
  49. data/lib/neo4j/active_rel.rb +67 -0
  50. data/lib/neo4j/active_rel/callbacks.rb +15 -0
  51. data/lib/neo4j/active_rel/initialize.rb +28 -0
  52. data/lib/neo4j/active_rel/persistence.rb +134 -0
  53. data/lib/neo4j/active_rel/persistence/query_factory.rb +95 -0
  54. data/lib/neo4j/active_rel/property.rb +95 -0
  55. data/lib/neo4j/active_rel/query.rb +101 -0
  56. data/lib/neo4j/active_rel/rel_wrapper.rb +31 -0
  57. data/lib/neo4j/active_rel/related_node.rb +87 -0
  58. data/lib/neo4j/active_rel/types.rb +82 -0
  59. data/lib/neo4j/active_rel/validations.rb +8 -0
  60. data/lib/neo4j/ansi.rb +14 -0
  61. data/lib/neo4j/class_arguments.rb +39 -0
  62. data/lib/neo4j/config.rb +135 -0
  63. data/lib/neo4j/core.rb +14 -0
  64. data/lib/neo4j/core/connection_failed_error.rb +6 -0
  65. data/lib/neo4j/core/cypher_error.rb +37 -0
  66. data/lib/neo4j/core/driver.rb +66 -0
  67. data/lib/neo4j/core/has_uri.rb +63 -0
  68. data/lib/neo4j/core/instrumentable.rb +36 -0
  69. data/lib/neo4j/core/label.rb +158 -0
  70. data/lib/neo4j/core/logging.rb +44 -0
  71. data/lib/neo4j/core/node.rb +23 -0
  72. data/lib/neo4j/core/querable.rb +88 -0
  73. data/lib/neo4j/core/query.rb +487 -0
  74. data/lib/neo4j/core/query_builder.rb +32 -0
  75. data/lib/neo4j/core/query_clauses.rb +727 -0
  76. data/lib/neo4j/core/query_ext.rb +24 -0
  77. data/lib/neo4j/core/query_find_in_batches.rb +49 -0
  78. data/lib/neo4j/core/relationship.rb +13 -0
  79. data/lib/neo4j/core/responses.rb +50 -0
  80. data/lib/neo4j/core/result.rb +33 -0
  81. data/lib/neo4j/core/schema.rb +30 -0
  82. data/lib/neo4j/core/schema_errors.rb +12 -0
  83. data/lib/neo4j/core/wrappable.rb +30 -0
  84. data/lib/neo4j/errors.rb +57 -0
  85. data/lib/neo4j/migration.rb +148 -0
  86. data/lib/neo4j/migrations.rb +27 -0
  87. data/lib/neo4j/migrations/base.rb +77 -0
  88. data/lib/neo4j/migrations/check_pending.rb +20 -0
  89. data/lib/neo4j/migrations/helpers.rb +105 -0
  90. data/lib/neo4j/migrations/helpers/id_property.rb +75 -0
  91. data/lib/neo4j/migrations/helpers/relationships.rb +66 -0
  92. data/lib/neo4j/migrations/helpers/schema.rb +51 -0
  93. data/lib/neo4j/migrations/migration_file.rb +24 -0
  94. data/lib/neo4j/migrations/runner.rb +195 -0
  95. data/lib/neo4j/migrations/schema.rb +44 -0
  96. data/lib/neo4j/migrations/schema_migration.rb +14 -0
  97. data/lib/neo4j/model_schema.rb +139 -0
  98. data/lib/neo4j/paginated.rb +27 -0
  99. data/lib/neo4j/railtie.rb +105 -0
  100. data/lib/neo4j/schema/operation.rb +102 -0
  101. data/lib/neo4j/shared.rb +60 -0
  102. data/lib/neo4j/shared/attributes.rb +216 -0
  103. data/lib/neo4j/shared/callbacks.rb +68 -0
  104. data/lib/neo4j/shared/cypher.rb +37 -0
  105. data/lib/neo4j/shared/declared_properties.rb +204 -0
  106. data/lib/neo4j/shared/declared_property.rb +109 -0
  107. data/lib/neo4j/shared/declared_property/index.rb +37 -0
  108. data/lib/neo4j/shared/enum.rb +167 -0
  109. data/lib/neo4j/shared/filtered_hash.rb +79 -0
  110. data/lib/neo4j/shared/identity.rb +34 -0
  111. data/lib/neo4j/shared/initialize.rb +64 -0
  112. data/lib/neo4j/shared/marshal.rb +23 -0
  113. data/lib/neo4j/shared/mass_assignment.rb +64 -0
  114. data/lib/neo4j/shared/permitted_attributes.rb +28 -0
  115. data/lib/neo4j/shared/persistence.rb +282 -0
  116. data/lib/neo4j/shared/property.rb +240 -0
  117. data/lib/neo4j/shared/query_factory.rb +102 -0
  118. data/lib/neo4j/shared/rel_type_converters.rb +43 -0
  119. data/lib/neo4j/shared/serialized_properties.rb +30 -0
  120. data/lib/neo4j/shared/type_converters.rb +433 -0
  121. data/lib/neo4j/shared/typecasted_attributes.rb +98 -0
  122. data/lib/neo4j/shared/typecaster.rb +53 -0
  123. data/lib/neo4j/shared/validations.rb +44 -0
  124. data/lib/neo4j/tasks/migration.rake +202 -0
  125. data/lib/neo4j/timestamps.rb +11 -0
  126. data/lib/neo4j/timestamps/created.rb +9 -0
  127. data/lib/neo4j/timestamps/updated.rb +9 -0
  128. data/lib/neo4j/transaction.rb +139 -0
  129. data/lib/neo4j/type_converters.rb +7 -0
  130. data/lib/neo4j/undeclared_properties.rb +53 -0
  131. data/lib/neo4j/version.rb +3 -0
  132. data/lib/neo4j/wrapper.rb +4 -0
  133. data/lib/rails/generators/neo4j/migration/migration_generator.rb +14 -0
  134. data/lib/rails/generators/neo4j/migration/templates/migration.erb +9 -0
  135. data/lib/rails/generators/neo4j/model/model_generator.rb +88 -0
  136. data/lib/rails/generators/neo4j/model/templates/migration.erb +9 -0
  137. data/lib/rails/generators/neo4j/model/templates/model.erb +15 -0
  138. data/lib/rails/generators/neo4j/upgrade_v8/templates/migration.erb +17 -0
  139. data/lib/rails/generators/neo4j/upgrade_v8/upgrade_v8_generator.rb +32 -0
  140. data/lib/rails/generators/neo4j_generator.rb +119 -0
  141. data/neo4j.gemspec +51 -0
  142. metadata +421 -0
@@ -0,0 +1,487 @@
1
+ require 'neo4j/core/query_clauses'
2
+ require 'neo4j/core/query_find_in_batches'
3
+ require 'active_support/notifications'
4
+
5
+ module Neo4j
6
+ module Core
7
+ # Allows for generation of cypher queries via ruby method calls (inspired by ActiveRecord / arel syntax)
8
+ #
9
+ # Can be used to express cypher queries in ruby nicely, or to more easily generate queries programatically.
10
+ #
11
+ # Also, queries can be passed around an application to progressively build a query across different concerns
12
+ #
13
+ # See also the following link for full cypher language documentation:
14
+ # http://docs.neo4j.org/chunked/milestone/cypher-query-lang.html
15
+ class Query
16
+ include Neo4j::Core::QueryClauses
17
+ include Neo4j::Core::QueryFindInBatches
18
+ DEFINED_CLAUSES = {}
19
+
20
+
21
+ attr_accessor :clauses
22
+
23
+ class Parameters
24
+ def initialize(hash = nil)
25
+ @parameters = (hash || {})
26
+ end
27
+
28
+ def to_hash
29
+ @parameters
30
+ end
31
+
32
+ def copy
33
+ self.class.new(@parameters.dup)
34
+ end
35
+
36
+ def add_param(key, value)
37
+ free_param_key(key).tap do |k|
38
+ @parameters[k.freeze] = value
39
+ end
40
+ end
41
+
42
+ def remove_param(key)
43
+ @parameters.delete(key.to_sym)
44
+ end
45
+
46
+ def add_params(params)
47
+ params.map do |key, value|
48
+ add_param(key, value)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def free_param_key(key)
55
+ k = key.to_sym
56
+
57
+ return k if !@parameters.key?(k)
58
+
59
+ i = 2
60
+ i += 1 while @parameters.key?("#{key}#{i}".to_sym)
61
+
62
+ "#{key}#{i}".to_sym
63
+ end
64
+ end
65
+
66
+ class << self
67
+ attr_accessor :pretty_cypher
68
+ end
69
+
70
+ def initialize(options = {})
71
+ @session = options[:session]
72
+
73
+ @options = options
74
+ @clauses = []
75
+ @_params = {}
76
+ @params = Parameters.new
77
+ end
78
+
79
+ def inspect
80
+ "#<Query CYPHER: #{ANSI::YELLOW}#{to_cypher.inspect}#{ANSI::CLEAR}>"
81
+ end
82
+
83
+ # @method start *args
84
+ # START clause
85
+ # @return [Query]
86
+
87
+ # @method match *args
88
+ # MATCH clause
89
+ # @return [Query]
90
+
91
+ # @method optional_match *args
92
+ # OPTIONAL MATCH clause
93
+ # @return [Query]
94
+
95
+ # @method using *args
96
+ # USING clause
97
+ # @return [Query]
98
+
99
+ # @method where *args
100
+ # WHERE clause
101
+ # @return [Query]
102
+
103
+ # @method with *args
104
+ # WITH clause
105
+ # @return [Query]
106
+
107
+ # @method with_distinct *args
108
+ # WITH clause with DISTINCT specified
109
+ # @return [Query]
110
+
111
+ # @method order *args
112
+ # ORDER BY clause
113
+ # @return [Query]
114
+
115
+ # @method limit *args
116
+ # LIMIT clause
117
+ # @return [Query]
118
+
119
+ # @method skip *args
120
+ # SKIP clause
121
+ # @return [Query]
122
+
123
+ # @method set *args
124
+ # SET clause
125
+ # @return [Query]
126
+
127
+ # @method remove *args
128
+ # REMOVE clause
129
+ # @return [Query]
130
+
131
+ # @method unwind *args
132
+ # UNWIND clause
133
+ # @return [Query]
134
+
135
+ # @method return *args
136
+ # RETURN clause
137
+ # @return [Query]
138
+
139
+ # @method create *args
140
+ # CREATE clause
141
+ # @return [Query]
142
+
143
+ # @method create_unique *args
144
+ # CREATE UNIQUE clause
145
+ # @return [Query]
146
+
147
+ # @method merge *args
148
+ # MERGE clause
149
+ # @return [Query]
150
+
151
+ # @method on_create_set *args
152
+ # ON CREATE SET clause
153
+ # @return [Query]
154
+
155
+ # @method on_match_set *args
156
+ # ON MATCH SET clause
157
+ # @return [Query]
158
+
159
+ # @method delete *args
160
+ # DELETE clause
161
+ # @return [Query]
162
+
163
+ # @method detach_delete *args
164
+ # DETACH DELETE clause
165
+ # @return [Query]
166
+
167
+ METHODS = %w[start match optional_match call using where create create_unique merge set on_create_set on_match_set remove unwind delete detach_delete with with_distinct return order skip limit] # rubocop:disable Metrics/LineLength
168
+ BREAK_METHODS = %(with with_distinct call)
169
+
170
+ CLAUSIFY_CLAUSE = proc { |method| const_get(method.to_s.split('_').map(&:capitalize).join + 'Clause') }
171
+ CLAUSES = METHODS.map(&CLAUSIFY_CLAUSE)
172
+
173
+ METHODS.each_with_index do |clause, i|
174
+ clause_class = CLAUSES[i]
175
+
176
+ DEFINED_CLAUSES[clause.to_sym] = clause_class
177
+ define_method(clause) do |*args|
178
+ result = build_deeper_query(clause_class, args)
179
+
180
+ BREAK_METHODS.include?(clause) ? result.break : result
181
+ end
182
+ end
183
+
184
+ alias offset skip
185
+ alias order_by order
186
+
187
+ # Clears out previous order clauses and allows only for those specified by args
188
+ def reorder(*args)
189
+ query = copy
190
+
191
+ query.remove_clause_class(OrderClause)
192
+ query.order(*args)
193
+ end
194
+
195
+ # Works the same as the #where method, but the clause is surrounded by a
196
+ # Cypher NOT() function
197
+ def where_not(*args)
198
+ build_deeper_query(WhereClause, args, not: true)
199
+ end
200
+
201
+ # Works the same as the #set method, but when given a nested array it will set properties rather than setting entire objects
202
+ # @example
203
+ # # Creates a query representing the cypher: MATCH (n:Person) SET n.age = 19
204
+ # Query.new.match(n: :Person).set_props(n: {age: 19})
205
+ def set_props(*args) # rubocop:disable Naming/AccessorMethodName
206
+ build_deeper_query(SetClause, args, set_props: true)
207
+ end
208
+
209
+ # Allows what's been built of the query so far to be frozen and the rest built anew. Can be called multiple times in a string of method calls
210
+ # @example
211
+ # # Creates a query representing the cypher: MATCH (q:Person), r:Car MATCH (p: Person)-->q
212
+ # Query.new.match(q: Person).match('r:Car').break.match('(p: Person)-->q')
213
+ def break
214
+ build_deeper_query(nil)
215
+ end
216
+
217
+ # Allows for the specification of values for params specified in query
218
+ # @example
219
+ # # Creates a query representing the cypher: MATCH (q: Person {id: {id}})
220
+ # # Calls to params don't affect the cypher query generated, but the params will be
221
+ # # Passed down when the query is made
222
+ # Query.new.match('(q: Person {id: {id}})').params(id: 12)
223
+ #
224
+ def params(args)
225
+ copy.tap { |new_query| new_query.instance_variable_get('@params'.freeze).add_params(args) }
226
+ end
227
+
228
+ def unwrapped
229
+ @_unwrapped_obj = true
230
+ self
231
+ end
232
+
233
+ def unwrapped?
234
+ !!@_unwrapped_obj
235
+ end
236
+
237
+ def response
238
+ return @response if @response
239
+
240
+ @response = Neo4j::Transaction.query(self, transaction: Transaction.root, wrap_level: (:core_entity if unwrapped?))
241
+ end
242
+
243
+ def raise_if_cypher_error!(response)
244
+ response.raise_cypher_error if response.respond_to?(:error?) && response.error?
245
+ end
246
+
247
+ def match_nodes(hash, optional_match = false)
248
+ hash.inject(self) do |query, (variable, node_object)|
249
+ neo_id = (node_object.respond_to?(:neo_id) ? node_object.neo_id : node_object)
250
+
251
+ match_method = optional_match ? :optional_match : :match
252
+ query.send(match_method, variable).where(variable => {neo_id: neo_id})
253
+ end
254
+ end
255
+
256
+ def optional_match_nodes(hash)
257
+ match_nodes(hash, true)
258
+ end
259
+
260
+ include Enumerable
261
+
262
+ def count(var = nil)
263
+ v = var.nil? ? '*' : var
264
+ pluck("count(#{v})").first
265
+ end
266
+
267
+ def each
268
+ response.each { |object| yield object }
269
+ end
270
+
271
+ # @method to_a
272
+ # Class is Enumerable. Each yield is a Hash with the key matching the variable returned and the value being the value for that key from the response
273
+ # @return [Array]
274
+ # @raise [Neo4j::Server::CypherResponse::ResponseError] Raises errors from neo4j server
275
+
276
+
277
+ # Executes a query without returning the result
278
+ # @return [Boolean] true if successful
279
+ # @raise [Neo4j::Server::CypherResponse::ResponseError] Raises errors from neo4j server
280
+ def exec
281
+ response
282
+
283
+ true
284
+ end
285
+
286
+ # Return the specified columns as an array.
287
+ # If one column is specified, a one-dimensional array is returned with the values of that column
288
+ # If two columns are specified, a n-dimensional array is returned with the values of those columns
289
+ #
290
+ # @example
291
+ # Query.new.match(n: :Person).return(p: :name}.pluck(p: :name) # => Array of names
292
+ # @example
293
+ # Query.new.match(n: :Person).return(p: :name}.pluck('p, DISTINCT p.name') # => Array of [node, name] pairs
294
+ #
295
+ def pluck(*columns)
296
+ fail ArgumentError, 'No columns specified for Query#pluck' if columns.size.zero?
297
+
298
+ query = return_query(columns)
299
+ columns = query.response.columns
300
+
301
+ if columns.size == 1
302
+ column = columns[0]
303
+ query.map { |row| row[column] }
304
+ else
305
+ query.map { |row| columns.map { |column| row[column] } }
306
+ end
307
+ end
308
+
309
+ def return_query(columns)
310
+ query = copy
311
+ query.remove_clause_class(ReturnClause)
312
+
313
+ query.return(*columns)
314
+ end
315
+
316
+ # Returns a CYPHER query string from the object query representation
317
+ # @example
318
+ # Query.new.match(p: :Person).where(p: {age: 30}) # => "MATCH (p:Person) WHERE p.age = 30
319
+ #
320
+ # @return [String] Resulting cypher query string
321
+ EMPTY = ' '
322
+ NEWLINE = "\n"
323
+ def to_cypher(options = {})
324
+ join_string = options[:pretty] ? NEWLINE : EMPTY
325
+
326
+ cypher_string = partitioned_clauses.map do |clauses|
327
+ clauses_by_class = clauses.group_by(&:class)
328
+
329
+ cypher_parts = CLAUSES.map do |clause_class|
330
+ clause_class.to_cypher(clauses, options[:pretty]) if clauses = clauses_by_class[clause_class]
331
+ end.compact
332
+
333
+ cypher_parts.join(join_string).tap(&:strip!)
334
+ end.join(join_string)
335
+
336
+ cypher_string = "CYPHER #{@options[:parser]} #{cypher_string}" if @options[:parser]
337
+ cypher_string.tap(&:strip!)
338
+ end
339
+ alias cypher to_cypher
340
+
341
+ def pretty_cypher
342
+ to_cypher(pretty: true)
343
+ end
344
+
345
+ def context
346
+ @options[:context]
347
+ end
348
+
349
+ def parameters
350
+ to_cypher
351
+ merge_params
352
+ end
353
+
354
+ def partitioned_clauses
355
+ @partitioned_clauses ||= PartitionedClauses.new(@clauses)
356
+ end
357
+
358
+ def print_cypher
359
+ puts to_cypher(pretty: true).gsub(/\e[^m]+m/, '')
360
+ end
361
+
362
+ # Returns a CYPHER query specifying the union of the callee object's query and the argument's query
363
+ #
364
+ # @example
365
+ # # Generates cypher: MATCH (n:Person) UNION MATCH (o:Person) WHERE o.age = 10
366
+ # q = Neo4j::Core::Query.new.match(o: :Person).where(o: {age: 10})
367
+ # result = Neo4j::Core::Query.new.match(n: :Person).union_cypher(q)
368
+ #
369
+ # @param other [Query] Second half of UNION
370
+ # @param options [Hash] Specify {all: true} to use UNION ALL
371
+ # @return [String] Resulting UNION cypher query string
372
+ def union_cypher(other, options = {})
373
+ "#{to_cypher} UNION#{options[:all] ? ' ALL' : ''} #{other.to_cypher}"
374
+ end
375
+
376
+ def &(other)
377
+ self.class.new(session: @session).tap do |new_query|
378
+ new_query.options = options.merge(other.options)
379
+ new_query.clauses = clauses + other.clauses
380
+ end.params(other._params)
381
+ end
382
+
383
+ def copy
384
+ dup.tap do |query|
385
+ to_cypher
386
+ query.instance_variable_set('@params'.freeze, @params.copy)
387
+ query.instance_variable_set('@partitioned_clauses'.freeze, nil)
388
+ query.instance_variable_set('@response'.freeze, nil)
389
+ end
390
+ end
391
+
392
+ def clause?(method)
393
+ clause_class = DEFINED_CLAUSES[method] || CLAUSIFY_CLAUSE.call(method)
394
+ clauses.any? { |clause| clause.is_a?(clause_class) }
395
+ end
396
+
397
+ protected
398
+
399
+ attr_accessor :session, :options, :_params
400
+
401
+ def add_clauses(clauses)
402
+ @clauses += clauses
403
+ end
404
+
405
+ def remove_clause_class(clause_class)
406
+ @clauses = @clauses.reject { |clause| clause.is_a?(clause_class) }
407
+ end
408
+
409
+ private
410
+
411
+ def build_deeper_query(clause_class, args = {}, options = {})
412
+ copy.tap do |new_query|
413
+ new_query.add_clauses [nil] if [nil, WithClause].include?(clause_class)
414
+ new_query.add_clauses clause_class.from_args(args, new_query.instance_variable_get('@params'.freeze), options) if clause_class
415
+ end
416
+ end
417
+
418
+ class PartitionedClauses
419
+ def initialize(clauses)
420
+ @clauses = clauses
421
+ @partitioning = [[]]
422
+ end
423
+
424
+ include Enumerable
425
+
426
+ def each
427
+ generate_partitioning!
428
+
429
+ @partitioning.each { |partition| yield partition }
430
+ end
431
+
432
+ def generate_partitioning!
433
+ @partitioning = [[]]
434
+
435
+ @clauses.each do |clause|
436
+ if clause.nil? && !fresh_partition?
437
+ @partitioning << []
438
+ elsif clause_is_order_or_limit_directly_following_with_or_order?(clause)
439
+ second_to_last << clause
440
+ elsif clause_is_with_following_order_or_limit?(clause)
441
+ second_to_last << clause
442
+ second_to_last.sort_by! { |c| c.is_a?(::Neo4j::Core::QueryClauses::OrderClause) ? 1 : 0 }
443
+ else
444
+ @partitioning.last << clause
445
+ end
446
+ end
447
+ end
448
+
449
+ private
450
+
451
+ def fresh_partition?
452
+ @partitioning.last == []
453
+ end
454
+
455
+ def second_to_last
456
+ @partitioning[-2]
457
+ end
458
+
459
+ def clause_is_order_or_limit_directly_following_with_or_order?(clause)
460
+ self.class.clause_is_order_or_limit?(clause) &&
461
+ @partitioning[-2] &&
462
+ @partitioning[-1].empty? &&
463
+ (@partitioning[-2].last.is_a?(::Neo4j::Core::QueryClauses::WithClause) ||
464
+ @partitioning[-2].last.is_a?(::Neo4j::Core::QueryClauses::OrderClause))
465
+ end
466
+
467
+ def clause_is_with_following_order_or_limit?(clause)
468
+ clause.is_a?(::Neo4j::Core::QueryClauses::WithClause) &&
469
+ @partitioning[-2] && @partitioning[-2].any? { |c| self.class.clause_is_order_or_limit?(c) }
470
+ end
471
+
472
+ class << self
473
+ def clause_is_order_or_limit?(clause)
474
+ clause.is_a?(::Neo4j::Core::QueryClauses::OrderClause) ||
475
+ clause.is_a?(::Neo4j::Core::QueryClauses::LimitClause)
476
+ end
477
+ end
478
+ end
479
+
480
+ # SHOULD BE DEPRECATED
481
+ def merge_params
482
+ @merge_params_base ||= @clauses.compact.inject({}) { |params, clause| params.merge!(clause.params) }
483
+ @params.to_hash.merge(@merge_params_base)
484
+ end
485
+ end
486
+ end
487
+ end