neo4j-core 0.0.1-java

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