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.
- data/Gemfile +2 -2
- data/README.rdoc +161 -13
- data/config/neo4j/config.yml +1 -1
- data/lib/db/active_tx_log +1 -0
- data/lib/db/index/lucene-store.db +0 -0
- data/lib/db/index/lucene.log.active +0 -0
- data/lib/db/messages.log +2299 -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.active +0 -0
- data/lib/db/tm_tx_log.1 +0 -0
- data/lib/neo4j-core.rb +20 -3
- data/lib/neo4j-core/cypher/cypher.rb +1033 -0
- data/lib/neo4j-core/cypher/result_wrapper.rb +48 -0
- data/lib/neo4j-core/database.rb +4 -5
- data/lib/neo4j-core/event_handler.rb +1 -1
- data/lib/neo4j-core/hash_with_indifferent_access.rb +165 -0
- data/lib/neo4j-core/index/class_methods.rb +27 -41
- data/lib/neo4j-core/index/index.rb +3 -4
- data/lib/neo4j-core/index/index_config.rb +30 -23
- data/lib/neo4j-core/index/indexer.rb +65 -53
- data/lib/neo4j-core/index/indexer_registry.rb +2 -2
- data/lib/neo4j-core/index/lucene_query.rb +53 -42
- data/lib/neo4j-core/index/unique_factory.rb +54 -0
- data/lib/neo4j-core/node/class_methods.rb +4 -4
- data/lib/neo4j-core/node/node.rb +1 -8
- data/lib/neo4j-core/property/java.rb +59 -0
- data/lib/neo4j-core/property/property.rb +1 -3
- data/lib/neo4j-core/relationship/relationship.rb +8 -10
- data/lib/neo4j-core/rels/rels.rb +9 -4
- data/lib/neo4j-core/rels/traverser.rb +13 -27
- data/lib/neo4j-core/traversal/prune_evaluator.rb +2 -2
- data/lib/neo4j-core/traversal/traverser.rb +122 -27
- data/lib/neo4j-core/version.rb +1 -1
- data/lib/neo4j-core/wrapper/class_methods.rb +22 -0
- data/lib/neo4j-core/wrapper/wrapper.rb +20 -0
- data/lib/neo4j/algo.rb +300 -0
- data/lib/neo4j/config.rb +3 -6
- data/lib/neo4j/cypher.rb +180 -0
- data/lib/neo4j/neo4j.rb +51 -23
- data/lib/neo4j/node.rb +27 -0
- data/lib/neo4j/relationship.rb +25 -0
- data/lib/test.rb~ +27 -0
- data/neo4j-core.gemspec +2 -2
- metadata +44 -11
- data/lib/neo4j-core/type_converters/type_converters.rb +0 -287
- data/lib/neo4j-core/version.rb~ +0 -3
- data/lib/test.rb +0 -27
data/lib/db/neostore
ADDED
Binary file
|
data/lib/db/neostore.id
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/lib/db/tm_tx_log.1
ADDED
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, ¶ms[: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
|