neo4j-core 2.0.0.alpha.1-java → 2.0.0-java
Sign up to get free protection for your applications and to get access to all the features.
- 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/neo4j/algo.rb
ADDED
@@ -0,0 +1,300 @@
|
|
1
|
+
module Neo4j
|
2
|
+
|
3
|
+
|
4
|
+
# A wrapper for the Neo4j Algo <tt>org.neo4j.graphalgo.GraphAlgoFactory</tt> class.
|
5
|
+
#
|
6
|
+
# @example the length of the first path found between two nodes
|
7
|
+
# Neo4j::Algo.all_paths(a,b).outgoing(:friends).depth(1).first.length
|
8
|
+
#
|
9
|
+
# @example the nodes in the shortest path between two nodes
|
10
|
+
# Neo4j::Algo.shortest_path(a,b).outgoing(:friends).outgoing(:knows).to_a #=> [node1, node2, node3 ...]
|
11
|
+
#
|
12
|
+
# @example the relationships in the shortest path between two nodes
|
13
|
+
# Neo4j::Algo.shortest_path(a,b).outgoing(:friends).outgoing(:knows).rels.to_a #=> [rel1, rel2, rel3 ...]
|
14
|
+
#
|
15
|
+
# @example The Dijkstra algorithm using a cost evaluator
|
16
|
+
# Neo4j::Algo.dijkstra_path(a,b).cost_evaluator{|rel,direction| rel[:weight]}
|
17
|
+
class Algo
|
18
|
+
include Enumerable
|
19
|
+
include Neo4j::Core::ToJava
|
20
|
+
|
21
|
+
class EstimateEvaluator #:nodoc
|
22
|
+
include org.neo4j.graphalgo.EstimateEvaluator
|
23
|
+
include Neo4j::Core::ToJava
|
24
|
+
|
25
|
+
def initialize(&evaluator)
|
26
|
+
@evaluator = evaluator
|
27
|
+
end
|
28
|
+
|
29
|
+
# Implements T getCost(Node node, Node goal)
|
30
|
+
# Estimate the weight of the remaining path from one node to another.
|
31
|
+
def get_cost(node, goal)
|
32
|
+
@evaluator.call(node, goal)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class CostEvaluator #:nodoc
|
37
|
+
include org.neo4j.graphalgo.CostEvaluator
|
38
|
+
include Neo4j::Core::ToJava
|
39
|
+
|
40
|
+
def initialize(&evaluator)
|
41
|
+
@evaluator = evaluator
|
42
|
+
end
|
43
|
+
|
44
|
+
# Implements the Java Method: T getCost(Relationship relationship, Direction direction)
|
45
|
+
# From the JavaDoc: <pre>
|
46
|
+
# This is the general method for looking up costs for relationships.
|
47
|
+
# This can do anything, like looking up a property or running some small calculation.
|
48
|
+
# Parameters:
|
49
|
+
# relationship -
|
50
|
+
# direction - The direction in which the relationship is being evaluated, either Direction.INCOMING or Direction.OUTGOING.
|
51
|
+
# Returns:
|
52
|
+
# The cost for this edge/relationship
|
53
|
+
# </pre>
|
54
|
+
def get_cost(relationship, direction)
|
55
|
+
@evaluator.call(relationship, dir_from_java(direction))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize(from, to, &factory_proc) #:nodoc:
|
60
|
+
@from = from
|
61
|
+
@to = to
|
62
|
+
@factory_proc = factory_proc
|
63
|
+
@type_and_dirs = []
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
def _depth #:nodoc:
|
68
|
+
@depth || java.lang.Integer::MAX_VALUE
|
69
|
+
end
|
70
|
+
|
71
|
+
def _expander #:nodoc:
|
72
|
+
expander = @expander
|
73
|
+
expander ||= @type_and_dirs.empty? ? org.neo4j.kernel.Traversal.expanderForAllTypes() : org.neo4j.kernel.Traversal.expanderForTypes(*@type_and_dirs)
|
74
|
+
expander
|
75
|
+
end
|
76
|
+
|
77
|
+
def _cost_evaluator #:nodoc:
|
78
|
+
raise "Algorithm requeries a cost evalulator, use the cost_evaluator to provide one" unless @cost_evaluator
|
79
|
+
@cost_evaluator
|
80
|
+
end
|
81
|
+
|
82
|
+
def _estimate_evaluator #:nodoc:
|
83
|
+
raise "Algorithm requeries a estimate evaluator, use the estimate_evaluator to provide one" unless @estimate_evaluator
|
84
|
+
@estimate_evaluator
|
85
|
+
end
|
86
|
+
|
87
|
+
# Specifies which outgoing relationship should be traversed for the graph algorithm
|
88
|
+
#
|
89
|
+
# @param [String, Symbol] rel the relationship type
|
90
|
+
# @return self
|
91
|
+
def outgoing(rel)
|
92
|
+
@type_and_dirs << type_to_java(rel)
|
93
|
+
@type_and_dirs << dir_to_java(:outgoing)
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
# Specifies which incoming relationship should be traversed for the graph algorithm
|
98
|
+
#
|
99
|
+
# @param [String, Symbol] rel the relationship type
|
100
|
+
# @return self
|
101
|
+
def incoming(rel)
|
102
|
+
@type_and_dirs << type_to_java(rel)
|
103
|
+
@type_and_dirs << dir_to_java(:incoming)
|
104
|
+
self
|
105
|
+
end
|
106
|
+
|
107
|
+
# Specifies which relationship should be traversed for the graph algorithm
|
108
|
+
#
|
109
|
+
# @example
|
110
|
+
# Neo4j::Algo.shortest_path(@x,@y).expand{|node| node._rels(:outgoing, :friends)}
|
111
|
+
#
|
112
|
+
# @example the above is same as:
|
113
|
+
# Neo4j::Algo.shortest_path(@x,@y).outgoing(:friends)
|
114
|
+
#
|
115
|
+
# @param [Proc] expander_proc a proc relationship type (symbol)
|
116
|
+
# @return self
|
117
|
+
def expand(&expander_proc)
|
118
|
+
@expander = (Neo4j::Core::Traversal::RelExpander.create_pair(&expander_proc))
|
119
|
+
self
|
120
|
+
end
|
121
|
+
|
122
|
+
# If only a single path should be returned,
|
123
|
+
# default for some algorithms, like shortest_path
|
124
|
+
def single
|
125
|
+
@single = true
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
# See #single
|
130
|
+
# Not sure if this method is useful
|
131
|
+
def many
|
132
|
+
@single = false
|
133
|
+
end
|
134
|
+
|
135
|
+
# The depth of the traversal
|
136
|
+
# Notice not all algorithm uses this argument (aStar and dijkstra)
|
137
|
+
def depth(depth)
|
138
|
+
@depth = depth
|
139
|
+
self
|
140
|
+
end
|
141
|
+
|
142
|
+
# Specifies a cost evaluator for the algorithm.
|
143
|
+
# Only available for the aStar and dijkstra algorithms.
|
144
|
+
#
|
145
|
+
# @example
|
146
|
+
# Neo4j::Algo.dijkstra(@x,@y).cost_evaluator{|rel,*| rel[:weight]}
|
147
|
+
#
|
148
|
+
def cost_evaluator(&cost_evaluator_proc)
|
149
|
+
@cost_evaluator = CostEvaluator.new(&cost_evaluator_proc)
|
150
|
+
self
|
151
|
+
end
|
152
|
+
|
153
|
+
# Specifies an evaluator that returns an (optimistic) estimation of the cost to get from the current node (in the traversal) to the end node.
|
154
|
+
# Only available for the aStar algorithm.
|
155
|
+
#
|
156
|
+
# The provided proc estimate the weight of the remaining path from one node to another.
|
157
|
+
# The proc takes two parameters:
|
158
|
+
# * node :: the node to estimate the weight from.
|
159
|
+
# * goal :: the node to estimate the weight to.
|
160
|
+
#
|
161
|
+
# The proc should return an estimation of the weight of the path from the first node to the second.
|
162
|
+
#
|
163
|
+
# @example
|
164
|
+
# Neo4j::Algo.a_star(@x,@y).cost_evaluator{...}.estimate_evaluator{|node,goal| some calculation returning a Float}
|
165
|
+
#
|
166
|
+
def estimate_evaluator(&estimate_evaluator_proc)
|
167
|
+
@estimate_evaluator = EstimateEvaluator.new(&estimate_evaluator_proc)
|
168
|
+
self
|
169
|
+
end
|
170
|
+
|
171
|
+
# Specifies that nodes should be returned from as result
|
172
|
+
# @see #rels
|
173
|
+
def nodes
|
174
|
+
@path_finder_method = :nodes
|
175
|
+
self
|
176
|
+
end
|
177
|
+
|
178
|
+
# Specifies that relationships should be returned from as result
|
179
|
+
# @see #nodes
|
180
|
+
def rels
|
181
|
+
@path_finder_method = :relationships
|
182
|
+
self
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
# So that one can call directly method on the PathFinder result from an executed algorithm.
|
187
|
+
def method_missing(m, *args, &block)
|
188
|
+
execute_algo.send(m, *args)
|
189
|
+
end
|
190
|
+
|
191
|
+
def each(&block) #:nodoc:
|
192
|
+
if @single && @path_finder_method
|
193
|
+
execute_algo.send(@path_finder_method).each &block
|
194
|
+
else
|
195
|
+
traversal = execute_algo
|
196
|
+
traversal.each &block if traversal
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def execute_algo #:nodoc:
|
201
|
+
instance = self.instance_eval(&@factory_proc)
|
202
|
+
if @single
|
203
|
+
instance.find_single_path(@from._java_node, @to._java_node)
|
204
|
+
else
|
205
|
+
instance.find_all_paths(@from._java_node, @to._java_node)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Returns an instance of Neo4j::Algo which can find all available paths between two nodes.
|
210
|
+
# These returned paths can contain loops (i.e. a node can occur more than once in any returned path).
|
211
|
+
def self.all_paths(from, to)
|
212
|
+
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.all_paths(_expander, _depth) }
|
213
|
+
end
|
214
|
+
|
215
|
+
# See #all_paths, returns the first path found
|
216
|
+
def self.all_path(from, to)
|
217
|
+
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.all_paths(_expander, _depth) }.single
|
218
|
+
end
|
219
|
+
|
220
|
+
# Returns an instance of Neo4j::Algo which can find all simple paths between two nodes.
|
221
|
+
# These returned paths cannot contain loops (i.e. a node cannot occur more than once in any returned path).
|
222
|
+
def self.all_simple_paths(from, to)
|
223
|
+
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.all_simple_paths(_expander, _depth) }
|
224
|
+
end
|
225
|
+
|
226
|
+
# See #all_simple_paths, returns the first path found
|
227
|
+
def self.all_simple_path(from, to)
|
228
|
+
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.all_simple_paths(_expander, _depth) }.single
|
229
|
+
end
|
230
|
+
|
231
|
+
# Returns an instance of Neo4j::Algo which can find all shortest paths (that is paths with as short Path.length() as possible) between two nodes.
|
232
|
+
# These returned paths cannot contain loops (i.e. a node cannot occur more than once in any returned path).
|
233
|
+
def self.shortest_paths(from, to)
|
234
|
+
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.shortest_path(_expander, _depth) }
|
235
|
+
end
|
236
|
+
|
237
|
+
# See #shortest_paths, returns the first path found
|
238
|
+
def self.shortest_path(from, to)
|
239
|
+
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.shortest_path(_expander, _depth) }.single
|
240
|
+
end
|
241
|
+
|
242
|
+
# Returns an instance of Neo4j::Algo which uses the Dijkstra algorithm to find the cheapest path between two nodes.
|
243
|
+
# The definition of "cheap" is the lowest possible cost to get from the start node to the end node, where the cost is returned from costEvaluator.
|
244
|
+
# These returned paths cannot contain loops (i.e. a node cannot occur more than once in any returned path).
|
245
|
+
# See http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm for more information.
|
246
|
+
#
|
247
|
+
# Example
|
248
|
+
#
|
249
|
+
# Neo4j::Algo.dijkstra_path(node_a,node_b).cost_evaluator{|rel,*| rel[:weight]}.rels
|
250
|
+
#
|
251
|
+
def self.dijkstra_paths(from, to)
|
252
|
+
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.dijkstra(_expander, _cost_evaluator) }
|
253
|
+
end
|
254
|
+
|
255
|
+
# See #dijkstra_paths, returns the first path found
|
256
|
+
#
|
257
|
+
def self.dijkstra_path(from, to)
|
258
|
+
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.dijkstra(_expander, _cost_evaluator) }.single
|
259
|
+
end
|
260
|
+
|
261
|
+
# Returns an instance of Neo4j::Algo which uses the A* algorithm to find the cheapest path between two nodes.
|
262
|
+
# The definition of "cheap" is the lowest possible cost to get from the start node to the end node, where the cost is returned from lengthEvaluator and estimateEvaluator. These returned paths cannot contain loops (i.e. a node cannot occur more than once in any returned path).
|
263
|
+
# See http://en.wikipedia.org/wiki/A*_search_algorithm for more information.
|
264
|
+
#
|
265
|
+
# Expacts an cost evaluator and estimate evaluator, see Algo#cost_evaluator and Algo#estimate_evaluator
|
266
|
+
#
|
267
|
+
# Example:
|
268
|
+
#
|
269
|
+
# Neo4j::Algo.a_star_path(@x,@y).cost_evaluator{|rel,*| rel[:weight]}.estimate_evaluator{|node,goal| returns a float value}
|
270
|
+
#
|
271
|
+
def self.a_star_paths(from, to)
|
272
|
+
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.a_star(_expander, _cost_evaluator, _estimate_evaluator) }
|
273
|
+
end
|
274
|
+
|
275
|
+
# See #a_star_paths, returns the first path found
|
276
|
+
#
|
277
|
+
def self.a_star_path(from, to)
|
278
|
+
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.a_star(_expander, _cost_evaluator, _estimate_evaluator) }.single
|
279
|
+
end
|
280
|
+
|
281
|
+
# Returns an instance of Neo4j::Algo can find all paths of a certain length(depth) between two nodes.
|
282
|
+
# These returned paths cannot contain loops (i.e. a node cannot occur more than once in any returned path).
|
283
|
+
# Expects setting the depth parameter (the lenghto of the path) by the Algo#depth method.
|
284
|
+
#
|
285
|
+
# Example:
|
286
|
+
#
|
287
|
+
# Neo4j::Algo.with_length_paths(node_a,node_b).depth(2).each {|x| puts "Node #{x}"}
|
288
|
+
#
|
289
|
+
def self.with_length_paths(from,to)
|
290
|
+
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.paths_with_length(_expander, _depth) }
|
291
|
+
end
|
292
|
+
|
293
|
+
# See #with_length_paths, returns the first path found
|
294
|
+
#
|
295
|
+
def self.with_length_path(from,to)
|
296
|
+
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.paths_with_length(_expander, _depth) }.single
|
297
|
+
end
|
298
|
+
|
299
|
+
end
|
300
|
+
end
|
data/lib/neo4j/config.rb
CHANGED
@@ -37,7 +37,7 @@ module Neo4j
|
|
37
37
|
# @return [Hash] the default file loaded by yaml
|
38
38
|
def defaults
|
39
39
|
require 'yaml'
|
40
|
-
@defaults ||= YAML.load_file(default_file)
|
40
|
+
@defaults ||= Neo4j::Core::HashWithIndifferentAccess.new(YAML.load_file(default_file))
|
41
41
|
end
|
42
42
|
|
43
43
|
# @return [String] the expanded path of the Config[:storage_path] property
|
@@ -56,7 +56,7 @@ module Neo4j
|
|
56
56
|
# @yield config
|
57
57
|
# @yieldparam [Neo4j::Config] config - this configuration class
|
58
58
|
def use
|
59
|
-
@configuration ||=
|
59
|
+
@configuration ||= Neo4j::Core::HashWithIndifferentAccess.new
|
60
60
|
yield @configuration
|
61
61
|
nil
|
62
62
|
end
|
@@ -123,16 +123,13 @@ module Neo4j
|
|
123
123
|
map
|
124
124
|
end
|
125
125
|
|
126
|
-
private
|
127
|
-
|
128
126
|
# @return The a new configuration using default values as a hash.
|
129
127
|
def setup()
|
130
|
-
@configuration =
|
128
|
+
@configuration = Neo4j::Core::HashWithIndifferentAccess.new
|
131
129
|
@configuration.merge!(defaults)
|
132
130
|
@configuration
|
133
131
|
end
|
134
132
|
|
135
|
-
|
136
133
|
end
|
137
134
|
end
|
138
135
|
|
data/lib/neo4j/cypher.rb
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
module Neo4j
|
2
|
+
|
3
|
+
# Generates a Cypher string from a Ruby DSL.
|
4
|
+
# This class is used by the {Ņeo4j#query} method.
|
5
|
+
# Methods on in this class returns object from the {Neo4j::Core::Cypher} module (e.g. {Neo4j::Cypher#node} can return a #{Neo4j::Core::Cypher::StartNode}).
|
6
|
+
#
|
7
|
+
# @example usage
|
8
|
+
# Neo4j::Cypher.new { node }
|
9
|
+
#
|
10
|
+
class Cypher
|
11
|
+
# @private
|
12
|
+
attr_reader :expressions
|
13
|
+
|
14
|
+
include Neo4j::Core::Cypher
|
15
|
+
include Neo4j::Core::Cypher::MathFunctions
|
16
|
+
|
17
|
+
# Creates a Cypher DSL query.
|
18
|
+
# To create a new cypher query you must initialize it either an String or a Block.
|
19
|
+
#
|
20
|
+
# @example <tt>START n0=node(3) MATCH (n0)--(x) RETURN x</tt> same as
|
21
|
+
# Cypher.new { start n = node(3); match n <=> :x; ret :x }.to_s
|
22
|
+
#
|
23
|
+
# @example <tt>START n0=node(3) MATCH (n0)-[r]->(x) RETURN r</tt> same as
|
24
|
+
# node(3) > :r > :x; :r
|
25
|
+
#
|
26
|
+
# @example <tt>START n0=node(3) MATCH (n0)-->(x) RETURN x</tt> same as
|
27
|
+
# node(3) >> :x; :x
|
28
|
+
#
|
29
|
+
# @param args the argument for the dsl_block
|
30
|
+
# @yield the block which will be evaluated in the context of this object in order to create an Cypher Query string
|
31
|
+
# @yieldreturn [Return, Object] If the return is not an instance of Return it will be converted it to a Return object (if possible).
|
32
|
+
# @see Neo4j::Core::Cypher
|
33
|
+
def initialize(*args, &dsl_block)
|
34
|
+
@expressions = []
|
35
|
+
@variables = []
|
36
|
+
to_dsl_args = args.map do |a|
|
37
|
+
case
|
38
|
+
when a.is_a?(Array) && a.first.respond_to?(:_java_node)
|
39
|
+
StartNode.new(a, @expressions)
|
40
|
+
when a.is_a?(Array) && a.first.respond_to?(:_java_rel)
|
41
|
+
StartRel.new(a, @expressions)
|
42
|
+
when a.respond_to?(:_java_node)
|
43
|
+
StartNode.new([a], @expressions)
|
44
|
+
when a.respond_to?(:_java_rel)
|
45
|
+
StartRel.new([a], @expressions)
|
46
|
+
else
|
47
|
+
a
|
48
|
+
end
|
49
|
+
end
|
50
|
+
res = self.instance_exec(*to_dsl_args, &dsl_block)
|
51
|
+
unless res.kind_of?(Return)
|
52
|
+
res.respond_to?(:to_a) ? ret(*res) : ret(res)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Does nothing, just for making the DSL easier to read (maybe).
|
57
|
+
# @return self
|
58
|
+
def match(*)
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Does nothing, just for making the DSL easier to read (maybe)
|
63
|
+
# @return self
|
64
|
+
def start(*)
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
def where(w=nil)
|
69
|
+
Where.new(@expressions, w) if w.is_a?(String)
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
# Specifies a start node by performing a lucene query.
|
74
|
+
# @param [Class] index_class a class responsible for an index
|
75
|
+
# @param [String] q the lucene query
|
76
|
+
# @param [Symbol] index_type the type of index
|
77
|
+
# @return [NodeQuery]
|
78
|
+
def query(index_class, q, index_type = :exact)
|
79
|
+
NodeQuery.new(index_class, q, index_type, @expressions)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Specifies a start node by performing a lucene query.
|
83
|
+
# @param [Class] index_class a class responsible for an index
|
84
|
+
# @param [String, Symbol] key the key we ask for
|
85
|
+
# @param [String, Symbol] value the value of the key we ask for
|
86
|
+
# @return [NodeLookup]
|
87
|
+
def lookup(index_class, key, value)
|
88
|
+
NodeLookup.new(index_class, key, value, @expressions)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Creates a node variable.
|
92
|
+
# It will create different variables depending on the type of the first element in the nodes argument.
|
93
|
+
# * Fixnum - it will be be used as neo_id for start node(s) (StartNode)
|
94
|
+
# * Symbol - it will create an unbound node variable with the same name as the symbol (NodeVar#as)
|
95
|
+
# * empty array - it will create an unbound node variable (NodeVar)
|
96
|
+
#
|
97
|
+
# @param [Fixnum,Symbol,String] nodes the id of the nodes we want to start from
|
98
|
+
# @return [StartNode, NodeVar]
|
99
|
+
def node(*nodes)
|
100
|
+
if nodes.first.is_a?(Symbol)
|
101
|
+
NodeVar.new(@expressions, @variables).as(nodes.first)
|
102
|
+
elsif !nodes.empty?
|
103
|
+
StartNode.new(nodes, @expressions)
|
104
|
+
else
|
105
|
+
NodeVar.new(@expressions, @variables)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Similar to #node
|
110
|
+
# @return [StartRel, RelVar]
|
111
|
+
def rel(*rels)
|
112
|
+
if rels.first.is_a?(Fixnum) || rels.first.respond_to?(:neo_id)
|
113
|
+
StartRel.new(rels, @expressions)
|
114
|
+
elsif rels.first.is_a?(Symbol)
|
115
|
+
RelVar.new(@expressions, @variables, ":`#{rels.first}`").as(rels.first)
|
116
|
+
elsif rels.first.is_a?(String)
|
117
|
+
RelVar.new(@expressions, @variables, rels.first)
|
118
|
+
else
|
119
|
+
raise "Unknown arg #{rels.inspect}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Specifies a return statement.
|
124
|
+
# Notice that this is not needed, since the last value of the DSL block will be converted into one or more
|
125
|
+
# return statements.
|
126
|
+
# @param [Symbol, #var_name] returns a list of variables we want to return
|
127
|
+
# @return [Return]
|
128
|
+
def ret(*returns)
|
129
|
+
options = returns.last.is_a?(Hash) ? returns.pop : {}
|
130
|
+
@expressions -= @expressions.find_all { |r| r.clause == :return && returns.include?(r) }
|
131
|
+
returns.each { |ret| Return.new(ret, @expressions, options) unless ret.respond_to?(:clause) && [:order_by, :skip, :limit].include?(ret.clause)}
|
132
|
+
@expressions.last
|
133
|
+
end
|
134
|
+
|
135
|
+
def shortest_path(&block)
|
136
|
+
match = instance_eval(&block)
|
137
|
+
match.algorithm = 'shortestPath'
|
138
|
+
match.find_match_start
|
139
|
+
end
|
140
|
+
|
141
|
+
def shortest_paths(&block)
|
142
|
+
match = instance_eval(&block)
|
143
|
+
match.algorithm = 'allShortestPaths'
|
144
|
+
match.find_match_start
|
145
|
+
end
|
146
|
+
|
147
|
+
# @param [Symbol,nil] variable the entity we want to count or wildcard (*)
|
148
|
+
# @return [Return] a counter return clause
|
149
|
+
def count(variable='*')
|
150
|
+
Return.new("count(#{variable.to_s})", @expressions)
|
151
|
+
end
|
152
|
+
|
153
|
+
def coalesce(*args)
|
154
|
+
s = args.map { |x| x.var_name }.join(", ")
|
155
|
+
Return.new("coalesce(#{s})", @expressions)
|
156
|
+
end
|
157
|
+
|
158
|
+
def nodes(*args)
|
159
|
+
s = args.map { |x| x.referenced!; x.var_name }.join(", ")
|
160
|
+
Return.new("nodes(#{s})", @expressions)
|
161
|
+
end
|
162
|
+
|
163
|
+
def rels(*args)
|
164
|
+
s = args.map { |x| x.referenced!; x.var_name }.join(", ")
|
165
|
+
Return.new("relationships(#{s})", @expressions)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Converts the DSL query to a cypher String which can be executed by cypher query engine.
|
169
|
+
def to_s
|
170
|
+
clause = nil
|
171
|
+
@expressions.map do |expr|
|
172
|
+
next unless expr.valid?
|
173
|
+
expr_to_s = expr.clause != clause ? "#{expr.prefix} #{expr.to_s}" : "#{expr.separator}#{expr.to_s}"
|
174
|
+
clause = expr.clause
|
175
|
+
expr_to_s
|
176
|
+
end.join
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
end
|