neo4j 1.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/CHANGELOG +141 -0
  2. data/CONTRIBUTORS +15 -0
  3. data/Gemfile +3 -0
  4. data/README.rdoc +2015 -0
  5. data/lib/neo4j.old/batch_inserter.rb +144 -0
  6. data/lib/neo4j.old/config.rb +138 -0
  7. data/lib/neo4j.old/event_handler.rb +73 -0
  8. data/lib/neo4j.old/extensions/activemodel.rb +158 -0
  9. data/lib/neo4j.old/extensions/aggregate.rb +12 -0
  10. data/lib/neo4j.old/extensions/aggregate/aggregate_enum.rb +40 -0
  11. data/lib/neo4j.old/extensions/aggregate/ext/node_mixin.rb +69 -0
  12. data/lib/neo4j.old/extensions/aggregate/node_aggregate.rb +8 -0
  13. data/lib/neo4j.old/extensions/aggregate/node_aggregate_mixin.rb +331 -0
  14. data/lib/neo4j.old/extensions/aggregate/node_aggregator.rb +216 -0
  15. data/lib/neo4j.old/extensions/aggregate/node_group.rb +43 -0
  16. data/lib/neo4j.old/extensions/aggregate/prop_group.rb +30 -0
  17. data/lib/neo4j.old/extensions/aggregate/property_enum.rb +24 -0
  18. data/lib/neo4j.old/extensions/aggregate/props_aggregate.rb +8 -0
  19. data/lib/neo4j.old/extensions/aggregate/props_aggregate_mixin.rb +31 -0
  20. data/lib/neo4j.old/extensions/aggregate/props_aggregator.rb +80 -0
  21. data/lib/neo4j.old/extensions/find_path.rb +117 -0
  22. data/lib/neo4j.old/extensions/graph_algo.rb +1 -0
  23. data/lib/neo4j.old/extensions/graph_algo/all_simple_paths.rb +133 -0
  24. data/lib/neo4j.old/extensions/graph_algo/neo4j-graph-algo-0.3.jar +0 -0
  25. data/lib/neo4j.old/extensions/reindexer.rb +104 -0
  26. data/lib/neo4j.old/extensions/rest.rb +21 -0
  27. data/lib/neo4j.old/extensions/rest/rest.rb +336 -0
  28. data/lib/neo4j.old/extensions/rest/rest_mixin.rb +193 -0
  29. data/lib/neo4j.old/extensions/rest/server.rb +50 -0
  30. data/lib/neo4j.old/extensions/rest/stubs.rb +141 -0
  31. data/lib/neo4j.old/extensions/rest_master.rb +34 -0
  32. data/lib/neo4j.old/extensions/rest_slave.rb +31 -0
  33. data/lib/neo4j.old/extensions/tx_tracker.rb +392 -0
  34. data/lib/neo4j.old/indexer.rb +187 -0
  35. data/lib/neo4j.old/jars.rb +6 -0
  36. data/lib/neo4j.old/jars/geronimo-jta_1.1_spec-1.1.1.jar +0 -0
  37. data/lib/neo4j.old/jars/neo4j-kernel-1.0.jar +0 -0
  38. data/lib/neo4j.old/mixins/java_list_mixin.rb +139 -0
  39. data/lib/neo4j.old/mixins/java_node_mixin.rb +205 -0
  40. data/lib/neo4j.old/mixins/java_property_mixin.rb +169 -0
  41. data/lib/neo4j.old/mixins/java_relationship_mixin.rb +60 -0
  42. data/lib/neo4j.old/mixins/migration_mixin.rb +157 -0
  43. data/lib/neo4j.old/mixins/node_mixin.rb +249 -0
  44. data/lib/neo4j.old/mixins/property_class_methods.rb +265 -0
  45. data/lib/neo4j.old/mixins/rel_class_methods.rb +167 -0
  46. data/lib/neo4j.old/mixins/relationship_mixin.rb +103 -0
  47. data/lib/neo4j.old/neo.rb +247 -0
  48. data/lib/neo4j.old/node.rb +49 -0
  49. data/lib/neo4j.old/reference_node.rb +15 -0
  50. data/lib/neo4j.old/relationship.rb +85 -0
  51. data/lib/neo4j.old/relationships/decl_relationship_dsl.rb +164 -0
  52. data/lib/neo4j.old/relationships/has_list.rb +101 -0
  53. data/lib/neo4j.old/relationships/has_n.rb +129 -0
  54. data/lib/neo4j.old/relationships/node_traverser.rb +138 -0
  55. data/lib/neo4j.old/relationships/relationship_dsl.rb +149 -0
  56. data/lib/neo4j.old/relationships/traversal_position.rb +50 -0
  57. data/lib/neo4j.old/relationships/wrappers.rb +51 -0
  58. data/lib/neo4j.old/search_result.rb +72 -0
  59. data/lib/neo4j.old/transaction.rb +254 -0
  60. data/lib/neo4j.old/version.rb +3 -0
  61. data/lib/neo4j.rb +50 -0
  62. data/lib/neo4j/config.rb +137 -0
  63. data/lib/neo4j/database.rb +43 -0
  64. data/lib/neo4j/equal.rb +22 -0
  65. data/lib/neo4j/event_handler.rb +91 -0
  66. data/lib/neo4j/index.rb +157 -0
  67. data/lib/neo4j/jars/geronimo-jta_1.1_spec-1.1.1.jar +0 -0
  68. data/lib/neo4j/jars/lucene-core-2.9.2.jar +0 -0
  69. data/lib/neo4j/jars/lucene-core-3.0.1.jar +0 -0
  70. data/lib/neo4j/jars/neo4j-index-1.1.jar +0 -0
  71. data/lib/neo4j/jars/neo4j-kernel-1.1.1.jar +0 -0
  72. data/lib/neo4j/jars/neo4j-kernel-1.1.jar +0 -0
  73. data/lib/neo4j/jars/neo4j-lucene-index-0.1-20100916.085626-67.jar +0 -0
  74. data/lib/neo4j/mapping/class_methods/index.rb +21 -0
  75. data/lib/neo4j/mapping/class_methods/property.rb +139 -0
  76. data/lib/neo4j/mapping/class_methods/relationship.rb +96 -0
  77. data/lib/neo4j/mapping/class_methods/rule.rb +135 -0
  78. data/lib/neo4j/mapping/decl_relationship_dsl.rb +151 -0
  79. data/lib/neo4j/mapping/has_n.rb +117 -0
  80. data/lib/neo4j/mapping/node_mixin.rb +70 -0
  81. data/lib/neo4j/neo4j.rb +65 -0
  82. data/lib/neo4j/node.rb +82 -0
  83. data/lib/neo4j/node_mixin.rb +4 -0
  84. data/lib/neo4j/node_relationship.rb +60 -0
  85. data/lib/neo4j/node_traverser.rb +141 -0
  86. data/lib/neo4j/property.rb +72 -0
  87. data/lib/neo4j/rails/lucene_connection_closer.rb +19 -0
  88. data/lib/neo4j/rails/model.rb +210 -0
  89. data/lib/neo4j/rails/railtie.rb +16 -0
  90. data/lib/neo4j/rails/transaction.rb +29 -0
  91. data/lib/neo4j/rails/value.rb +43 -0
  92. data/lib/neo4j/relationship.rb +88 -0
  93. data/lib/neo4j/relationship_traverser.rb +57 -0
  94. data/lib/neo4j/to_java.rb +17 -0
  95. data/lib/neo4j/transaction.rb +69 -0
  96. data/lib/neo4j/version.rb +3 -0
  97. data/neo4j.gemspec +30 -0
  98. metadata +243 -0
@@ -0,0 +1,216 @@
1
+ module Neo4j::Aggregate
2
+ # Used to create a DSL describing how to aggregate an enumeration of nodes
3
+ #
4
+ # :api: public
5
+ class NodeAggregator
6
+ attr_accessor :root_dsl
7
+
8
+ def initialize(root_node, dsl_nodes_or_filter)
9
+ @root_node = root_node
10
+ self.root_dsl = self #if not chained dsl then the root dsl is self
11
+
12
+ if dsl_nodes_or_filter.kind_of?(self.class)
13
+ # we are chaining aggregates
14
+ @child_dsl = dsl_nodes_or_filter
15
+ @child_dsl.root_dsl = self # the child has a pointer to the parent
16
+ elsif dsl_nodes_or_filter.kind_of?(Enumerable)
17
+ # we are aggregating an enumerable set of nodes
18
+ @nodes = dsl_nodes_or_filter
19
+ elsif (dsl_nodes_or_filter.kind_of?(Class) and dsl_nodes_or_filter.ancestors.include?(Neo4j::NodeMixin))
20
+ # We are listening for events on Neo4j nodes - that will be included in the aggregates
21
+ @filter = dsl_nodes_or_filter
22
+ # Register with the Neo4j event handler
23
+ Neo4j.event_handler.add(self)
24
+ end
25
+
26
+ end
27
+
28
+
29
+ # Unregisters this aggregate so that it will not be notified any longer
30
+ # on Neo4j node events. Used when we create an aggregate that is registered
31
+ # with the Neo4j even listener by including a filter in the aggregate method
32
+ #
33
+ # ==== Example
34
+ # agg_reg = my_aggregate.aggregate(MyNode).group_by(:something)
35
+ # # add some MyNodes that my_aggregate will aggregate into groups
36
+ # MyNode.new # etc...
37
+ # # we now do not want to add more nodes using the aggregate above - unregister it
38
+ # agg_reg.unregister
39
+ # # no more nodes will be appended /deleted /modified in the my_aggregate.
40
+ #
41
+ def unregister
42
+ Neo4j.event_handler.remove(self)
43
+ end
44
+
45
+ def to_s
46
+ "Aggregator group_by #{@group_by} filter #{!@filter.nil?} object_id: #{self.object_id} child: #{!@child_dsl.nil?}"
47
+ end
48
+
49
+
50
+ # called from neo4j event handler
51
+ # :api: private
52
+ def on_property_changed(node, prop_key, old_value, new_value) # :nodoc:
53
+ return if node.class != @filter
54
+ return unless @group_by.include?(prop_key.to_sym)
55
+ old_node = node.props
56
+ old_node[prop_key] = old_value
57
+ root_dsl.on_prop_added(node, node, old_node)
58
+ on_prop_deleted(node, node, old_node)
59
+ end
60
+
61
+ # called from neo4j event handler
62
+ # :api: private
63
+ def on_node_deleted(node) # :nodoc:
64
+ return if node.class != @filter
65
+ # node.print(:incoming, 2)
66
+ node.rels.incoming(:aggregate).filter{start_node.property? :aggregate_size}.each do |group_rel|
67
+ group_node = group_rel.start_node
68
+ group_node.aggregate_size -= 1
69
+ # should we delete the whole group ?
70
+ delete_group(group_node) if (group_node.aggregate_size == 0)
71
+ end
72
+ end
73
+
74
+ def delete_group(group_node) # :nodoc:
75
+ # get parent aggregates and decrease the aggregate size
76
+ group_node.rels.incoming.nodes.each do |parent_group|
77
+ next unless parent_group.respond_to? :aggregate_size
78
+ parent_group[:aggregate_size] -= 1
79
+ delete_group(parent_group) if parent_group[:aggregate_size] == 0
80
+ end
81
+ group_node.del
82
+ end
83
+
84
+
85
+ def on_prop_deleted(node, curr_node_values, old_node_values) # :nodoc:
86
+ old_group_keys = group_key_of(old_node_values)
87
+ new_group_keys = group_key_of(curr_node_values)
88
+
89
+ # keys that are removed
90
+ removed = old_group_keys - new_group_keys
91
+
92
+ removed.each do |key|
93
+ member_of = [*node.rels.incoming(:aggregate).filter{self[:aggregate_group] == key}]
94
+ raise "same group key used in several aggregate groups, strange #{member_of.size}" if member_of.size > 1
95
+ next if member_of.empty?
96
+ group_node = member_of[0].start_node
97
+ group_node.aggregate_size -= 1
98
+ member_of[0].delete
99
+
100
+ # should we delete the whole group
101
+ delete_group(group_node) if (group_node.aggregate_size == 0)
102
+ end
103
+
104
+ end
105
+
106
+ def on_prop_added(node, curr_node_values, old_node_values) # :nodoc:
107
+ old_group_keys = group_key_of(old_node_values)
108
+ new_group_keys = group_key_of(curr_node_values)
109
+
110
+ # keys that are added
111
+ added = new_group_keys - old_group_keys
112
+ added.each { |key| root_dsl.create_group_for_key(@root_node, node, key) }
113
+ end
114
+
115
+
116
+ # Specifies which properties we should group on.
117
+ # All thos properties can be combined to create a new group.
118
+ #
119
+ # :api: public
120
+ def group_by(*keys)
121
+ @group_by = keys
122
+ self
123
+ end
124
+
125
+
126
+ # Maps the values of the given properties (in group_by or group_by_each).
127
+ # If this method is not used the group name will be the same as the property value.
128
+ #
129
+ # :api: public
130
+ def map_value(&map_func)
131
+ @map_func = map_func
132
+ self
133
+ end
134
+
135
+ # Create a group key for given node
136
+ # :api: private
137
+ def group_key_of(node)
138
+ values = @group_by.map{|key| node[key.to_s]}
139
+
140
+ # are there any keys ?
141
+ return [] if values.to_s.empty?
142
+
143
+ # should we map the values ?
144
+ if !@map_func.nil?
145
+ raise "Wrong number of argument of map_value function, expected #{values.size} args but it takes #{@map_func.arity} args" if @map_func.arity != values.size
146
+ values = @map_func.call(*values)
147
+ values = [values] unless values.kind_of? Enumerable
148
+ end
149
+
150
+
151
+ # check all values and expand enumerable values
152
+ group_keys = [*values.inject(Set.new) do |result, value|
153
+ if value.respond_to?(:to_a)
154
+ result.merge([*value])
155
+ else
156
+ result << value
157
+ end
158
+ end]
159
+
160
+ # if we are not grouping by_each then there will only be one group_key - join it
161
+ group_keys = [group_keys] unless group_keys.respond_to?(:each)
162
+ group_keys
163
+ end
164
+
165
+ # Executes the DSL and creates the specified groups.
166
+ # This method is not necessary to call, since it will automatically be called when needed.
167
+ #
168
+ # :api: public
169
+ def execute(nodes = @nodes)
170
+ return if nodes.nil?
171
+
172
+ # prevent execute to execute again with the same nodes
173
+ @nodes = nil
174
+
175
+ nodes.each { |node| root_dsl.create_groups(@root_node, node) }
176
+ end
177
+
178
+ # :api: private
179
+ def create_groups(parent, node)
180
+ group_key_of(node).each { |key| create_group_for_key(parent, node, key) }
181
+ end
182
+
183
+ # :api: private
184
+ def create_group_for_key(parent, node, key)
185
+ # find a group node for the given key
186
+ group_node = parent.rels.outgoing(key).nodes.find{|n| n.kind_of? NodeGroup}
187
+
188
+ # if no group key is found create a new one
189
+ group_node ||= create_group_node(parent, key)
190
+
191
+ # check if it is the leaf node or not
192
+ if (@child_dsl)
193
+ # this is not the leaf aggregate dsl, let the child node add the node instead
194
+ @child_dsl.create_groups(group_node, node)
195
+ else
196
+ # this IS a leaf aggregate dsl, add node to the group
197
+ rel_type = node.kind_of?(NodeGroup)? key : :aggregate
198
+ rel = group_node.add_rel(rel_type, node)
199
+ rel[:aggregate_group] = key
200
+ # increase the size counter on this group
201
+ group_node.aggregate_size += 1
202
+ end
203
+ end
204
+
205
+ # :api: private
206
+ def create_group_node(parent, key)
207
+ new_node = NodeGroup.create(key)
208
+ rel = parent.add_rel(key, new_node)
209
+ parent.aggregate_size += 1 # another group was created
210
+ rel[:aggregate_group] = key
211
+ new_node
212
+ end
213
+
214
+ end
215
+
216
+ end
@@ -0,0 +1,43 @@
1
+ module Neo4j::Aggregate
2
+
3
+ # This is the group node. When a new aggregate group is created it will be of this type.
4
+ # Includes the Enumerable mixin in order to iterator over each node member in the group.
5
+ # Overrides [] and []= properties, so that we can access aggregated properties or relationships.
6
+ #
7
+ # :api: private
8
+ class NodeGroup #:nodoc:
9
+ include Neo4j::NodeMixin
10
+ include Enumerable
11
+
12
+ property :aggregate_group, :aggregate_size
13
+
14
+ def self.create(aggregate_group)
15
+ new_node = NodeGroup.new
16
+ new_node.aggregate_group = aggregate_group.kind_of?(Symbol)? aggregate_group.to_s : aggregate_group
17
+ new_node.aggregate_size = 0
18
+ new_node
19
+ end
20
+
21
+ def each
22
+ rels.outgoing.nodes.each { |n| yield n }
23
+ end
24
+
25
+ # :api: private
26
+ def [](key)
27
+ value = super(key)
28
+ return value unless value.nil?
29
+
30
+ sub_group = rels.outgoing(key).nodes.first
31
+ return sub_group unless sub_group.nil?
32
+
33
+ # traverse all sub nodes and get their properties
34
+ PropertyEnum.new(rels.outgoing.nodes, key)
35
+ end
36
+
37
+ # def []=(key, value)
38
+ # super key, value
39
+ # self.get_property(key)
40
+ # end
41
+ end
42
+
43
+ end
@@ -0,0 +1,30 @@
1
+ module Neo4j::Aggregate
2
+
3
+ class PropGroup
4
+ include Neo4j::NodeMixin
5
+ include Enumerable
6
+
7
+ has_one :aggregate, :cascade_delete => :incoming
8
+ property :aggregate_group, :aggregate_size, :group_by
9
+
10
+ def each
11
+ group_by.split(',').each do |group|
12
+ yield aggregate[group]
13
+ end
14
+ end
15
+
16
+ # :api: private
17
+ def get_property(key)
18
+ value = super(key)
19
+ return value unless value.nil?
20
+ return nil unless aggregate
21
+ aggregate[key]
22
+ end
23
+
24
+ def ignore_incoming_cascade_delete? (relationship)
25
+ super || relationship.start_node.kind_of?(Neo4j::Aggregate::PropsAggregate)
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,24 @@
1
+ # Used to aggregate property values.
2
+ #
3
+ # :api: private
4
+ class Neo4j::Aggregate::PropertyEnum #:nodoc:
5
+ include Enumerable
6
+
7
+ def initialize(nodes, property)
8
+ @nodes = nodes
9
+ @property = property
10
+ end
11
+
12
+ def each
13
+ @nodes.each do |n|
14
+ v = n[@property]
15
+ if v.kind_of?(Enumerable)
16
+ v.each {|vv| yield vv}
17
+ else
18
+ yield v
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,8 @@
1
+ module Neo4j::Aggregate
2
+
3
+ class PropsAggregate
4
+ include Neo4j::NodeMixin
5
+ include Neo4j::Aggregate::PropsAggregateMixin
6
+ end
7
+
8
+ end
@@ -0,0 +1,31 @@
1
+ module Neo4j::Aggregate
2
+
3
+ # Rules properties on one or more nodes.
4
+ # Can also be used to apply functions (e.g. sum/average) on a set of properties.
5
+ #
6
+ module PropsAggregateMixin
7
+ include Neo4j::NodeMixin
8
+ include Enumerable
9
+
10
+ has_list :groups, :counter => true #, :cascade_delete => :incoming
11
+
12
+ def init_node(*args)
13
+ @aggregate_id = args[0] unless args.empty?
14
+ end
15
+
16
+ def aggregate_size
17
+ @aggregator.execute if @aggregator
18
+ groups.size
19
+ end
20
+
21
+ def each
22
+ @aggregator.execute if @aggregator
23
+ groups.each {|sub_group| sub_group.each {|val| yield val}}
24
+ end
25
+
26
+ def aggregate(agg_id)
27
+ @aggregator = PropsAggregator.new(self, agg_id.to_s)
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,80 @@
1
+ module Neo4j::Aggregate
2
+ class PropsAggregator
3
+ def initialize(root_node, agg_id)
4
+ @root_node = root_node
5
+ @agg_id = agg_id
6
+ end
7
+
8
+
9
+ def on(nodes_or_class)
10
+ if (nodes_or_class.kind_of?(Class) and nodes_or_class.ancestors.include?(Neo4j::NodeMixin))
11
+ Neo4j.event_handler.add(self)
12
+ @filter = nodes_or_class
13
+ @nodes = nodes_or_class
14
+ elsif (!nodes_or_class.respond_to?(:each))
15
+ @nodes = [nodes_or_class]
16
+ else
17
+ @nodes = nodes_or_class
18
+ end
19
+ self
20
+ end
21
+
22
+ def props(*properties)
23
+ @group_by = properties
24
+ self
25
+ end
26
+
27
+ # Unregisters this aggregate so that it will not be notified any longer
28
+ # on Neo4j node events. Used when we create an aggregate that is registered
29
+ # with the Neo4j even listener by including a filter in the aggregate method
30
+ #
31
+ # ==== Example
32
+ # agg_reg = my_aggregate.aggregate_each(MyNode).group_by(:something)
33
+ # # add some MyNodes that my_aggregate will aggregate into groups
34
+ # MyNode.new # etc...
35
+ # # we now do not want to add more nodes using the aggregate above - unregister it
36
+ # agg_reg.unregister
37
+ # # no more nodes will be appended /deleted /modified in the my_aggregate.
38
+ #
39
+ def unregister
40
+ Neo4j.event_handler.remove(self)
41
+ end
42
+
43
+ # called from neo4j event handler
44
+ # :api: private
45
+ def on_property_changed(node, prop_key, old_value, new_value) # :nodoc:
46
+ return if node.class != @filter
47
+ return unless @group_by.include?(prop_key.to_sym)
48
+
49
+ # recreate the aggregate group
50
+ execute([node])
51
+ end
52
+
53
+
54
+ def with(prop_key, &proc)
55
+ @with_proc = proc
56
+ @prop_key = prop_key
57
+ self
58
+ end
59
+
60
+ def execute(nodes = @nodes)
61
+ return unless nodes
62
+ nodes.each do |node|
63
+ group_node = node.aggregate_groups(@agg_id)
64
+ if group_node.nil?
65
+ group_node = PropGroup.new
66
+ group_node.group_by = @group_by.join(',')
67
+ group_node.aggregate = node
68
+ rel = group_node.rels.outgoing(:aggregate)[node]
69
+ rel[:aggregate_group] = @agg_id
70
+ @root_node.groups << group_node
71
+ end
72
+ if @with_proc
73
+ val = group_node.inject(0) {|sum, val| next sum if val.nil?; @with_proc.call(sum, val, 0)}
74
+ group_node[@prop_key.to_s] = val
75
+ end
76
+ end
77
+ @nodes = nil # prevent it to run twice
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,117 @@
1
+ # Extension which finds the shortest path (in terms of number of links) between
2
+ # two nodes. Use something like this:
3
+ #
4
+ # require 'neo4j/extensions/find_path'
5
+ # node1.traverse.both(:knows).depth(:all).path_to(node2)
6
+ # # => [node1, node42, node1234, node256, node2]
7
+ #
8
+ # This extension is still rather experimental. The algorithm is based on the one
9
+ # used in the Neo4j Java IMDB example.
10
+ #
11
+ # Martin Kleppmann, July 2009
12
+ module Neo4j
13
+ module Relationships
14
+ class NodeTraverser
15
+
16
+ attr_accessor :predecessor_map, :other_traverser, :returnable_evaluator
17
+ attr_writer :_java_node
18
+
19
+ # Finds a path by starting a breadth-first traverser from each end and stopping as soon
20
+ # as the two meet. Keeps a hash of current_node -> previous_node which allows us to
21
+ # reconstruct the path that was taken once we meet. Returns an array with all of the
22
+ # nodes constituting the path from +self+ to +other_node+ (inclusive), or +nil+ if no
23
+ # path could be found.
24
+ def path_to(other_node)
25
+ return [] if other_node._java_node.neo_id == self._java_node.neo_id
26
+ self.other_traverser = clone
27
+ self.other_traverser._java_node = other_node._java_node
28
+ self.other_traverser.other_traverser = self
29
+ self.other_traverser.prepare_path_search
30
+ self.other_traverser.swap_directions
31
+ self.prepare_path_search
32
+
33
+ while true # Advance the traversers in alternation
34
+ break if self.search_for_path
35
+ break if other_traverser.search_for_path
36
+ end
37
+
38
+ path = returnable_evaluator.found_path
39
+ if !path && other_traverser.returnable_evaluator.found_path
40
+ path = other_traverser.returnable_evaluator.found_path.reverse
41
+ end
42
+ path
43
+ end
44
+
45
+ # :nodoc:
46
+ def path_traceback(end_node)
47
+ # Reconstructs the chain of predecessors leading up to +end_node+. For internal use only.
48
+ path = []
49
+ while end_node = predecessor_map[end_node]
50
+ path.unshift end_node
51
+ end
52
+ path
53
+ end
54
+
55
+ # :nodoc:
56
+ def prepare_path_search
57
+ self.returnable_evaluator = FindPathEvaluator.new(self, returnable_evaluator)
58
+ self.predecessor_map = {Neo4j.load_node(_java_node.neo_id) => nil}
59
+ end
60
+
61
+ # :nodoc:
62
+ def swap_directions
63
+ @types_and_dirs = @types_and_dirs.map do |item|
64
+ case item
65
+ when org.neo4j.graphdb.Direction::INCOMING
66
+ org.neo4j.graphdb.Direction::OUTGOING
67
+ when org.neo4j.graphdb.Direction::OUTGOING
68
+ org.neo4j.graphdb.Direction::INCOMING
69
+ else
70
+ item
71
+ end
72
+ end
73
+ end
74
+
75
+ # Advances this traverser's path search by one step. Returns +false+ if there is still
76
+ # more to do and +true+ if finished (irrespective of whether or not a result was found).
77
+ def search_for_path
78
+ @path_iterator ||= iterator
79
+ return true unless @path_iterator.hasNext
80
+ node = @path_iterator.next
81
+ !!returnable_evaluator.found_path # !! forces type to boolean
82
+ end
83
+ end
84
+
85
+
86
+ class FindPathEvaluator
87
+ include org.neo4j.graphdb.ReturnableEvaluator
88
+
89
+ attr_accessor :traverser, :original_evaluator, :found_path
90
+
91
+ def initialize(traverser, original_evaluator)
92
+ @traverser = traverser
93
+ @original_evaluator = original_evaluator
94
+ end
95
+
96
+ # Called at each traversal position while searching for a path. Records the current node
97
+ # and its predecessor (for generating the traceback) and checks whether the two traversers
98
+ # have met.
99
+ def isReturnableNode(traversal_position)
100
+ return false unless original_evaluator.isReturnableNode(traversal_position)
101
+
102
+ current = Neo4j.load_node(traversal_position.current_node.getId)
103
+ previous = Neo4j.load_node(traversal_position.previous_node.getId) unless traversal_position.previous_node.nil?
104
+ traverser.predecessor_map[current] = previous
105
+
106
+ if traverser.other_traverser.predecessor_map.include? current
107
+ # Yay, found a path!
108
+ @found_path = traverser.path_traceback(current)
109
+ @found_path << current
110
+ @found_path += traverser.other_traverser.path_traceback(current).reverse
111
+ end
112
+
113
+ true
114
+ end
115
+ end
116
+ end
117
+ end