neo4j-core 2.0.0.alpha.1-java → 2.0.0-java

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.
Files changed (66) hide show
  1. data/Gemfile +2 -2
  2. data/README.rdoc +161 -13
  3. data/config/neo4j/config.yml +1 -1
  4. data/lib/db/active_tx_log +1 -0
  5. data/lib/db/index/lucene-store.db +0 -0
  6. data/lib/db/index/lucene.log.active +0 -0
  7. data/lib/db/messages.log +2299 -0
  8. data/lib/db/neostore +0 -0
  9. data/lib/db/neostore.id +0 -0
  10. data/lib/db/neostore.nodestore.db +0 -0
  11. data/lib/db/neostore.nodestore.db.id +0 -0
  12. data/lib/db/neostore.propertystore.db +0 -0
  13. data/lib/db/neostore.propertystore.db.arrays +0 -0
  14. data/lib/db/neostore.propertystore.db.arrays.id +0 -0
  15. data/lib/db/neostore.propertystore.db.id +0 -0
  16. data/lib/db/neostore.propertystore.db.index +0 -0
  17. data/lib/db/neostore.propertystore.db.index.id +0 -0
  18. data/lib/db/neostore.propertystore.db.index.keys +0 -0
  19. data/lib/db/neostore.propertystore.db.index.keys.id +0 -0
  20. data/lib/db/neostore.propertystore.db.strings +0 -0
  21. data/lib/db/neostore.propertystore.db.strings.id +0 -0
  22. data/lib/db/neostore.relationshipstore.db +0 -0
  23. data/lib/db/neostore.relationshipstore.db.id +0 -0
  24. data/lib/db/neostore.relationshiptypestore.db +0 -0
  25. data/lib/db/neostore.relationshiptypestore.db.id +0 -0
  26. data/lib/db/neostore.relationshiptypestore.db.names +0 -0
  27. data/lib/db/neostore.relationshiptypestore.db.names.id +0 -0
  28. data/lib/db/nioneo_logical.log.active +0 -0
  29. data/lib/db/tm_tx_log.1 +0 -0
  30. data/lib/neo4j-core.rb +20 -3
  31. data/lib/neo4j-core/cypher/cypher.rb +1033 -0
  32. data/lib/neo4j-core/cypher/result_wrapper.rb +48 -0
  33. data/lib/neo4j-core/database.rb +4 -5
  34. data/lib/neo4j-core/event_handler.rb +1 -1
  35. data/lib/neo4j-core/hash_with_indifferent_access.rb +165 -0
  36. data/lib/neo4j-core/index/class_methods.rb +27 -41
  37. data/lib/neo4j-core/index/index.rb +3 -4
  38. data/lib/neo4j-core/index/index_config.rb +30 -23
  39. data/lib/neo4j-core/index/indexer.rb +65 -53
  40. data/lib/neo4j-core/index/indexer_registry.rb +2 -2
  41. data/lib/neo4j-core/index/lucene_query.rb +53 -42
  42. data/lib/neo4j-core/index/unique_factory.rb +54 -0
  43. data/lib/neo4j-core/node/class_methods.rb +4 -4
  44. data/lib/neo4j-core/node/node.rb +1 -8
  45. data/lib/neo4j-core/property/java.rb +59 -0
  46. data/lib/neo4j-core/property/property.rb +1 -3
  47. data/lib/neo4j-core/relationship/relationship.rb +8 -10
  48. data/lib/neo4j-core/rels/rels.rb +9 -4
  49. data/lib/neo4j-core/rels/traverser.rb +13 -27
  50. data/lib/neo4j-core/traversal/prune_evaluator.rb +2 -2
  51. data/lib/neo4j-core/traversal/traverser.rb +122 -27
  52. data/lib/neo4j-core/version.rb +1 -1
  53. data/lib/neo4j-core/wrapper/class_methods.rb +22 -0
  54. data/lib/neo4j-core/wrapper/wrapper.rb +20 -0
  55. data/lib/neo4j/algo.rb +300 -0
  56. data/lib/neo4j/config.rb +3 -6
  57. data/lib/neo4j/cypher.rb +180 -0
  58. data/lib/neo4j/neo4j.rb +51 -23
  59. data/lib/neo4j/node.rb +27 -0
  60. data/lib/neo4j/relationship.rb +25 -0
  61. data/lib/test.rb~ +27 -0
  62. data/neo4j-core.gemspec +2 -2
  63. metadata +44 -11
  64. data/lib/neo4j-core/type_converters/type_converters.rb +0 -287
  65. data/lib/neo4j-core/version.rb~ +0 -3
  66. data/lib/test.rb +0 -27
data/lib/db/neostore ADDED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
data/lib/neo4j-core.rb CHANGED
@@ -1,4 +1,12 @@
1
1
  require 'java'
2
+ include Java
3
+
4
+ module Neo4j
5
+ # Enumerator has been moved to top level in Ruby 1.9.2, make it compatible with Ruby 1.8.7
6
+ Enumerator = Enumerable::Enumerator unless defined? Enumerator
7
+ end
8
+
9
+ require 'neo4j-core/version'
2
10
  require 'neo4j/config'
3
11
 
4
12
  require 'neo4j-community'
@@ -12,6 +20,7 @@ require 'neo4j-core/database'
12
20
  require 'neo4j-core/to_java'
13
21
 
14
22
  require 'neo4j-core/property/property'
23
+ require 'neo4j-core/property/java'
15
24
 
16
25
  require 'neo4j-core/rels/rels'
17
26
  require 'neo4j-core/rels/traverser'
@@ -29,9 +38,13 @@ require 'neo4j-core/index/indexer_registry'
29
38
  require 'neo4j-core/index/index_config'
30
39
  require 'neo4j-core/index/indexer'
31
40
  require 'neo4j-core/index/lucene_query'
41
+ require 'neo4j-core/index/unique_factory'
32
42
 
33
43
  require 'neo4j-core/equal/equal'
34
44
 
45
+ require 'neo4j-core/wrapper/class_methods'
46
+ require 'neo4j-core/wrapper/wrapper'
47
+
35
48
  require 'neo4j-core/relationship/relationship'
36
49
  require 'neo4j-core/relationship/class_methods'
37
50
 
@@ -40,8 +53,6 @@ require 'neo4j-core/node/class_methods'
40
53
 
41
54
  require 'neo4j/transaction'
42
55
 
43
- require 'neo4j-core/type_converters/type_converters'
44
-
45
56
  require 'neo4j-core/traversal/evaluator'
46
57
  require 'neo4j-core/traversal/filter_predicate'
47
58
  require 'neo4j-core/traversal/prune_evaluator'
@@ -49,5 +60,11 @@ require 'neo4j-core/traversal/rel_expander'
49
60
  require 'neo4j-core/traversal/traversal'
50
61
  require 'neo4j-core/traversal/traverser'
51
62
 
63
+ require 'neo4j-core/cypher/cypher'
64
+ require 'neo4j-core/cypher/result_wrapper'
65
+ require 'neo4j-core/hash_with_indifferent_access'
66
+
67
+ require 'neo4j/algo'
68
+ require 'neo4j/cypher'
52
69
  require 'neo4j/node'
53
- require 'neo4j/relationship'
70
+ require 'neo4j/relationship'
@@ -0,0 +1,1033 @@
1
+ module Neo4j
2
+ module Core
3
+
4
+ # This module contains a number of mixins and classes used by the neo4j.rb cypher DSL.
5
+ # The Cypher DSL is evaluated in the context of {Neo4j::Cypher} which contains a number of methods (e.g. {Neo4j::Cypher#node})
6
+ # which returns classes from this module.
7
+ module Cypher
8
+
9
+ module MathFunctions
10
+ def abs(value=nil)
11
+ _add_math_func(:abs, value)
12
+ end
13
+
14
+ def sqrt(value=nil)
15
+ _add_math_func(:sqrt, value)
16
+ end
17
+
18
+ def round(value=nil)
19
+ _add_math_func(:round, value)
20
+ end
21
+
22
+ def sign(value=nil)
23
+ _add_math_func(:sign, value)
24
+ end
25
+
26
+ # @private
27
+ def _add_math_func(name, value=nil)
28
+ value ||= self.respond_to?(:var_name) ? self.var_name : to_s
29
+ expressions.delete(self)
30
+ Property.new(expressions, nil, name).to_function!(value)
31
+ end
32
+ end
33
+
34
+ module MathOperator
35
+ def -(other)
36
+ ExprOp.new(self, other, '-')
37
+ end
38
+
39
+ def +(other)
40
+ ExprOp.new(self, other, '+')
41
+ end
42
+ end
43
+
44
+ module Comparable
45
+ def <(other)
46
+ ExprOp.new(self, other, '<')
47
+ end
48
+
49
+ def <=(other)
50
+ ExprOp.new(self, other, '<=')
51
+ end
52
+
53
+ def =~(other)
54
+ ExprOp.new(self, other, '=~')
55
+ end
56
+
57
+ def >(other)
58
+ ExprOp.new(self, other, '>')
59
+ end
60
+
61
+ def >=(other)
62
+ ExprOp.new(self, other, '>=')
63
+ end
64
+
65
+ ## Only in 1.9
66
+ if RUBY_VERSION > "1.9.0"
67
+ eval %{
68
+ def !=(other)
69
+ other.is_a?(String) ? ExprOp.new(self, other, "!=") : super
70
+ end }
71
+ end
72
+
73
+ def ==(other)
74
+ if other.is_a?(Fixnum) || other.is_a?(String) || other.is_a?(Regexp)
75
+ ExprOp.new(self, other, "=")
76
+ else
77
+ super
78
+ end
79
+ end
80
+ end
81
+
82
+ module PredicateMethods
83
+ def all?(&block)
84
+ self.respond_to?(:iterable)
85
+ Predicate.new(expressions, :op => 'all', :clause => :where, :input => input, :iterable => iterable, :predicate_block => block)
86
+ end
87
+
88
+ def extract(&block)
89
+ Predicate.new(expressions, :op => 'extract', :clause => :return, :input => input, :iterable => iterable, :predicate_block => block)
90
+ end
91
+
92
+ def filter(&block)
93
+ Predicate.new(expressions, :op => 'filter', :clause => :return, :input => input, :iterable => iterable, :predicate_block => block)
94
+ end
95
+
96
+ def any?(&block)
97
+ Predicate.new(@expressions, :op => 'any', :clause => :where, :input => input, :iterable => iterable, :predicate_block => block)
98
+ end
99
+
100
+ def none?(&block)
101
+ Predicate.new(@expressions, :op => 'none', :clause => :where, :input => input, :iterable => iterable, :predicate_block => block)
102
+ end
103
+
104
+ def single?(&block)
105
+ Predicate.new(@expressions, :op => 'single', :clause => :where, :input => input, :iterable => iterable, :predicate_block => block)
106
+ end
107
+
108
+ end
109
+
110
+ module Variable
111
+ attr_accessor :return_method
112
+
113
+ def distinct
114
+ self.return_method = {:name => 'distinct', :bracket => false}
115
+ self
116
+ end
117
+
118
+ def [](prop_name)
119
+ Property.new(expressions, self, prop_name)
120
+ end
121
+
122
+ def as(v)
123
+ @var_name = v
124
+ self
125
+ end
126
+
127
+ # generates a <tt>ID</tt> cypher fragment.
128
+ def neo_id
129
+ Property.new(@expressions, self, 'ID').to_function!
130
+ end
131
+
132
+ # generates a <tt>has</tt> cypher fragment.
133
+ def property?(p)
134
+ p = Property.new(expressions, self, p)
135
+ p.binary_operator("has")
136
+ end
137
+
138
+ # generates a <tt>is null</tt> cypher fragment.
139
+ def exist?
140
+ p = Property.new(expressions, self, p)
141
+ p.binary_operator("", " is null")
142
+ end
143
+
144
+ # Can be used instead of [_classname] == klass
145
+ def is_a?(klass)
146
+ return super if klass.class != Class || !klass.respond_to?(:_load_wrapper)
147
+ self[:_classname] == klass.to_s
148
+ end
149
+
150
+ def count
151
+ #expressions.delete(self)
152
+ ExprOp.new(self, nil, 'count')
153
+ end
154
+ end
155
+
156
+ module Matchable
157
+
158
+ def where(&block)
159
+ x = block.call(self)
160
+ expressions.delete(x)
161
+ ExprOp.new(x, nil, "").binary!
162
+ self
163
+ end
164
+
165
+ def where_not(&block)
166
+ x = block.call(self)
167
+ expressions.delete(x)
168
+ ExprOp.new(x, nil, "not").binary!
169
+ self
170
+ end
171
+
172
+
173
+ # This operator means related to, without regard to type or direction.
174
+ # @param [Symbol, #var_name] other either a node (Symbol, #var_name)
175
+ # @return [MatchRelLeft, MatchNode]
176
+ def <=>(other)
177
+ MatchNode.new(self, other, expressions, :both)
178
+ end
179
+
180
+ # This operator means outgoing related to
181
+ # @param [Symbol, #var_name, String] other the relationship
182
+ # @return [MatchRelLeft, MatchNode]
183
+ def >(other)
184
+ MatchRelLeft.new(self, other, expressions, :outgoing)
185
+ end
186
+
187
+ # This operator means any direction related to
188
+ # @param (see #>)
189
+ # @return [MatchRelLeft, MatchNode]
190
+ def -(other)
191
+ MatchRelLeft.new(self, other, expressions, :both)
192
+ end
193
+
194
+ # This operator means incoming related to
195
+ # @param (see #>)
196
+ # @return [MatchRelLeft, MatchNode]
197
+ def <(other)
198
+ MatchRelLeft.new(self, other, expressions, :incoming)
199
+ end
200
+
201
+ # Outgoing relationship to other node
202
+ # @param [Symbol, #var_name] other either a node (Symbol, #var_name)
203
+ # @return [MatchRelLeft, MatchNode]
204
+ def >>(other)
205
+ MatchNode.new(self, other, expressions, :outgoing)
206
+ end
207
+
208
+ def outgoing(rel_type)
209
+ node = NodeVar.new(@expressions, @variables)
210
+ MatchRelLeft.new(self, ":`#{rel_type}`", expressions, :outgoing) > node
211
+ node
212
+ end
213
+
214
+ def incoming(rel_type)
215
+ node = NodeVar.new(@expressions, @variables)
216
+ MatchRelLeft.new(self, ":`#{rel_type}`", expressions, :incoming) < node
217
+ node
218
+ end
219
+
220
+ def both(rel_type)
221
+ node = NodeVar.new(@expressions, @variables)
222
+ MatchRelLeft.new(self, ":`#{rel_type}`", expressions, :both) < node
223
+ node
224
+ end
225
+
226
+ # Incoming relationship to other node
227
+ # @param [Symbol, #var_name] other either a node (Symbol, #var_name)
228
+ # @return [MatchRelLeft, MatchNode]
229
+ def <<(other)
230
+ MatchNode.new(self, other, expressions, :incoming)
231
+ end
232
+ end
233
+
234
+ class Expression
235
+ attr_reader :expressions
236
+ attr_accessor :separator, :clause
237
+
238
+ def initialize(expressions, clause)
239
+ @clause = clause
240
+ @expressions = expressions
241
+ insert_last(clause)
242
+ @separator = ","
243
+ end
244
+
245
+ def insert_last(clause)
246
+ curr_clause = clause
247
+ while (i = @expressions.reverse.index { |e| e.clause == curr_clause }).nil? && curr_clause != :start
248
+ curr_clause = prev_clause(curr_clause)
249
+ end
250
+
251
+ if i.nil?
252
+ @expressions << self
253
+ else
254
+ pos = @expressions.size - i
255
+ @expressions.insert(pos, self)
256
+ end
257
+ end
258
+
259
+ def prev_clause(clause)
260
+ {:limit => :skip, :skip => :order_by, :order_by => :return, :return => :where, :where => :match, :match => :start}[clause]
261
+ end
262
+
263
+ def prefixes
264
+ {:start => "START", :where => " WHERE", :match => " MATCH", :return => " RETURN", :order_by => " ORDER BY", :skip => " SKIP", :limit => " LIMIT"}
265
+ end
266
+
267
+ def prefix
268
+ prefixes[clause]
269
+ end
270
+
271
+ def valid?
272
+ true
273
+ end
274
+
275
+ end
276
+
277
+ # A property is returned from a Variable by using the [] operator.
278
+ #
279
+ # It has a number of useful method like
280
+ # <tt>count</tt>, <tt>sum</tt>, <tt>avg</tt>, <tt>min</tt>, <tt>max</tt>, <tt>collect</tt>, <tt>head</tt>, <tt>last</tt>, <tt>tail</tt>,
281
+ #
282
+ # @example
283
+ # n=node(2, 3, 4); n[:name].collect
284
+ # # same as START n0=node(2,3,4) RETURN collect(n0.property)
285
+ class Property
286
+ # @private
287
+ attr_reader :expressions, :var_name, :var_expr
288
+ include Comparable
289
+ include MathOperator
290
+ include MathFunctions
291
+ include PredicateMethods
292
+
293
+ def initialize(expressions, var_expr, prop_name)
294
+ @var_expr = var_expr
295
+ @var = var_expr.respond_to?(:var_name) ? var_expr.var_name : var_expr
296
+ @expressions = expressions
297
+ @prop_name = prop_name
298
+ @var_name = @prop_name ? "#{@var.to_s}.#{@prop_name}" : @var.to_s
299
+ end
300
+
301
+ # @private
302
+ def to_function!(var = @var.to_s)
303
+ @var_name = "#{@prop_name}(#{var})"
304
+ self
305
+ end
306
+
307
+ # Make it possible to rename a property with a different name (AS)
308
+ def as(new_name)
309
+ @var_name = "#{@var_name} AS #{new_name}"
310
+ end
311
+
312
+ # required by the Predicate Methods Module
313
+ # @see PredicateMethods
314
+ # @private
315
+ def iterable
316
+ var_name
317
+ end
318
+
319
+ def input
320
+ self
321
+ end
322
+
323
+ # @private
324
+ def in?(values)
325
+ binary_operator("", " IN [#{values.map { |x| %Q["#{x}"] }.join(',')}]")
326
+ end
327
+
328
+ # Only return distinct values/nodes/rels/paths
329
+ def distinct
330
+ @var_name = "distinct #{@var_name}"
331
+ self
332
+ end
333
+
334
+ def length
335
+ @prop_name = "length"
336
+ to_function!
337
+ self
338
+ end
339
+
340
+ %w[count sum avg min max collect head last tail].each do |meth_name|
341
+ define_method(meth_name) do
342
+ function(meth_name.to_s)
343
+ end
344
+ end
345
+
346
+ # @private
347
+ def function(func_name_pre, func_name_post = "")
348
+ ExprOp.new(self, nil, func_name_pre, func_name_post)
349
+ end
350
+
351
+ # @private
352
+ def binary_operator(op, post_fix = "")
353
+ ExprOp.new(self, nil, op, post_fix).binary!
354
+ end
355
+ end
356
+
357
+ class Start < Expression
358
+ # @private
359
+ attr_reader :var_name
360
+ include Variable
361
+ include Matchable
362
+
363
+ def initialize(var_name, expressions)
364
+ @var_name = "#{var_name}#{expressions.size}"
365
+ super(expressions, :start)
366
+ end
367
+
368
+ end
369
+
370
+ # Can be created from a <tt>node</tt> dsl method.
371
+ class StartNode < Start
372
+ # @private
373
+ attr_reader :nodes
374
+
375
+ def initialize(nodes, expressions)
376
+ super("n", expressions)
377
+
378
+ @nodes = nodes.map { |n| n.respond_to?(:neo_id) ? n.neo_id : n }
379
+ end
380
+
381
+ def to_s
382
+ "#{var_name}=node(#{nodes.join(',')})"
383
+ end
384
+ end
385
+
386
+
387
+ # Can be created from a <tt>rel</tt> dsl method.
388
+ class StartRel < Start
389
+ # @private
390
+ attr_reader :rels
391
+
392
+ def initialize(rels, expressions)
393
+ super("r", expressions)
394
+ @rels = rels.map { |n| n.respond_to?(:neo_id) ? n.neo_id : n }
395
+ end
396
+
397
+ def to_s
398
+ "#{var_name}=relationship(#{rels.join(',')})"
399
+ end
400
+ end
401
+
402
+ class NodeQuery < Start
403
+ attr_reader :index_name, :query
404
+
405
+ def initialize(index_class, query, index_type, expressions)
406
+ super("n", expressions)
407
+ @index_name = index_class.index_name_for_type(index_type)
408
+ @query = query
409
+ end
410
+
411
+ def to_s
412
+ "#{var_name}=node:#{index_name}(#{query})"
413
+ end
414
+ end
415
+
416
+ class NodeLookup < Start
417
+ attr_reader :index_name, :query
418
+
419
+ def initialize(index_class, key, value, expressions)
420
+ super("n", expressions)
421
+ index_type = index_class.index_type(key.to_s)
422
+ raise "No index on #{index_class} property #{key}" unless index_type
423
+ @index_name = index_class.index_name_for_type(index_type)
424
+ @query = %Q[#{key}="#{value}"]
425
+ end
426
+
427
+ def to_s
428
+ %Q[#{var_name}=node:#{index_name}(#{query})]
429
+ end
430
+
431
+ end
432
+
433
+ # The return statement in the cypher query
434
+ class Return < Expression
435
+ attr_reader :var_name
436
+
437
+ def initialize(name_or_ref, expressions, opts = {})
438
+ super(expressions, :return)
439
+ @name_or_ref = name_or_ref
440
+ @name_or_ref.referenced! if @name_or_ref.respond_to?(:referenced!)
441
+ @var_name = @name_or_ref.respond_to?(:var_name) ? @name_or_ref.var_name : @name_or_ref.to_s
442
+ opts.each_pair { |k, v| self.send(k, v) }
443
+ end
444
+
445
+ # @private
446
+ def return_method
447
+ @name_or_ref.respond_to?(:return_method) && @name_or_ref.return_method
448
+ end
449
+
450
+ # @private
451
+ def as_return_method
452
+ if return_method[:bracket]
453
+ "#{return_method[:name]}(#@var_name)"
454
+ else
455
+ "#{return_method[:name]} #@var_name"
456
+ end
457
+ end
458
+
459
+ # Specifies an <tt>ORDER BY</tt> cypher query
460
+ # @param [Property] props the properties which should be sorted
461
+ # @return self
462
+ def asc(*props)
463
+ @order_by ||= OrderBy.new(expressions)
464
+ expressions.delete(props.first)
465
+ @order_by.asc(props)
466
+ self
467
+ end
468
+
469
+ # Specifies an <tt>ORDER BY</tt> cypher query
470
+ # @param [Property] props the properties which should be sorted
471
+ # @return self
472
+ def desc(*props)
473
+ @order_by ||= OrderBy.new(expressions)
474
+ expressions.delete(props.first)
475
+ @order_by.desc(props)
476
+ self
477
+ end
478
+
479
+ # Creates a <tt>SKIP</tt> cypher clause
480
+ # @param [Fixnum] val the number of entries to skip
481
+ # @return self
482
+ def skip(val)
483
+ Skip.new(expressions, val)
484
+ self
485
+ end
486
+
487
+ # Creates a <tt>LIMIT</tt> cypher clause
488
+ # @param [Fixnum] val the number of entries to limit
489
+ # @return self
490
+ def limit(val)
491
+ Limit.new(expressions, val)
492
+ self
493
+ end
494
+
495
+ def to_s
496
+ return_method ? as_return_method : var_name.to_s
497
+ end
498
+ end
499
+
500
+ # Can be used to skip result from a return clause
501
+ class Skip < Expression
502
+ def initialize(expressions, value)
503
+ super(expressions, :skip)
504
+ @value = value
505
+ end
506
+
507
+ def to_s
508
+ @value
509
+ end
510
+ end
511
+
512
+ # Can be used to limit result from a return clause
513
+ class Limit < Expression
514
+ def initialize(expressions, value)
515
+ super(expressions, :limit)
516
+ @value = value
517
+ end
518
+
519
+ def to_s
520
+ @value
521
+ end
522
+ end
523
+
524
+ class OrderBy < Expression
525
+ def initialize(expressions)
526
+ super(expressions, :order_by)
527
+ @orders = []
528
+ end
529
+
530
+ def asc(props)
531
+ @orders << [:asc, props]
532
+ end
533
+
534
+ def desc(props)
535
+ @orders << [:desc, props]
536
+ end
537
+
538
+ def to_s
539
+ @orders.map do |pair|
540
+ if pair[0] == :asc
541
+ pair[1].map(&:var_name).join(', ')
542
+ else
543
+ pair[1].map(&:var_name).join(', ') + " DESC"
544
+ end
545
+ end.join(', ')
546
+ end
547
+ end
548
+
549
+ # Created from a node's match operator like >> or <.
550
+ class Match < Expression
551
+ # @private
552
+ attr_reader :dir, :expressions, :left, :right, :var_name, :dir_op
553
+ # @private
554
+ attr_accessor :algorithm, :next, :prev
555
+ include Variable
556
+
557
+ def initialize(left, right, expressions, dir, dir_op)
558
+ super(expressions, :match)
559
+ @var_name = "m#{expressions.size}"
560
+ @dir = dir
561
+ @dir_op = dir_op
562
+ @prev = left if left.is_a?(Match)
563
+ @left = left
564
+ @right = right
565
+ end
566
+
567
+
568
+ # Generates a <tt>x in nodes(m3)</tt> cypher expression.
569
+ #
570
+ # @example
571
+ # p.nodes.all? { |x| x[:age] > 30 }
572
+ def nodes
573
+ Entities.new(@expressions, "nodes", self)
574
+ end
575
+
576
+ # Generates a <tt>x in relationships(m3)</tt> cypher expression.
577
+ #
578
+ # @example
579
+ # p.relationships.all? { |x| x[:age] > 30 }
580
+ def rels
581
+ Entities.new(@expressions, "relationships", self)
582
+ end
583
+
584
+ # returns the length of the path
585
+ def length
586
+ self.return_method = {:name => 'length', :bracket => true}
587
+ self
588
+ end
589
+
590
+ # @private
591
+ def find_match_start
592
+ c = self
593
+ while (c.prev) do
594
+ c = c.prev
595
+ end
596
+ c
597
+ end
598
+
599
+ # @private
600
+ def left_var_name
601
+ @left.respond_to?(:var_name) ? @left.var_name : @left.to_s
602
+ end
603
+
604
+ # @private
605
+ def right_var_name
606
+ @right.respond_to?(:var_name) ? @right.var_name : @right.to_s
607
+ end
608
+
609
+ # @private
610
+ def right_expr
611
+ c = @right
612
+ r = while (c)
613
+ break c.var_expr if c.respond_to?(:var_expr)
614
+ c = c.respond_to?(:left_expr) && c.left_expr
615
+ end || @right
616
+
617
+ r.respond_to?(:expr) ? r.expr : right_var_name
618
+ end
619
+
620
+ # @private
621
+ def referenced!
622
+ @referenced = true
623
+ end
624
+
625
+ # @private
626
+ def referenced?
627
+ !!@referenced
628
+ end
629
+
630
+ # @private
631
+ def to_s
632
+ curr = find_match_start
633
+ result = (referenced? || curr.referenced?) ? "#{var_name} = " : ""
634
+ result << (algorithm ? "#{algorithm}(" : "")
635
+ begin
636
+ result << curr.expr
637
+ end while (curr = curr.next)
638
+ result << ")" if algorithm
639
+ result
640
+ end
641
+ end
642
+
643
+ # The left part of a match clause, e.g. node < rel(':friends')
644
+ # Can return {MatchRelRight} using a match operator method.
645
+ class MatchRelLeft < Match
646
+ def initialize(left, right, expressions, dir)
647
+ super(left, right, expressions, dir, dir == :incoming ? '<-' : '-')
648
+ end
649
+
650
+ # @param [Symbol,NodeVar,String] other part of the match cypher statement.
651
+ # @return [MatchRelRight] the right part of an relationship cypher query.
652
+ def >(other)
653
+ expressions.delete(self)
654
+ self.next = MatchRelRight.new(self, other, expressions, :outgoing)
655
+ end
656
+
657
+ # @see #>
658
+ # @return (see #>)
659
+ def <(other)
660
+ expressions.delete(self)
661
+ self.next = MatchRelRight.new(self, other, expressions, :incoming)
662
+ end
663
+
664
+ # @see #>
665
+ # @return (see #>)
666
+ def -(other)
667
+ expressions.delete(self)
668
+ self.next = MatchRelRight.new(self, other, expressions, :both)
669
+ end
670
+
671
+ # @return [String] a cypher string for this match.
672
+ def expr
673
+ if prev
674
+ # we have chained more then one relationships in a match expression
675
+ "#{dir_op}[#{right_expr}]"
676
+ else
677
+ # the right is an relationship and could be an expressions, e.g "r?"
678
+ "(#{left_var_name})#{dir_op}[#{right_expr}]"
679
+ end
680
+ end
681
+ end
682
+
683
+ class MatchRelRight < Match
684
+ # @param left the left part of the query
685
+ # @param [Symbol,NodeVar,String] right part of the match cypher statement.
686
+ def initialize(left, right, expressions, dir)
687
+ super(left, right, expressions, dir, dir == :outgoing ? '->' : '-')
688
+ end
689
+
690
+ # @param [Symbol,NodeVar,String] other part of the match cypher statement.
691
+ # @return [MatchRelLeft] the right part of an relationship cypher query.
692
+ def >(other)
693
+ expressions.delete(self)
694
+ self.next = MatchRelLeft.new(self, other, expressions, :outgoing)
695
+ end
696
+
697
+ # @see #>
698
+ # @return (see #>)
699
+ def <(other)
700
+ expressions.delete(self)
701
+ self.next = MatchRelLeft.new(self, other, expressions, :incoming)
702
+ end
703
+
704
+ # @see #>
705
+ # @return (see #>)
706
+ def -(other)
707
+ expressions.delete(self)
708
+ self.next = MatchRelLeft.new(self, other, expressions, :both)
709
+ end
710
+
711
+ def <<(other)
712
+ expressions.delete(self)
713
+ self.next = MatchNode.new(self, other, expressions, :incoming)
714
+ end
715
+
716
+ def >>(other)
717
+ expressions.delete(self)
718
+ self.next = MatchNode.new(self, other, expressions, :outgoing)
719
+ end
720
+
721
+ # @return [String] a cypher string for this match.
722
+ def expr
723
+ "#{dir_op}(#{right_expr})"
724
+ end
725
+
726
+ # negate this match
727
+ def not
728
+ expressions.delete(self)
729
+ ExprOp.new(left, nil, "not").binary!
730
+ end
731
+
732
+ if RUBY_VERSION > "1.9.0"
733
+ eval %{
734
+ def !
735
+ expressions.delete(self)
736
+ ExprOp.new(left, nil, "not").binary!
737
+ end
738
+ }
739
+ end
740
+
741
+ end
742
+
743
+ # The right part of a match clause (node_b), e.g. node_a > rel(':friends') > node_b
744
+ #
745
+ class MatchNode < Match
746
+ attr_reader :dir_op
747
+
748
+ def initialize(left, right, expressions, dir)
749
+ dir_op = case dir
750
+ when :outgoing then
751
+ "-->"
752
+ when :incoming then
753
+ "<--"
754
+ when :both then
755
+ "--"
756
+ end
757
+ super(left, right, expressions, dir, dir_op)
758
+ end
759
+
760
+ # @return [String] a cypher string for this match.
761
+ def expr
762
+ if prev
763
+ # we have chained more then one relationships in a match expression
764
+ "#{dir_op}(#{right_expr})"
765
+ else
766
+ # the right is an relationship and could be an expressions, e.g "r?"
767
+ "(#{left_var_name})#{dir_op}(#{right_expr})"
768
+ end
769
+ end
770
+
771
+ def <<(other)
772
+ expressions.delete(self)
773
+ self.next = MatchNode.new(self, other, expressions, :incoming)
774
+ end
775
+
776
+ def >>(other)
777
+ expressions.delete(self)
778
+ self.next = MatchNode.new(self, other, expressions, :outgoing)
779
+ end
780
+
781
+ # @param [Symbol,NodeVar,String] other part of the match cypher statement.
782
+ # @return [MatchRelRight] the right part of an relationship cypher query.
783
+ def >(other)
784
+ expressions.delete(self)
785
+ self.next = MatchRelLeft.new(self, other, expressions, :outgoing)
786
+ end
787
+
788
+ # @see #>
789
+ # @return (see #>)
790
+ def <(other)
791
+ expressions.delete(self)
792
+ self.next = MatchRelLeft.new(self, other, expressions, :incoming)
793
+ end
794
+
795
+ # @see #>
796
+ # @return (see #>)
797
+ def -(other)
798
+ expressions.delete(self)
799
+ self.next = MatchRelLeft.new(self, other, expressions, :both)
800
+ end
801
+
802
+ end
803
+
804
+ # Represents an unbound node variable used in match statements
805
+ class NodeVar
806
+ include Variable
807
+ include Matchable
808
+
809
+ # @return the name of the variable
810
+ attr_reader :var_name
811
+ attr_reader :expressions
812
+
813
+ def initialize(expressions, variables)
814
+ variables ||= []
815
+ @var_name = "v#{variables.size}"
816
+ variables << self
817
+ @variables = variables
818
+ @expressions = expressions
819
+ end
820
+
821
+ # @return [String] a cypher string for this node variable
822
+ def to_s
823
+ var_name
824
+ end
825
+
826
+ # @private
827
+ def expr
828
+ to_s
829
+ end
830
+
831
+ end
832
+
833
+ # represent an unbound relationship variable used in match,where,return statement
834
+ class RelVar
835
+ include Variable
836
+
837
+ attr_reader :var_name, :expr, :expressions
838
+
839
+ def initialize(expressions, variables, expr)
840
+ variables << self
841
+ @expr = expr
842
+ @expressions = expressions
843
+ guess = expr ? /([[:alpha:]_]*)/.match(expr)[1] : ""
844
+ @auto_var_name = "v#{variables.size}"
845
+ @var_name = guess.empty? ? @auto_var_name : guess
846
+ end
847
+
848
+ def rel_type
849
+ Property.new(@expressions, self, 'type').to_function!
850
+ end
851
+
852
+ def [](p)
853
+ if @expr.to_s[0..0] == ':'
854
+ @var_name = @auto_var_name
855
+ @expr = "#{@var_name}#{@expr}"
856
+ end
857
+ super
858
+ end
859
+
860
+ # @return [String] a cypher string for this relationship variable
861
+ def to_s
862
+ var_name
863
+ end
864
+
865
+ end
866
+
867
+ class ExprOp < Expression
868
+ attr_reader :left, :right, :op, :neg, :post_fix, :left_expr, :right_expr
869
+ include MathFunctions
870
+
871
+
872
+ def initialize(left_expr, right_expr, op, post_fix = "")
873
+ super(left_expr.expressions, :where)
874
+ @left_expr = left_expr
875
+ @right_expr = right_expr
876
+ @op = op
877
+ @post_fix = post_fix
878
+ self.expressions.delete(left_expr)
879
+ self.expressions.delete(right_expr)
880
+ @left = quote(left_expr)
881
+ if regexp?(right_expr)
882
+ @op = "=~"
883
+ @right = to_regexp(right_expr)
884
+ else
885
+ @right = right_expr && quote(right_expr)
886
+ end
887
+ @neg = nil
888
+ end
889
+
890
+ def separator
891
+ " and "
892
+ end
893
+
894
+ def quote(val)
895
+ if val.respond_to?(:var_name) && !val.kind_of?(Match)
896
+ val.var_name
897
+ else
898
+ val.is_a?(String) ? %Q["#{val}"] : val
899
+ end
900
+ end
901
+
902
+ def regexp?(right)
903
+ @op == "=~" || right.is_a?(Regexp)
904
+ end
905
+
906
+ def to_regexp(val)
907
+ %Q[/#{val.respond_to?(:source) ? val.source : val.to_s}/]
908
+ end
909
+
910
+ def count
911
+ ExprOp.new(self, nil, 'count')
912
+ end
913
+
914
+ def &(other)
915
+ ExprOp.new(self, other, "and")
916
+ end
917
+
918
+ def |(other)
919
+ ExprOp.new(self, other, "or")
920
+ end
921
+
922
+ def -@
923
+ @neg = "not"
924
+ self
925
+ end
926
+
927
+ def not
928
+ @neg = "not"
929
+ self
930
+ end
931
+
932
+ # Only in 1.9
933
+ if RUBY_VERSION > "1.9.0"
934
+ eval %{
935
+ def !
936
+ @neg = "not"
937
+ self
938
+ end
939
+ }
940
+ end
941
+
942
+ def left_to_s
943
+ left.is_a?(ExprOp) ? "(#{left})" : left
944
+ end
945
+
946
+ def right_to_s
947
+ right.is_a?(ExprOp) ? "(#{right})" : right
948
+ end
949
+
950
+ def binary!
951
+ @binary = true
952
+ self
953
+ end
954
+
955
+ def valid?
956
+ # it is only valid in a where clause if it's either binary or it has right and left values
957
+ @binary ? @left : @left && @right
958
+ end
959
+
960
+ def to_s
961
+ if @right
962
+ neg ? "#{neg}(#{left_to_s} #{op} #{right_to_s})" : "#{left_to_s} #{op} #{right_to_s}"
963
+ else
964
+ # binary operator
965
+ neg ? "#{neg}#{op}(#{left_to_s}#{post_fix})" : "#{op}(#{left_to_s}#{post_fix})"
966
+ end
967
+ end
968
+ end
969
+
970
+ class Where < Expression
971
+ def initialize(expressions, where_statement = nil)
972
+ super(expressions, :where)
973
+ @where_statement = where_statement
974
+ end
975
+
976
+ def to_s
977
+ @where_statement.to_s
978
+ end
979
+ end
980
+
981
+ class Predicate < Expression
982
+ attr_accessor :params
983
+
984
+ def initialize(expressions, params)
985
+ @params = params
986
+ @identifier = :x
987
+ params[:input].referenced! if params[:input].respond_to?(:referenced!)
988
+ super(expressions, params[:clause])
989
+ end
990
+
991
+ def identifier(i)
992
+ @identifier = i
993
+ self
994
+ end
995
+
996
+ def to_s
997
+ input = params[:input]
998
+ if input.kind_of?(Property)
999
+ yield_param = Property.new([], @identifier, nil)
1000
+ args = ""
1001
+ else
1002
+ yield_param = NodeVar.new([], []).as(@identifier.to_sym)
1003
+ args = "(#{input.var_name})"
1004
+ end
1005
+ context = Neo4j::Cypher.new(yield_param, &params[:predicate_block])
1006
+ context.expressions.each { |e| e.clause = nil }
1007
+ if params[:clause] == :return
1008
+ where_or_colon = ':'
1009
+ else
1010
+ where_or_colon = 'WHERE'
1011
+ end
1012
+ predicate_value = context.to_s[1..-1] # skip separator ,
1013
+ "#{params[:op]}(#@identifier in #{params[:iterable]}#{args} #{where_or_colon} #{predicate_value})"
1014
+ end
1015
+ end
1016
+
1017
+ class Entities
1018
+ include PredicateMethods
1019
+ attr_reader :input, :expressions, :iterable
1020
+
1021
+ def initialize(expressions, iterable, input)
1022
+ @iterable = iterable
1023
+ @input = input
1024
+ @expressions = expressions
1025
+ end
1026
+
1027
+ end
1028
+
1029
+ end
1030
+
1031
+ end
1032
+
1033
+ end