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.
Files changed (66) hide show
  1. data/Gemfile +2 -2
  2. data/README.rdoc +161 -13
  3. data/config/neo4j/config.yml +1 -1
  4. data/lib/db/active_tx_log +1 -0
  5. data/lib/db/index/lucene-store.db +0 -0
  6. data/lib/db/index/lucene.log.active +0 -0
  7. data/lib/db/messages.log +2299 -0
  8. data/lib/db/neostore +0 -0
  9. data/lib/db/neostore.id +0 -0
  10. data/lib/db/neostore.nodestore.db +0 -0
  11. data/lib/db/neostore.nodestore.db.id +0 -0
  12. data/lib/db/neostore.propertystore.db +0 -0
  13. data/lib/db/neostore.propertystore.db.arrays +0 -0
  14. data/lib/db/neostore.propertystore.db.arrays.id +0 -0
  15. data/lib/db/neostore.propertystore.db.id +0 -0
  16. data/lib/db/neostore.propertystore.db.index +0 -0
  17. data/lib/db/neostore.propertystore.db.index.id +0 -0
  18. data/lib/db/neostore.propertystore.db.index.keys +0 -0
  19. data/lib/db/neostore.propertystore.db.index.keys.id +0 -0
  20. data/lib/db/neostore.propertystore.db.strings +0 -0
  21. data/lib/db/neostore.propertystore.db.strings.id +0 -0
  22. data/lib/db/neostore.relationshipstore.db +0 -0
  23. data/lib/db/neostore.relationshipstore.db.id +0 -0
  24. data/lib/db/neostore.relationshiptypestore.db +0 -0
  25. data/lib/db/neostore.relationshiptypestore.db.id +0 -0
  26. data/lib/db/neostore.relationshiptypestore.db.names +0 -0
  27. data/lib/db/neostore.relationshiptypestore.db.names.id +0 -0
  28. data/lib/db/nioneo_logical.log.active +0 -0
  29. data/lib/db/tm_tx_log.1 +0 -0
  30. data/lib/neo4j-core.rb +20 -3
  31. data/lib/neo4j-core/cypher/cypher.rb +1033 -0
  32. data/lib/neo4j-core/cypher/result_wrapper.rb +48 -0
  33. data/lib/neo4j-core/database.rb +4 -5
  34. data/lib/neo4j-core/event_handler.rb +1 -1
  35. data/lib/neo4j-core/hash_with_indifferent_access.rb +165 -0
  36. data/lib/neo4j-core/index/class_methods.rb +27 -41
  37. data/lib/neo4j-core/index/index.rb +3 -4
  38. data/lib/neo4j-core/index/index_config.rb +30 -23
  39. data/lib/neo4j-core/index/indexer.rb +65 -53
  40. data/lib/neo4j-core/index/indexer_registry.rb +2 -2
  41. data/lib/neo4j-core/index/lucene_query.rb +53 -42
  42. data/lib/neo4j-core/index/unique_factory.rb +54 -0
  43. data/lib/neo4j-core/node/class_methods.rb +4 -4
  44. data/lib/neo4j-core/node/node.rb +1 -8
  45. data/lib/neo4j-core/property/java.rb +59 -0
  46. data/lib/neo4j-core/property/property.rb +1 -3
  47. data/lib/neo4j-core/relationship/relationship.rb +8 -10
  48. data/lib/neo4j-core/rels/rels.rb +9 -4
  49. data/lib/neo4j-core/rels/traverser.rb +13 -27
  50. data/lib/neo4j-core/traversal/prune_evaluator.rb +2 -2
  51. data/lib/neo4j-core/traversal/traverser.rb +122 -27
  52. data/lib/neo4j-core/version.rb +1 -1
  53. data/lib/neo4j-core/wrapper/class_methods.rb +22 -0
  54. data/lib/neo4j-core/wrapper/wrapper.rb +20 -0
  55. data/lib/neo4j/algo.rb +300 -0
  56. data/lib/neo4j/config.rb +3 -6
  57. data/lib/neo4j/cypher.rb +180 -0
  58. data/lib/neo4j/neo4j.rb +51 -23
  59. data/lib/neo4j/node.rb +27 -0
  60. data/lib/neo4j/relationship.rb +25 -0
  61. data/lib/test.rb~ +27 -0
  62. data/neo4j-core.gemspec +2 -2
  63. metadata +44 -11
  64. data/lib/neo4j-core/type_converters/type_converters.rb +0 -287
  65. data/lib/neo4j-core/version.rb~ +0 -3
  66. 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
 
@@ -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