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

Sign up to get free protection for your applications and to get access to all the features.
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