neo4j-cypher 1.0.0.rc2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/README.rdoc +28 -77
- data/lib/neo4j-cypher.rb +3 -1
- data/lib/neo4j-cypher/abstract_filter.rb +78 -0
- data/lib/neo4j-cypher/argument.rb +0 -1
- data/lib/neo4j-cypher/clause.rb +39 -1
- data/lib/neo4j-cypher/clause_list.rb +30 -15
- data/lib/neo4j-cypher/collection.rb +14 -0
- data/lib/neo4j-cypher/context.rb +27 -27
- data/lib/neo4j-cypher/create.rb +0 -3
- data/lib/neo4j-cypher/foreach.rb +16 -0
- data/lib/neo4j-cypher/match.rb +21 -18
- data/lib/neo4j-cypher/neography.rb +22 -0
- data/lib/neo4j-cypher/node_var.rb +0 -1
- data/lib/neo4j-cypher/operator.rb +4 -10
- data/lib/neo4j-cypher/predicate.rb +5 -56
- data/lib/neo4j-cypher/property.rb +4 -12
- data/lib/neo4j-cypher/rel_var.rb +25 -31
- data/lib/neo4j-cypher/return.rb +0 -1
- data/lib/neo4j-cypher/root.rb +44 -22
- data/lib/neo4j-cypher/start.rb +36 -40
- data/lib/neo4j-cypher/version.rb +2 -2
- data/lib/neo4j-cypher/where.rb +16 -3
- data/lib/neo4j-cypher/with.rb +1 -3
- data/lib/tasks/analyzer.rake +54 -0
- metadata +13 -6
- data/lib/neo4j-cypher/mixins.rb +0 -47
data/lib/neo4j-cypher/create.rb
CHANGED
@@ -17,9 +17,7 @@ module Neo4j
|
|
17
17
|
|
18
18
|
|
19
19
|
class Create
|
20
|
-
include ToPropString
|
21
20
|
include Clause
|
22
|
-
include Referenceable
|
23
21
|
|
24
22
|
def initialize(clause_list, props)
|
25
23
|
super(clause_list, :create, EvalContext)
|
@@ -59,7 +57,6 @@ module Neo4j
|
|
59
57
|
|
60
58
|
class CreatePath
|
61
59
|
include Clause
|
62
|
-
include Referenceable
|
63
60
|
|
64
61
|
attr_reader :arg_list
|
65
62
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Cypher
|
3
|
+
class Foreach < AbstractFilter
|
4
|
+
|
5
|
+
def initialize(clause_list, input_context, &block)
|
6
|
+
super(clause_list, :foreach, input_context)
|
7
|
+
# Input can either be a property array or a node/relationship collection
|
8
|
+
input = input_context.clause
|
9
|
+
clause_list.delete(input)
|
10
|
+
filter_initialize(input_context, '', " : ", &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/lib/neo4j-cypher/match.rb
CHANGED
@@ -3,7 +3,6 @@ module Neo4j
|
|
3
3
|
|
4
4
|
class MatchStart
|
5
5
|
include Clause
|
6
|
-
include Referenceable
|
7
6
|
|
8
7
|
attr_reader :match_list
|
9
8
|
attr_accessor :algorithm
|
@@ -196,7 +195,7 @@ module Neo4j
|
|
196
195
|
set_rel(rels.first)
|
197
196
|
else
|
198
197
|
# wrap and maybe join several relationship strings
|
199
|
-
@rel_var = RelVar.
|
198
|
+
@rel_var = RelVar.new(clause_list, rels)
|
200
199
|
end
|
201
200
|
self
|
202
201
|
end
|
@@ -215,16 +214,11 @@ module Neo4j
|
|
215
214
|
elsif rel.respond_to?(:clause) && rel.clause.match_value
|
216
215
|
@rel_var = rel.clause
|
217
216
|
else
|
218
|
-
@rel_var = RelVar.new(clause_list, rel)
|
217
|
+
@rel_var = RelVar.new(clause_list, [rel])
|
219
218
|
end
|
220
219
|
self
|
221
220
|
end
|
222
221
|
|
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
222
|
def -(to)
|
229
223
|
@match_start.match_list.delete(self) # since it is complete now
|
230
224
|
RelRightMatchContext.new(@match_start, self, @rel_var, to, :both)
|
@@ -308,32 +302,41 @@ module Neo4j
|
|
308
302
|
end
|
309
303
|
|
310
304
|
def to_cypher_no_join
|
311
|
-
|
305
|
+
x = @to.match_value
|
306
|
+
"(#{@from.match_value})#{DIR_OPERATORS[@dir]}(#{x})"
|
312
307
|
end
|
313
308
|
|
314
309
|
def to_cypher_join
|
315
|
-
"#{DIR_OPERATORS[@dir]}(#{@to.
|
310
|
+
"#{DIR_OPERATORS[@dir]}(#{@to.match_value})"
|
316
311
|
end
|
317
312
|
end
|
318
313
|
|
319
314
|
class Entities
|
320
315
|
include Clause
|
316
|
+
attr_reader :input
|
321
317
|
|
322
318
|
def initialize(clause_list, iterable, input)
|
323
319
|
super(clause_list, :entities, EvalContext)
|
324
|
-
|
325
|
-
|
320
|
+
@iterable = iterable
|
321
|
+
@input = input.clause
|
322
|
+
end
|
323
|
+
|
324
|
+
def referenced!
|
325
|
+
@input.referenced!
|
326
|
+
end
|
327
|
+
|
328
|
+
def return_value
|
329
|
+
"#{@iterable}(#{@input.var_name})"
|
326
330
|
end
|
327
331
|
|
328
332
|
class EvalContext
|
329
333
|
include Context
|
330
334
|
include PredicateMethods
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
#end
|
335
|
+
include Returnable
|
336
|
+
|
337
|
+
include Variable
|
338
|
+
include Matchable
|
339
|
+
include Aggregate
|
337
340
|
end
|
338
341
|
end
|
339
342
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Neography
|
2
|
+
|
3
|
+
# Monkey patch so it works better with neo4-cypher gem and becomes more similar to neo4j-core
|
4
|
+
class Relationship
|
5
|
+
def _java_rel
|
6
|
+
self
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Node
|
11
|
+
def _java_node
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Rest
|
17
|
+
def execute_cypher(params, &dsl)
|
18
|
+
q = Neo4j::Cypher.query(params, &dsl).to_s
|
19
|
+
execute_query(q)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -35,7 +35,6 @@ module Neo4j
|
|
35
35
|
class Operator
|
36
36
|
attr_reader :left_operand, :right_operand, :op, :neg, :eval_context
|
37
37
|
include Clause
|
38
|
-
include Referenceable
|
39
38
|
|
40
39
|
def initialize(clause_list, left_operand, right_operand, op, clause_type = :where, post_fix = nil, &dsl)
|
41
40
|
super(clause_list, clause_type, EvalContext)
|
@@ -48,19 +47,14 @@ module Neo4j
|
|
48
47
|
@valid = true
|
49
48
|
|
50
49
|
# since we handle it ourself in to_cypher method unless it needs to be declared (as a cypher start node/relationship)
|
51
|
-
clause_list.delete(left_operand)
|
52
|
-
clause_list.delete(right_operand)
|
53
|
-
|
50
|
+
clause_list.delete(left_operand) if remove_operand?(left_operand)
|
51
|
+
clause_list.delete(right_operand) if remove_operand?(right_operand)
|
54
52
|
@neg = nil
|
55
53
|
end
|
56
54
|
|
57
|
-
def
|
55
|
+
def remove_operand?(operand)
|
58
56
|
clause = operand.respond_to?(:clause) ? operand.clause : operand
|
59
|
-
clause.kind_of?(Clause) && clause.clause_type == :
|
60
|
-
end
|
61
|
-
|
62
|
-
def separator
|
63
|
-
" and "
|
57
|
+
clause.kind_of?(Clause) && clause.clause_type == :where
|
64
58
|
end
|
65
59
|
|
66
60
|
def match_value
|
@@ -1,62 +1,11 @@
|
|
1
1
|
module Neo4j
|
2
2
|
module Cypher
|
3
|
-
class Predicate
|
4
|
-
include Clause
|
5
|
-
include Referenceable
|
6
|
-
attr_accessor :params
|
3
|
+
class Predicate < AbstractFilter
|
7
4
|
|
8
|
-
def initialize(clause_list,
|
9
|
-
super(clause_list,
|
10
|
-
|
11
|
-
|
12
|
-
params[:input].referenced! if params[:input].respond_to?(:referenced!)
|
13
|
-
|
14
|
-
clause_list.push
|
15
|
-
|
16
|
-
var = NodeVar.as_var(clause_list, @identifier)
|
17
|
-
|
18
|
-
input = params[:input]
|
19
|
-
|
20
|
-
# TODO refactor please
|
21
|
-
if input.kind_of?(Property)
|
22
|
-
eval_prop = Property.new(var)
|
23
|
-
eval_prop.expr = @identifier
|
24
|
-
yield_param = eval_prop.eval_context
|
25
|
-
args = ""
|
26
|
-
else
|
27
|
-
yield_param = var.eval_context
|
28
|
-
args = "(#{input.var_name})"
|
29
|
-
end
|
30
|
-
|
31
|
-
result = RootClause::EvalContext.new(self).instance_exec(yield_param, ¶ms[:predicate_block])
|
32
|
-
|
33
|
-
result = case params[:clause]
|
34
|
-
when :return_item
|
35
|
-
block_result = result.clause.to_cypher
|
36
|
-
"#{params[:op]}(#@identifier in #{params[:iterable]}#{args} : #{block_result})"
|
37
|
-
when :foreach
|
38
|
-
block_result = clause_list.to_cypher
|
39
|
-
"#{params[:op]}(#@identifier in #{params[:iterable]}#{args} : #{block_result})"
|
40
|
-
else
|
41
|
-
block_result = clause_list.to_cypher
|
42
|
-
"#{params[:op]}(#@identifier in #{params[:iterable]}#{args} WHERE #{block_result})"
|
43
|
-
end
|
44
|
-
|
45
|
-
clause_list.pop
|
46
|
-
@result = result
|
47
|
-
end
|
48
|
-
|
49
|
-
def return_value
|
50
|
-
to_cypher
|
51
|
-
end
|
52
|
-
|
53
|
-
|
54
|
-
def separator
|
55
|
-
@separator
|
56
|
-
end
|
57
|
-
|
58
|
-
def to_cypher
|
59
|
-
@result
|
5
|
+
def initialize(clause_list, method_name, input_context, &block)
|
6
|
+
super(clause_list, :where, input_context)
|
7
|
+
# Input can either be a property array or a node/relationship collection
|
8
|
+
filter_initialize(input_context, method_name, " WHERE ", &block)
|
60
9
|
end
|
61
10
|
end
|
62
11
|
|
@@ -10,15 +10,17 @@ module Neo4j
|
|
10
10
|
# n=node(2, 3, 4); n[:name].collect
|
11
11
|
# # same as START n0=node(2,3,4) RETURN collect(n0.property)
|
12
12
|
class Property
|
13
|
-
include Referenceable
|
14
13
|
include Clause
|
15
14
|
|
15
|
+
attr_accessor :prop_name
|
16
|
+
|
16
17
|
def initialize(var, prop_name = nil)
|
17
18
|
super(var.clause_list, :property, EvalContext)
|
18
19
|
@var = var
|
19
20
|
@prop_name = prop_name
|
20
21
|
end
|
21
22
|
|
23
|
+
# TODO check why needed
|
22
24
|
def var_name
|
23
25
|
@var.var_name
|
24
26
|
end
|
@@ -65,6 +67,7 @@ module Neo4j
|
|
65
67
|
include MathFunctions
|
66
68
|
include PredicateMethods
|
67
69
|
include Aggregate
|
70
|
+
include Returnable
|
68
71
|
|
69
72
|
def asc
|
70
73
|
ReturnItem.new(clause_list, self).eval_context.asc
|
@@ -88,17 +91,6 @@ module Neo4j
|
|
88
91
|
self
|
89
92
|
end
|
90
93
|
|
91
|
-
# required by the Predicate Methods Module
|
92
|
-
# @see PredicateMethods
|
93
|
-
# @private
|
94
|
-
def iterable
|
95
|
-
clause.return_value
|
96
|
-
end
|
97
|
-
|
98
|
-
def input
|
99
|
-
clause
|
100
|
-
end
|
101
|
-
|
102
94
|
# @private
|
103
95
|
def in?(values)
|
104
96
|
clause.unary_operator("", :where, " IN [#{values.map { |x| %Q["#{x}"] }.join(',')}]")
|
data/lib/neo4j-cypher/rel_var.rb
CHANGED
@@ -3,49 +3,43 @@ module Neo4j
|
|
3
3
|
|
4
4
|
class RelVar
|
5
5
|
include Clause
|
6
|
-
include ToPropString
|
7
|
-
include Referenceable
|
8
6
|
|
9
7
|
def initialize(clause_list, expr, props = nil)
|
10
8
|
super(clause_list, :rel_var, EvalContext)
|
11
9
|
|
12
|
-
|
13
|
-
when String
|
14
|
-
@match_value = expr.empty? ? '?' : expr.to_s
|
15
|
-
guess = expr.is_a?(String) && /([[:alpha:]_]*)/.match(expr)[1]
|
16
|
-
self.var_name = guess.to_sym if guess && !guess.empty?
|
17
|
-
when Symbol
|
18
|
-
@match_value = ":`#{expr}`"
|
19
|
-
else
|
20
|
-
raise "Illegal arg for rel #{expr.class}"
|
21
|
-
end
|
10
|
+
self.var_name = guess_var_name_from_string(expr.first) if expr.first.is_a?(String)
|
22
11
|
|
23
|
-
|
12
|
+
if props
|
13
|
+
@match_value = "#{match_value_from_args(expr)} #{to_prop_string(props)}"
|
14
|
+
else
|
15
|
+
@match_value = match_value_from_args(expr)
|
16
|
+
end
|
24
17
|
end
|
25
18
|
|
26
|
-
def
|
27
|
-
|
19
|
+
def match_value_from_args(expr)
|
20
|
+
if expr.first.is_a?(String)
|
21
|
+
expr.first
|
22
|
+
elsif expr.first.is_a?(Symbol)
|
23
|
+
":#{expr.map { |e| match_value_from_symbol(e) }.join('|')}"
|
24
|
+
elsif expr.empty?
|
25
|
+
'?'
|
26
|
+
else
|
27
|
+
# try to join several RelVars to one rel var
|
28
|
+
":#{expr.map { |e| e.clause.rel_type }.join('|')}"
|
29
|
+
end
|
28
30
|
end
|
29
31
|
|
30
|
-
def
|
31
|
-
|
32
|
+
def guess_var_name_from_string(expr)
|
33
|
+
guess = /([[:alpha:]_]*)/.match(expr)[1]
|
34
|
+
guess && !guess.empty? && guess
|
35
|
+
end
|
32
36
|
|
33
|
-
|
34
|
-
|
35
|
-
else
|
36
|
-
RelVar.new(clause_list, ":#{rel_string}")
|
37
|
-
end
|
37
|
+
def match_value_from_symbol(expr)
|
38
|
+
"`#{expr}`"
|
38
39
|
end
|
39
40
|
|
40
|
-
def
|
41
|
-
|
42
|
-
when String, Symbol
|
43
|
-
RelVar.new(clause_list, rel_or_symbol).rel_type
|
44
|
-
when Neo4j::Cypher::RelVar::EvalContext
|
45
|
-
rel_or_symbol.clause.rel_type
|
46
|
-
else
|
47
|
-
raise "Unknown type of relationship, got #{rel_or_symbol.class}"
|
48
|
-
end
|
41
|
+
def rel_type
|
42
|
+
@match_value.include?(':') ? @match_value.split(':').last : @match_value.sub('?', '')
|
49
43
|
end
|
50
44
|
|
51
45
|
def referenced!
|
data/lib/neo4j-cypher/return.rb
CHANGED
data/lib/neo4j-cypher/root.rb
CHANGED
@@ -33,7 +33,7 @@ module Neo4j
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def return_names
|
36
|
-
ret = clause_list.
|
36
|
+
ret = clause_list.return_clause
|
37
37
|
ret ? ret.return_items.map { |ri| (ri.alias_name || ri.return_value).to_sym } : []
|
38
38
|
end
|
39
39
|
|
@@ -46,12 +46,10 @@ module Neo4j
|
|
46
46
|
# @return self
|
47
47
|
def match(*, &match_dsl)
|
48
48
|
instance_eval(&match_dsl) if match_dsl
|
49
|
-
self
|
50
49
|
end
|
51
50
|
|
52
51
|
def match_not(&match_dsl)
|
53
52
|
instance_eval(&match_dsl).not
|
54
|
-
self
|
55
53
|
end
|
56
54
|
|
57
55
|
# Does nothing, just for making the DSL easier to read (maybe)
|
@@ -60,29 +58,55 @@ module Neo4j
|
|
60
58
|
self
|
61
59
|
end
|
62
60
|
|
63
|
-
def where(w=nil)
|
64
|
-
Where.new(clause_list, w
|
61
|
+
def where(w=nil, &block)
|
62
|
+
Where.new(clause_list, self, w, &block)
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
def where_not(w=nil, &block)
|
67
|
+
Where.new(clause_list, self, w, &block).neg!
|
65
68
|
self
|
66
69
|
end
|
67
70
|
|
68
71
|
# Specifies a start node by performing a lucene query.
|
69
|
-
# @param [Class] index_class a class responsible for an index
|
72
|
+
# @param [Class, String] index_class a class responsible for an index or the string value of the index
|
70
73
|
# @param [String] q the lucene query
|
71
74
|
# @param [Symbol] index_type the type of index
|
72
|
-
# @return [
|
75
|
+
# @return [LuceneQuery]
|
73
76
|
def query(index_class, q, index_type = :exact)
|
74
|
-
|
77
|
+
LuceneQuery.query_node_by_class(clause_list, index_class, q, index_type).eval_context
|
78
|
+
end
|
79
|
+
|
80
|
+
# Specifies a start relationship by performing a lucene query.
|
81
|
+
# @param [Class, String] index_class a class responsible for an index or the string value of the index
|
82
|
+
# @param [String] q the lucene query
|
83
|
+
# @param [Symbol] index_type the type of index
|
84
|
+
# @return [LuceneQuery]
|
85
|
+
def query_rel(index_class, q, index_type = :exact)
|
86
|
+
LuceneQuery.query_rel_by_class(clause_list, index_class, q, index_type).eval_context
|
75
87
|
end
|
76
88
|
|
89
|
+
|
77
90
|
# Specifies a start node by performing a lucene query.
|
78
|
-
# @param [Class] index_class a class responsible for an index
|
91
|
+
# @param [Class, String] index_class a class responsible for an index or the string value of the index
|
79
92
|
# @param [String, Symbol] key the key we ask for
|
80
93
|
# @param [String, Symbol] value the value of the key we ask for
|
81
|
-
# @return [
|
94
|
+
# @return [LuceneQuery]
|
82
95
|
def lookup(index_class, key, value)
|
83
|
-
|
96
|
+
LuceneQuery.lookup_node_by_class(clause_list, index_class, key, value).eval_context
|
84
97
|
end
|
85
98
|
|
99
|
+
|
100
|
+
# Specifies a start relationship by performing a lucene query.
|
101
|
+
# @param [Class, String] index_class a class responsible for an index or the string value of the index
|
102
|
+
# @param [String, Symbol] key the key we ask for
|
103
|
+
# @param [String, Symbol] value the value of the key we ask for
|
104
|
+
# @return [LuceneQuery]
|
105
|
+
def lookup_rel(index_class, key, value)
|
106
|
+
LuceneQuery.lookup_rel_by_class(clause_list, index_class, key, value).eval_context
|
107
|
+
end
|
108
|
+
|
109
|
+
|
86
110
|
# Creates a node variable.
|
87
111
|
# It will create different variables depending on the type of the first element in the nodes argument.
|
88
112
|
# * Fixnum - it will be be used as neo_id for start node(s) (StartNode)
|
@@ -106,14 +130,9 @@ module Neo4j
|
|
106
130
|
def rel(*rels)
|
107
131
|
if rels.first.is_a?(Fixnum) || rels.first.respond_to?(:neo_id)
|
108
132
|
StartRel.new(clause_list, rels).eval_context
|
109
|
-
elsif rels.first.is_a?(Symbol)
|
110
|
-
RelVar.new(clause_list, ":`#{rels.first}`", rels[1]).eval_context
|
111
|
-
elsif rels.first.is_a?(String)
|
112
|
-
RelVar.new(clause_list, rels.first, rels[1]).eval_context
|
113
|
-
elsif rels.empty?
|
114
|
-
RelVar.new(clause_list, '?').eval_context
|
115
133
|
else
|
116
|
-
|
134
|
+
props = rels.pop if rels.last.is_a?(Hash)
|
135
|
+
RelVar.new(clause_list, rels, props).eval_context
|
117
136
|
end
|
118
137
|
end
|
119
138
|
|
@@ -145,13 +164,16 @@ module Neo4j
|
|
145
164
|
end
|
146
165
|
|
147
166
|
def nodes(*args)
|
148
|
-
|
149
|
-
ReturnItem.new(clause_list, "nodes(#{s})").eval_context
|
167
|
+
_entities(args, 'nodes')
|
150
168
|
end
|
151
169
|
|
152
170
|
def rels(*args)
|
153
|
-
|
154
|
-
|
171
|
+
_entities(args, 'relationships')
|
172
|
+
end
|
173
|
+
|
174
|
+
def _entities(arg_list, entity_type)
|
175
|
+
s = arg_list.map { |x| x.clause.referenced!; x.clause.var_name }.join(", ")
|
176
|
+
ReturnItem.new(clause_list, "#{entity_type}(#{s})").eval_context
|
155
177
|
end
|
156
178
|
|
157
179
|
def create_path(*args, &block)
|