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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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