neo4j-core 0.0.1-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.
- data/Gemfile +27 -0
- data/README.rdoc +27 -0
- data/config/neo4j/config.yml +102 -0
- data/lib/db/active_tx_log +1 -0
- data/lib/db/index/lucene-store.db +0 -0
- data/lib/db/index/lucene.log.1 +0 -0
- data/lib/db/index/lucene.log.active +0 -0
- data/lib/db/lock +0 -0
- data/lib/db/messages.log +530 -0
- data/lib/db/neostore +0 -0
- data/lib/db/neostore.id +0 -0
- data/lib/db/neostore.nodestore.db +0 -0
- data/lib/db/neostore.nodestore.db.id +0 -0
- data/lib/db/neostore.propertystore.db +0 -0
- data/lib/db/neostore.propertystore.db.arrays +0 -0
- data/lib/db/neostore.propertystore.db.arrays.id +0 -0
- data/lib/db/neostore.propertystore.db.id +0 -0
- data/lib/db/neostore.propertystore.db.index +0 -0
- data/lib/db/neostore.propertystore.db.index.id +0 -0
- data/lib/db/neostore.propertystore.db.index.keys +0 -0
- data/lib/db/neostore.propertystore.db.index.keys.id +0 -0
- data/lib/db/neostore.propertystore.db.strings +0 -0
- data/lib/db/neostore.propertystore.db.strings.id +0 -0
- data/lib/db/neostore.relationshipstore.db +0 -0
- data/lib/db/neostore.relationshipstore.db.id +0 -0
- data/lib/db/neostore.relationshiptypestore.db +0 -0
- data/lib/db/neostore.relationshiptypestore.db.id +0 -0
- data/lib/db/neostore.relationshiptypestore.db.names +0 -0
- data/lib/db/neostore.relationshiptypestore.db.names.id +0 -0
- data/lib/db/nioneo_logical.log.2 +0 -0
- data/lib/db/nioneo_logical.log.active +0 -0
- data/lib/db/tm_tx_log.1 +0 -0
- data/lib/neo4j/config.rb +139 -0
- data/lib/neo4j/cypher.rb +156 -0
- data/lib/neo4j/neo4j.rb +244 -0
- data/lib/neo4j/neo4j.rb~ +214 -0
- data/lib/neo4j/node.rb +39 -0
- data/lib/neo4j/relationship.rb +61 -0
- data/lib/neo4j/transaction.rb +86 -0
- data/lib/neo4j/type_converters/type_converters.rb +287 -0
- data/lib/neo4j-core/cypher/cypher.rb +867 -0
- data/lib/neo4j-core/cypher/result_wrapper.rb +39 -0
- data/lib/neo4j-core/database.rb +191 -0
- data/lib/neo4j-core/equal/equal.rb +23 -0
- data/lib/neo4j-core/event_handler.rb +265 -0
- data/lib/neo4j-core/index/class_methods.rb +117 -0
- data/lib/neo4j-core/index/index.rb +36 -0
- data/lib/neo4j-core/index/index_config.rb +112 -0
- data/lib/neo4j-core/index/indexer.rb +243 -0
- data/lib/neo4j-core/index/indexer_registry.rb +55 -0
- data/lib/neo4j-core/index/lucene_query.rb +264 -0
- data/lib/neo4j-core/lazy_map.rb +21 -0
- data/lib/neo4j-core/node/class_methods.rb +77 -0
- data/lib/neo4j-core/node/node.rb +47 -0
- data/lib/neo4j-core/property/property.rb +94 -0
- data/lib/neo4j-core/relationship/class_methods.rb +80 -0
- data/lib/neo4j-core/relationship/relationship.rb +97 -0
- data/lib/neo4j-core/relationship_set.rb +61 -0
- data/lib/neo4j-core/rels/rels.rb +147 -0
- data/lib/neo4j-core/rels/traverser.rb +99 -0
- data/lib/neo4j-core/to_java.rb +51 -0
- data/lib/neo4j-core/traversal/evaluator.rb +36 -0
- data/lib/neo4j-core/traversal/filter_predicate.rb +30 -0
- data/lib/neo4j-core/traversal/prune_evaluator.rb +20 -0
- data/lib/neo4j-core/traversal/rel_expander.rb +35 -0
- data/lib/neo4j-core/traversal/traversal.rb +130 -0
- data/lib/neo4j-core/traversal/traverser.rb +295 -0
- data/lib/neo4j-core/version.rb +5 -0
- data/lib/neo4j-core.rb +64 -0
- data/neo4j-core.gemspec +31 -0
- 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, ¶ms[: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
|