neo4j-cypher 1.0.0.rc1

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