neo4j-cypher 1.0.0.rc1

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.
@@ -0,0 +1,99 @@
1
+ module Neo4j
2
+ module Cypher
3
+
4
+ class Delete
5
+ include Clause
6
+
7
+ def initialize(clause_list, var)
8
+ super(clause_list, :delete)
9
+ @var = var
10
+ end
11
+
12
+ def to_cypher
13
+ @var.var_name.to_s
14
+ end
15
+
16
+ end
17
+
18
+
19
+ class Create
20
+ include ToPropString
21
+ include Clause
22
+ include Referenceable
23
+
24
+ def initialize(clause_list, props)
25
+ super(clause_list, :create, EvalContext)
26
+ @props = props
27
+ end
28
+
29
+ def as_create_path?
30
+ !!@as_create_path
31
+ end
32
+
33
+ def as_create_path!
34
+ @as_create_path = true # this is because create path has a little different syntax (extra parantheses)
35
+ end
36
+
37
+ def match_value
38
+ to_cypher
39
+ end
40
+
41
+ def to_cypher
42
+ without_parantheses = if @props
43
+ "#{var_name} #{to_prop_string(@props)}"
44
+ else
45
+ var_name
46
+ end
47
+
48
+ as_create_path? ? without_parantheses : "(#{without_parantheses})"
49
+ end
50
+
51
+ class EvalContext
52
+ include Context
53
+ include Alias
54
+ include Variable
55
+ include Matchable
56
+ end
57
+
58
+ end
59
+
60
+ class CreatePath
61
+ include Clause
62
+ include Referenceable
63
+
64
+ attr_reader :arg_list
65
+
66
+ def initialize(clause_list, *args, &cypher_dsl)
67
+ super(clause_list, args.empty? ? :create : :with, EvalContext)
68
+
69
+ clause_list.push
70
+
71
+ @args = create_clause_args_for(args)
72
+ @arg_list = @args.map { |a| a.return_value }.join(',')
73
+ arg_exec = @args.map(&:eval_context)
74
+
75
+ RootClause::EvalContext.new(self).instance_exec(*arg_exec, &cypher_dsl)
76
+
77
+ @body = "#{clause_list.to_cypher}"
78
+ clause_list.pop
79
+ end
80
+
81
+ def unique!
82
+ @unique = true
83
+ self
84
+ end
85
+
86
+ def to_cypher
87
+ clause_type == :create ? "#{var_name} = #{@body}" : "#{@arg_list} CREATE #{@unique && "UNIQUE "}#{@body}"
88
+ end
89
+
90
+ class EvalContext
91
+ include Context
92
+ include Variable
93
+ include Alias
94
+ end
95
+
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,343 @@
1
+ module Neo4j
2
+ module Cypher
3
+
4
+ class MatchStart
5
+ include Clause
6
+ include Referenceable
7
+
8
+ attr_reader :match_list
9
+ attr_accessor :algorithm
10
+
11
+ def initialize(from)
12
+ super(from.clause_list, :match)
13
+ @from = from
14
+ @match_list = []
15
+ end
16
+
17
+ def new_match_node(from, to, dir)
18
+ NodeMatchContext.new_first(self, from, to, dir)
19
+ self
20
+ end
21
+
22
+ def new_match_rel(rel)
23
+ RelLeftMatchContext.new(self, @from).set_rel(rel)
24
+ self
25
+ end
26
+
27
+ def new_match_rels(rels)
28
+ RelLeftMatchContext.new(self, @from).set_rels(rels)
29
+ self
30
+ end
31
+
32
+ def new_match_rels?(rels)
33
+ RelLeftMatchContext.new(self, @from).set_rels?(rels)
34
+ self
35
+ end
36
+
37
+
38
+ def eval_context
39
+ @match_list.last
40
+ end
41
+
42
+
43
+ #negate this match
44
+ def not
45
+ clause_list.delete(self)
46
+ Operator.new(clause_list, self, nil, "not").unary!
47
+ end
48
+
49
+ def to_cypher
50
+ match_string = @match_list.map(&:to_cypher).join
51
+ match_string = algorithm ? "#{algorithm}(#{match_string})" : match_string
52
+ referenced? ? "#{var_name} = #{match_string}" : match_string
53
+ end
54
+
55
+ def self.new_match_node(from, to, dir)
56
+ MatchStart.new(from).new_match_node(from, to, dir)
57
+ end
58
+
59
+ module MatchContext
60
+
61
+
62
+ ## Only in 1.9
63
+ if RUBY_VERSION > "1.9.0"
64
+ eval %{
65
+ def !
66
+ Operator.new(clause_list, clause, nil, "not").unary!
67
+ self
68
+ end }
69
+ end
70
+
71
+ def initialize(match_start)
72
+ super(match_start)
73
+ @match_start = match_start
74
+ @match_start.match_list << self
75
+ end
76
+
77
+ def convert_create_clauses(to_or_from)
78
+ # perform a create operation in a match clause ?
79
+ c = to_or_from.respond_to?(:clause) ? to_or_from.clause : to_or_from
80
+ if c.respond_to?(:clause_type) && c.clause_type == :create
81
+ clause_list.delete(c)
82
+ c.as_create_path!
83
+ end
84
+ end
85
+
86
+ def clause
87
+ @match_start
88
+ end
89
+
90
+ def join_previous!
91
+ @join_previous = true
92
+ self
93
+ end
94
+
95
+ def join_previous?
96
+ @join_previous
97
+ end
98
+
99
+ def to_cypher
100
+ if join_previous?
101
+ to_cypher_join
102
+ else
103
+ to_cypher_no_join
104
+ end
105
+ end
106
+
107
+ # Generates a <tt>x in nodes(m3)</tt> cypher expression.
108
+ #
109
+ # @example
110
+ # p.nodes.all? { |x| x[:age] > 30 }
111
+ def nodes
112
+ Entities.new(clause.clause_list, "nodes", self).eval_context
113
+ end
114
+
115
+ # Generates a <tt>x in relationships(m3)</tt> cypher expression.
116
+ #
117
+ # @example
118
+ # p.relationships.all? { |x| x[:age] > 30 }
119
+ def rels
120
+ Entities.new(clause.clause_list, "relationships", self).eval_context
121
+ end
122
+
123
+ # returns the length of the path
124
+ def length
125
+ clause.referenced!
126
+ Property.new(clause, 'length').to_function!
127
+ end
128
+
129
+ def not
130
+ clause.not
131
+ end
132
+ end
133
+
134
+ module JoinableMatchContext
135
+
136
+ def next_new_node(to, dir)
137
+ to_var = NodeVar.as_var(@match_start.clause_list, to)
138
+ NodeMatchContext.new(@match_start, self, to_var, dir).join_previous!
139
+ end
140
+
141
+ def next_new_rel(rel)
142
+ RelLeftMatchContext.new(@match_start, self).set_rel(rel).join_previous!
143
+ end
144
+
145
+ def <=>(other)
146
+ next_new_node(other, :both)
147
+ end
148
+
149
+ def >>(other)
150
+ next_new_node(other, :outgoing)
151
+ end
152
+
153
+ def <<(other)
154
+ next_new_node(other, :incoming)
155
+ end
156
+
157
+ def <(rel)
158
+ next_new_rel(rel)
159
+ end
160
+
161
+ def >(rel)
162
+ next_new_rel(rel)
163
+ end
164
+
165
+ def -(rel)
166
+ next_new_rel(rel)
167
+ end
168
+
169
+ end
170
+
171
+ module Algorithms
172
+
173
+ def shortest_path
174
+ @match_start.algorithm = "shortestPath"
175
+ @match_start.eval_context
176
+ end
177
+
178
+ def shortest_paths
179
+ @match_start.algorithm = "allShortestPaths"
180
+ @match_start.eval_context
181
+ end
182
+ end
183
+
184
+ class RelLeftMatchContext
185
+ include Context
186
+ include MatchContext
187
+
188
+ def initialize(match_start, from)
189
+ super(match_start)
190
+ @from = from
191
+ convert_create_clauses(from)
192
+ end
193
+
194
+ def set_rels(rels)
195
+ if rels.size == 1
196
+ set_rel(rels.first)
197
+ else
198
+ # wrap and maybe join several relationship strings
199
+ @rel_var = RelVar.join(clause_list, rels)
200
+ end
201
+ self
202
+ end
203
+
204
+ def set_rels?(rels)
205
+ set_rels(rels)
206
+ @rel_var.optionally!
207
+ self
208
+ end
209
+
210
+ def set_rel(rel)
211
+ return set_rels(rel) if rel.is_a?(Array)
212
+
213
+ if rel.is_a?(Neo4j::Cypher::RelVar::EvalContext)
214
+ @rel_var = rel.clause
215
+ elsif rel.respond_to?(:clause) && rel.clause.match_value
216
+ @rel_var = rel.clause
217
+ else
218
+ @rel_var = RelVar.new(clause_list, rel)
219
+ end
220
+ self
221
+ end
222
+
223
+ def self.new_first(match_start, from, rel)
224
+ from_var = NodeVar.as_var(match_start.clause_list, from)
225
+ RelLeftMatchContext.new(match_start, from_var).set_rel(rel)
226
+ end
227
+
228
+ def -(to)
229
+ @match_start.match_list.delete(self) # since it is complete now
230
+ RelRightMatchContext.new(@match_start, self, @rel_var, to, :both)
231
+ end
232
+
233
+ def >(to)
234
+ @match_start.match_list.delete(self)
235
+ RelRightMatchContext.new(@match_start, self, @rel_var, to, :outgoing)
236
+ end
237
+
238
+ def <(to)
239
+ @match_start.match_list.delete(self)
240
+ RelRightMatchContext.new(@match_start, self, @rel_var, to, :incoming)
241
+ end
242
+
243
+ def match_value
244
+ @from.match_value
245
+ end
246
+ end
247
+
248
+ class RelRightMatchContext
249
+ include Context
250
+ include Variable
251
+ include Returnable
252
+ include MatchContext
253
+ include JoinableMatchContext
254
+ include Algorithms
255
+ include PredicateMethods
256
+ include Alias
257
+
258
+ FIRST_DIR_OP = {:outgoing => "-", :incoming => "<-", :both => '-'}
259
+ SECOND_DIR_OP = {:outgoing => "->", :incoming => "-", :both => '-'}
260
+
261
+ def initialize(match_start, from, rel_var, to, dir)
262
+ super(match_start)
263
+ @from = from
264
+ convert_create_clauses(from)
265
+ convert_create_clauses(to)
266
+ join_previous! if @from.kind_of?(MatchContext) && @from.join_previous?
267
+ @rel_var = rel_var
268
+ @dir = dir
269
+ convert_create_clauses(to)
270
+ @to = NodeVar.as_var(match_start.clause_list, to)
271
+ end
272
+
273
+ def to_cypher_no_join
274
+ "(#{@from.match_value})#{FIRST_DIR_OP[@dir]}[#{@rel_var.match_value}]#{SECOND_DIR_OP[@dir]}(#{@to.match_value})"
275
+ end
276
+
277
+ def to_cypher_join
278
+ "#{FIRST_DIR_OP[@dir]}[#{@rel_var.match_value}]#{SECOND_DIR_OP[@dir]}(#{@to.match_value})"
279
+ end
280
+
281
+ end
282
+
283
+
284
+ class NodeMatchContext
285
+ include Context
286
+ include Variable
287
+ include Returnable
288
+ include MatchContext
289
+ include JoinableMatchContext
290
+ include Alias
291
+
292
+ DIR_OPERATORS = {:outgoing => "-->", :incoming => "<--", :both => '--'}
293
+
294
+ def initialize(match_start, from, to, dir)
295
+ super(match_start)
296
+ @from = from
297
+ @to = to
298
+ convert_create_clauses(from)
299
+ convert_create_clauses(to)
300
+ @dir = dir
301
+ end
302
+
303
+
304
+ def self.new_first(match_start, from, to, dir)
305
+ from_var = NodeVar.as_var(match_start.clause_list, from)
306
+ to_var = NodeVar.as_var(match_start.clause_list, to)
307
+ NodeMatchContext.new(match_start, from_var, to_var, dir)
308
+ end
309
+
310
+ def to_cypher_no_join
311
+ "(#{@from.var_name})#{DIR_OPERATORS[@dir]}(#{@to.var_name})"
312
+ end
313
+
314
+ def to_cypher_join
315
+ "#{DIR_OPERATORS[@dir]}(#{@to.var_name})"
316
+ end
317
+ end
318
+
319
+ class Entities
320
+ include Clause
321
+
322
+ def initialize(clause_list, iterable, input)
323
+ super(clause_list, :entities, EvalContext)
324
+ eval_context.iterable = iterable
325
+ eval_context.input = input.clause
326
+ end
327
+
328
+ class EvalContext
329
+ include Context
330
+ include PredicateMethods
331
+ attr_accessor :input, :iterable
332
+ #
333
+ #def each(&cypher_dsl)
334
+ # Predicate.new(clause_list, :clause => clause, :input => input, :predicate_block => cypher_dsl)
335
+ # #RootClause::EvalContext.new(self).instance_exec(*arg_exec, &cypher_dsl)
336
+ #end
337
+ end
338
+ end
339
+
340
+ end
341
+
342
+ end
343
+ end
@@ -0,0 +1,43 @@
1
+ module Neo4j
2
+ module Cypher
3
+ module Referenceable
4
+ def var_name
5
+ @var_name ||= @clause_list.create_variable(self)
6
+ end
7
+
8
+ def var_name=(new_name)
9
+ @var_name = new_name.to_sym
10
+ end
11
+
12
+ def referenced?
13
+ !!@referenced
14
+ end
15
+
16
+ def referenced!
17
+ @referenced = true
18
+ end
19
+
20
+ def as_alias(new_name)
21
+ @alias = true
22
+ self.var_name = new_name
23
+ end
24
+
25
+ def as_alias?
26
+ !!@alias && var_name != return_value
27
+ end
28
+
29
+ end
30
+
31
+ module ToPropString
32
+ def to_prop_string(props)
33
+ key_values = props.keys.map do |key|
34
+ raw = key.to_s[0, 1] == '_'
35
+ val = props[key].is_a?(String) && !raw ? "'#{props[key]}'" : props[key]
36
+ "#{raw ? key.to_s[1..-1] : key} : #{val}"
37
+ end
38
+ "{#{key_values.join(', ')}}"
39
+ end
40
+ end
41
+
42
+ end
43
+ end