neo4j 1.0.0.beta.9 → 1.0.0.beta.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/README.rdoc +5 -1971
  2. data/lib/neo4j/mapping/class_methods/relationship.rb +2 -2
  3. data/lib/neo4j/mapping/decl_relationship_dsl.rb +6 -1
  4. data/lib/neo4j/mapping/has_n.rb +17 -1
  5. data/lib/neo4j/node_traverser.rb +14 -1
  6. data/lib/neo4j/rails/model.rb +127 -31
  7. data/lib/neo4j/rails/tx_methods.rb +11 -0
  8. data/lib/neo4j/rails/value.rb +44 -1
  9. data/lib/neo4j/version.rb +1 -1
  10. data/lib/neo4j.rb +1 -0
  11. metadata +4 -59
  12. data/lib/neo4j.old/batch_inserter.rb +0 -144
  13. data/lib/neo4j.old/config.rb +0 -138
  14. data/lib/neo4j.old/event_handler.rb +0 -73
  15. data/lib/neo4j.old/extensions/activemodel.rb +0 -158
  16. data/lib/neo4j.old/extensions/aggregate/aggregate_enum.rb +0 -40
  17. data/lib/neo4j.old/extensions/aggregate/ext/node_mixin.rb +0 -69
  18. data/lib/neo4j.old/extensions/aggregate/node_aggregate.rb +0 -8
  19. data/lib/neo4j.old/extensions/aggregate/node_aggregate_mixin.rb +0 -331
  20. data/lib/neo4j.old/extensions/aggregate/node_aggregator.rb +0 -216
  21. data/lib/neo4j.old/extensions/aggregate/node_group.rb +0 -43
  22. data/lib/neo4j.old/extensions/aggregate/prop_group.rb +0 -30
  23. data/lib/neo4j.old/extensions/aggregate/property_enum.rb +0 -24
  24. data/lib/neo4j.old/extensions/aggregate/props_aggregate.rb +0 -8
  25. data/lib/neo4j.old/extensions/aggregate/props_aggregate_mixin.rb +0 -31
  26. data/lib/neo4j.old/extensions/aggregate/props_aggregator.rb +0 -80
  27. data/lib/neo4j.old/extensions/aggregate.rb +0 -12
  28. data/lib/neo4j.old/extensions/find_path.rb +0 -117
  29. data/lib/neo4j.old/extensions/graph_algo/all_simple_paths.rb +0 -133
  30. data/lib/neo4j.old/extensions/graph_algo/neo4j-graph-algo-0.3.jar +0 -0
  31. data/lib/neo4j.old/extensions/graph_algo.rb +0 -1
  32. data/lib/neo4j.old/extensions/reindexer.rb +0 -104
  33. data/lib/neo4j.old/extensions/rest/rest.rb +0 -336
  34. data/lib/neo4j.old/extensions/rest/rest_mixin.rb +0 -193
  35. data/lib/neo4j.old/extensions/rest/server.rb +0 -50
  36. data/lib/neo4j.old/extensions/rest/stubs.rb +0 -141
  37. data/lib/neo4j.old/extensions/rest.rb +0 -21
  38. data/lib/neo4j.old/extensions/rest_master.rb +0 -34
  39. data/lib/neo4j.old/extensions/rest_slave.rb +0 -31
  40. data/lib/neo4j.old/extensions/tx_tracker.rb +0 -392
  41. data/lib/neo4j.old/indexer.rb +0 -187
  42. data/lib/neo4j.old/jars/geronimo-jta_1.1_spec-1.1.1.jar +0 -0
  43. data/lib/neo4j.old/jars/neo4j-kernel-1.0.jar +0 -0
  44. data/lib/neo4j.old/jars.rb +0 -6
  45. data/lib/neo4j.old/mixins/java_list_mixin.rb +0 -139
  46. data/lib/neo4j.old/mixins/java_node_mixin.rb +0 -205
  47. data/lib/neo4j.old/mixins/java_property_mixin.rb +0 -169
  48. data/lib/neo4j.old/mixins/java_relationship_mixin.rb +0 -60
  49. data/lib/neo4j.old/mixins/migration_mixin.rb +0 -157
  50. data/lib/neo4j.old/mixins/node_mixin.rb +0 -249
  51. data/lib/neo4j.old/mixins/property_class_methods.rb +0 -265
  52. data/lib/neo4j.old/mixins/rel_class_methods.rb +0 -167
  53. data/lib/neo4j.old/mixins/relationship_mixin.rb +0 -103
  54. data/lib/neo4j.old/neo.rb +0 -247
  55. data/lib/neo4j.old/node.rb +0 -49
  56. data/lib/neo4j.old/reference_node.rb +0 -15
  57. data/lib/neo4j.old/relationship.rb +0 -85
  58. data/lib/neo4j.old/relationships/decl_relationship_dsl.rb +0 -164
  59. data/lib/neo4j.old/relationships/has_list.rb +0 -101
  60. data/lib/neo4j.old/relationships/has_n.rb +0 -129
  61. data/lib/neo4j.old/relationships/node_traverser.rb +0 -138
  62. data/lib/neo4j.old/relationships/relationship_dsl.rb +0 -149
  63. data/lib/neo4j.old/relationships/traversal_position.rb +0 -50
  64. data/lib/neo4j.old/relationships/wrappers.rb +0 -51
  65. data/lib/neo4j.old/search_result.rb +0 -72
  66. data/lib/neo4j.old/transaction.rb +0 -254
  67. data/lib/neo4j.old/version.rb +0 -3
@@ -1,331 +0,0 @@
1
- module Neo4j::Aggregate
2
-
3
-
4
-
5
- # Enables aggregation of nodes into groups.
6
- # An aggregation is a node which in contains group nodes.
7
- # A group node aggregates the properties of the nodes that belongs to its group.
8
- #
9
- # There are two ways of creating an aggregate.
10
- # * Providing an enumeration of nodes.
11
- # * Register a Node class. All nodes of this class will (can) be part of the aggregate.
12
- #
13
- # One example of usage of providing an enumeration of nodes is by taking the output from
14
- # the Neo4j::NodeMixin#traverse as input to the aggregate method, or even
15
- # create aggregates over aggregates.
16
- #
17
- # This mixin includes the Enumerable mixin.
18
- #
19
- # There is also a different aggregation which aggregates on properties
20
- # instead of nodes - Neo4j::Aggregate::PropsAggregateMixin
21
- #
22
- # ==== Example - group by one property
23
- #
24
- # Let say we have nodes with properties :colour and we want to group them by colour:
25
- #
26
- # a = AggregateNode.new
27
- #
28
- # a.aggregate(nodes).group_by(:colour).execute
29
- #
30
- # The execute method is only needed when providing nodes (instead of a NodeClass) for the aggregate method.
31
- # Print all three groups, one for each colour
32
- #
33
- # a.each{|n| puts n[:colour]}
34
- #
35
- # Print all nodes belonging to one colour group:
36
- #
37
- # a[:red].each {|node| puts node}
38
- #
39
- # ==== Example - Aggregating Properties
40
- #
41
- # The aggregator also aggregate properties. If a property does not exist on an aggregated group it will traverse all nodes in its group and
42
- # return an enumeration of its values.
43
- #
44
- # Get an enumeration of names of people having favorite colour 'red'
45
- #
46
- # [*a[:red][:name]] => ['bertil', 'adam', 'adam']
47
- #
48
- # ==== Example - group by a property value which is transformed
49
- #
50
- # Let say way want to have group which include a range of values.
51
- # Example - group by an age range, 0-4, 5-9, 10-14 etc...
52
- #
53
- # a = AggregateNode.new
54
- # a.aggregate(an enumeration of nodes).group_by(:age).of_value{|age| age / 5}
55
- #
56
- # # traverse all people in age group 10-14 (3 maps to range 10-14)
57
- # a[3].each {|x| ...}
58
- #
59
- # # traverse all groups
60
- # a.each {|x| ...}
61
- #
62
- # # how many age groups are there ?
63
- # a.aggregate_size
64
- #
65
- # # how many people are in age group 10-14
66
- # a[3].aggregate_size
67
- #
68
- # ==== Example - Group by several properties
69
- #
70
- # The group_by method takes one or more property keys which it combines into one or more groups.
71
- #
72
- # node1 = Neo4j::Node.new; node1[:colour] = 'red'; node1[:type] = 'A'
73
- # node2 = Neo4j::Node.new; node2[:colour] = 'red'; node2[:type] = 'B'
74
- #
75
- # agg_node = MyAggregateNode.new
76
- # agg_node.aggregate([node1, node2]).group_by(:colour, :type)
77
- #
78
- # # node1 is member of two groups, red and A
79
- # [*node1.aggregate_groups] # => [agg_node[:red], agg_node[:A]]
80
- #
81
- # # group A contains node1
82
- # agg_node[:A].include?(node1) # => true
83
- #
84
- # # group red also contains node1
85
- # agg_node[:red].include?(node1) # => true
86
- #
87
- # ==== Example - Appending new nodes to aggregates
88
- #
89
- # The aggregate node mixin implements the << operator that allows you to append nodes to the aggregate and the
90
- # appended node will be put in the correct group.
91
- #
92
- # a = AggregateNode.new
93
- # a.aggregate.group_by(:age).of_value{|age| age / 5}
94
- #
95
- # a << node1 << node2
96
- #
97
- # Notice that we do not need call the execute method. That method will be called each time we append nodes to the aggregate.
98
- #
99
- # ==== Example - trees of aggregates
100
- #
101
- # One example where this is needed is for having a tree structure of nodes with latitude and longitude grouped by a 'zoom' factor
102
- #
103
- # create an aggregation of groups where members have the same latitude longitude integer values (to_i)
104
- # reg1 = agg_root.aggregate().group_by(:latitude, :longitude).map_value{|lat, lng| "#{(lat*1000).to_i}_#{(lng*1000).to_i}"}
105
- #
106
- # create another aggregation of groups where members have the same latitude longitude 1/10 value
107
- # reg2 = agg_root.aggregate(reg1).group_by(:latitude, :longitude).map_value{|lat, lng| "#{(lat*100).to_i}_#{(lng*100).to_i" }
108
- #
109
- # Notice how the second aggregate uses the first aggregate (reg1). This will create the following structure with
110
- # * node n1 - (latitude 42.1234 and longitude 12.1234) and
111
- # * node n2 (latitude 42.1299 and longitude 12.1298)
112
- # * node n3 (latitude 42.1333 and longitude 12.1298)
113
- #
114
- # Root agg_root
115
- # | |
116
- # Group 4212_1212 Group 4213_1212
117
- # | |
118
- # Group 42123_12123 Group 42133_12129
119
- # | | |
120
- # n1 n2 n3
121
- #
122
- # When the nodes n1,n2,n3 are added to the agg_root, e.g:
123
- # agg_root << n1 << n2 << n3
124
- #
125
- #
126
- # ==== Example - Add and remove nodes by events
127
- #
128
- # We want to both create and delete nodes and the aggregates should be updated automatically
129
- # This is done by providing a NodeClass for the aggregate method.
130
- # (it registering the aggregate dsl method as an event listener
131
- #
132
- # Here is an example that update the aggregate a on all nodes of type MyNode
133
- # a = AggregateNode.new
134
- #
135
- # # the aggreate will get notified when nodes of type MyNode get changed
136
- # a.aggregate(MyNode).group_by(:colour)
137
- #
138
- # Neo4j::Transaction.run { blue_node = MyNode.new; a.colour = 'blue' }
139
- # # then the aggregate will be updated automatically since it listen to property change events
140
- # a['blue'].size = 1
141
- # [*a['blue']][0] # => blue_node
142
- #
143
- # blue_node[:colour] = 'red'
144
- # a['blue'] # => nil
145
- # [*a['red']] # => [blue_node]
146
- # blue_node.delete
147
- # a['red'] # => nil
148
- #
149
- #
150
- # ===== TODO Only Aggregate ???
151
- #
152
- # a.aggregate([n1,n2])
153
- # [*a] => [n1, n2]
154
- #
155
- # OR without aggregate method
156
- # a << n1 << n2
157
- #
158
- # ===== Example Group by Each (1)
159
- #
160
- # class MyRoot
161
- # include AggregateEach
162
- # end
163
- #
164
- # a = MyRoot.new
165
- #
166
- # n1 = [jan=>1, feb=>5, mars=>2, apr=>10, ...]
167
- # n2 = [jan=>2, feb=>0, mars=>2, apr=>10, ...]
168
- #
169
- # a.aggregate_each([n1,n2]).group(:jan,:feb,:mars).by(:q1)
170
- # [*a[:q1]] = [g1,g2]
171
- # g1.props => [n1.neo_id, jan=>1, feb=>5, ..., dec=>]
172
- # [*g1] => [1,5,2]
173
- #
174
- # ===== Example Group by Each (2)
175
- #
176
- # n1 = [:colour => 'red', :age => 10]
177
- # n2 = [:colour => 'blue', :age => 11]
178
- # n3 = [:colour => 'red', :age => 12]
179
- #
180
- # a.aggregate_each([n1,n2,n3]).group_by(:colour, :age)
181
- # n1.aggregate_groups = [g1]
182
- # [*g1] = ['red', 10]
183
- # [*a] => [g1,g2,g3]
184
- #
185
- # [*g2] = ['blue', 11]
186
- # g1.props => [
187
- #
188
- #
189
- # ===== Example Group by new property
190
- #
191
- # q1.aggregate_each(nodes).group_by(:jan,:feb,:mars)
192
- # q2.aggregate_each(nodes).group_by(:apr,:may,:june)
193
- #
194
- #
195
- # T O D O - G R O U P _ B Y the only needed one (not group and by)
196
- # n1 = [jan=>1, feb=>5, mars=>2, apr=>10, ...]
197
- # n2 = [jan=>2, feb=>0, mars=>2, apr=>10, ...]
198
- #
199
- # [*q1] => [g1,g2]
200
- # [*g1] => [1,5,2]
201
- # [*g2] => [2,0,2]
202
- #
203
- # [*q2] => [g3,g4]
204
- # n1.aggregate_groups = [g1,g2]
205
- # n1[:q1] => nil OR [1,5,2] ????
206
- # a[:q1] => [1,5,2,2,0,2]
207
- # n1.each {|n| n[:q1]}
208
- #
209
- # SUM
210
- # a.aggregate([n1,n2]).group(:jan,:feb,:mars).by(:q1).sum
211
- # n1[:q1] => 8
212
- # a[:q1] => 12
213
- #
214
- #
215
- # m1 = [revenue => 1000]
216
- # m2 = [revenue => 500]
217
- # m3 = [revenue => 2000]
218
- # a2.aggregate([m1,m2,m3]).group(:revenue).by(:rev).map_value{|v| v >= 1000 ? "good" : "bad"}.count
219
- #
220
- # a[:rev] => ["good", "good", "bad"]
221
- # a[:rev]["good"] => 2
222
- # a[:rev]["bad"] => 1
223
- module NodeAggregateMixin
224
- include Neo4j::NodeMixin
225
- include Enumerable
226
-
227
-
228
- # The number of groups that this aggregate contains
229
- def aggregate_size
230
- _java_node.set_property("aggregate_size", 0) unless _java_node.has_property("aggregate_size")
231
- self[:aggregate_size]
232
- end
233
-
234
-
235
- # Internal method - set the number of groups that this node contains
236
- # We can then use this property instead of traversing and counting each node in order to find out how many groups there are.
237
- def aggregate_size=(value) # :nodoc:
238
- self[:aggregate_size] = value
239
- end
240
-
241
- # Creates aggregated nodes by grouping nodes by one or more property values.
242
- # Raises an exception if the aggregation already exists.
243
- #
244
- # ==== Parameters
245
- # * aggregate(optional an enumeration) - specifies which nodes it should aggregate into groups of nodes
246
- #
247
- # If the no argument is given for the aggregate method then nodes can be appended to the aggregate using the << method.
248
- #
249
- # ==== Returns
250
- # an object that has the following methods
251
- # * group_by(*keys) - specifies which property or properties values it should group by
252
- # * group_each_by - same as group_by but instead of combinding the properties it creates new groups for each given property
253
- # * execute - executes the aggregation, creates new nodes that groups the specified nodes
254
- #
255
- # :api: public
256
- def aggregate(nodes_or_filter=nil)
257
- # setting a property here using neo4j.rb might trigger events which we do not want
258
- @aggregator = NodeAggregator.new(self, nodes_or_filter)
259
- end
260
-
261
- # Appends one or a whole enumeration of nodes to the existing aggregation.
262
- # Each node will be put into aggregate groups that was specified using the aggregate method.
263
- #
264
- # If the node does not have a property(ies) used for grouping nodes then the node will node be appendend to the aggreation.
265
- # Example:
266
- # my_agg.aggregate.group_by(:colour)
267
- # my_agg << Neo4j::Node.new # this node will not be added since it is missing the colour property
268
- #
269
- # ==== Parameter
270
- # * node(an enumeration, or one node) - specifies which node(s) should be appneit should aggregate into groups of nodes
271
- #
272
- # ==== Returns
273
- # self
274
- #
275
- def <<(node)
276
- # @aggregator.execute if @aggregator
277
- if node.kind_of?(Enumerable)
278
- @aggregator.execute(node)
279
- else
280
- @aggregator.execute([node])
281
- end
282
- self
283
- end
284
-
285
-
286
- # Checks if the given node is include in this aggregate
287
- #
288
- # ==== Returns
289
- # true if the aggregate includes the given node.
290
- #
291
- # :api: public
292
- def include_node?(node)
293
- key = @aggregator.group_key_of(node)
294
- group = group_node(key)
295
- return false if group.nil?
296
- group.include?(node)
297
- end
298
-
299
- # Returns the group with the given key
300
- # If there is no group with that key it returns nil
301
- #
302
- # :api: public
303
- def group_node(key)
304
- @aggregator.execute if @aggregator
305
- rels.outgoing(key).nodes.find{|n| n.kind_of? NodeGroup}
306
- end
307
-
308
-
309
- # Overrides the [] method
310
- #
311
- # If there is a relationship of the given key, and that node is kind_of?
312
- # that that relationships point to will be returned (as an Enumeration).
313
- # Otherwise, return the property of this node.
314
- #
315
- def [](key)
316
- node = group_node(key)
317
- return node unless node.nil?
318
- super(key)
319
- end
320
-
321
-
322
- def each
323
- @aggregator.execute if @aggregator
324
- rels.outgoing.nodes.each {|n| yield n if n.kind_of? NodeGroup}
325
- end
326
-
327
- end
328
-
329
-
330
-
331
- end
@@ -1,216 +0,0 @@
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
@@ -1,43 +0,0 @@
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
@@ -1,30 +0,0 @@
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
@@ -1,24 +0,0 @@
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
@@ -1,8 +0,0 @@
1
- module Neo4j::Aggregate
2
-
3
- class PropsAggregate
4
- include Neo4j::NodeMixin
5
- include Neo4j::Aggregate::PropsAggregateMixin
6
- end
7
-
8
- end
@@ -1,31 +0,0 @@
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