diff --git a/lib/neo4j/extensions/aggregate.rb b/lib/neo4j/extensions/aggregate.rb index 63b6bd004..624b32e0b 100644 --- a/lib/neo4j/extensions/aggregate.rb +++ b/lib/neo4j/extensions/aggregate.rb @@ -1,13 +1,12 @@ require 'set' -require 'neo4j/extensions/aggregate/aggregator' -require 'neo4j/extensions/aggregate/group_enum' -require 'neo4j/extensions/aggregate/group_node' -require 'neo4j/extensions/aggregate/property_enum' -require 'neo4j/extensions/aggregate/aggregate_node_mixin' -require 'neo4j/extensions/aggregate/aggregate_node' require 'neo4j/extensions/aggregate/ext/node_mixin' - -require 'neo4j/extensions/aggregate/aggregator_each' -require 'neo4j/extensions/aggregate/group_each_node' -require 'neo4j/extensions/aggregate/aggregate_each_node_mixin' -require 'neo4j/extensions/aggregate/aggregate_each_node' +require 'neo4j/extensions/aggregate/node_aggregate_mixin' +require 'neo4j/extensions/aggregate/props_aggregate_mixin' +require 'neo4j/extensions/aggregate/node_aggregate' +require 'neo4j/extensions/aggregate/props_aggregate' +require 'neo4j/extensions/aggregate/aggregate_enum' +require 'neo4j/extensions/aggregate/node_aggregator' +require 'neo4j/extensions/aggregate/node_group' +require 'neo4j/extensions/aggregate/prop_group' +require 'neo4j/extensions/aggregate/property_enum' +require 'neo4j/extensions/aggregate/props_aggregator' diff --git a/lib/neo4j/extensions/aggregate/aggregate_each_node.rb b/lib/neo4j/extensions/aggregate/aggregate_each_node.rb deleted file mode 100644 index 4f51b2037..000000000 --- a/lib/neo4j/extensions/aggregate/aggregate_each_node.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Neo4j::Aggregate - class AggregateEachNode - include Neo4j::NodeMixin - include Neo4j::Aggregate::AggregateEachNodeMixin - end -end \ No newline at end of file diff --git a/lib/neo4j/extensions/aggregate/group_enum.rb b/lib/neo4j/extensions/aggregate/aggregate_enum.rb similarity index 78% rename from lib/neo4j/extensions/aggregate/group_enum.rb rename to lib/neo4j/extensions/aggregate/aggregate_enum.rb index 2dc3b6725..a966ae5d0 100644 --- a/lib/neo4j/extensions/aggregate/group_enum.rb +++ b/lib/neo4j/extensions/aggregate/aggregate_enum.rb @@ -1,15 +1,19 @@ module Neo4j::Aggregate -# Used for an enumerable result of aggregates + # Used for an enumerable result of aggregates # See Neo4j::NodeMixin#aggregates # # :api: private - class GroupEnum #:nodoc: + class AggregateEnum #:nodoc: include Enumerable def initialize(node) @node = node end + def empty? + each {true}.nil? + end + def each # if node is an aggregate group then we should look for parent aggregates if (@node.property?(:aggregate_group)) @@ -17,7 +21,7 @@ def each next unless parent_group.property?(:aggregate_size) # if it has the property aggregate_group then it is a group node if (parent_group.property?(:aggregate_group)) - GroupEnum.new(parent_group).each {|agg| yield agg} + AggregateEnum.new(parent_group).each {|agg| yield agg} else # aggregate found yield parent_group @@ -27,7 +31,7 @@ def each # the given node (@node) is not a group, we guess it is an leaf in an aggregate # get all the groups that this leaf belongs to and then those groups aggregate nodes @node.relationships.incoming(:aggregate).nodes.each do |group| - GroupEnum.new(group ).each {|agg| yield agg} + AggregateEnum.new(group ).each {|agg| yield agg} end end end diff --git a/lib/neo4j/extensions/aggregate/aggregate_node.rb b/lib/neo4j/extensions/aggregate/aggregate_node.rb deleted file mode 100644 index 376b8061e..000000000 --- a/lib/neo4j/extensions/aggregate/aggregate_node.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Neo4j::Aggregate - - class AggregateNode - include Neo4j::NodeMixin - include Neo4j::Aggregate::AggregateNodeMixin - end - - -end \ No newline at end of file diff --git a/lib/neo4j/extensions/aggregate/ext/node_mixin.rb b/lib/neo4j/extensions/aggregate/ext/node_mixin.rb index 2e03a2d1a..f0696a584 100644 --- a/lib/neo4j/extensions/aggregate/ext/node_mixin.rb +++ b/lib/neo4j/extensions/aggregate/ext/node_mixin.rb @@ -11,22 +11,19 @@ module NodeMixin # # class MyNode # include Neo4j::NodeMixin - # include Neo4j::AggregateNodeMixin + # include Neo4j::NodeAggregateMixin # end # # agg1 = MyNode - # agg1.aggregate(:colours).group_by(:colour) + # agg1.aggregate([node1,node2]).group_by(:colour) # # agg2 = MyNode - # agg2.aggregate(:age).group_by(:age) - # - # agg1 << node1 - # agg2 << node1 + # agg2.aggregate([node1,node2]).group_by(:age) # # node1.aggregates.to_a # => [agg1, agg2] # def aggregates - Neo4j::Aggregate::GroupEnum.new(self) + Neo4j::Aggregate::AggregateEnum.new(self) end # Returns an enumeration of groups that this nodes belongs to. diff --git a/lib/neo4j/extensions/aggregate/node_aggregate.rb b/lib/neo4j/extensions/aggregate/node_aggregate.rb new file mode 100644 index 000000000..11c73a62f --- /dev/null +++ b/lib/neo4j/extensions/aggregate/node_aggregate.rb @@ -0,0 +1,8 @@ +module Neo4j::Aggregate + + class NodeAggregate + include Neo4j::NodeMixin + include Neo4j::Aggregate::NodeAggregateMixin + end + +end \ No newline at end of file diff --git a/lib/neo4j/extensions/aggregate/aggregate_node_mixin.rb b/lib/neo4j/extensions/aggregate/node_aggregate_mixin.rb similarity index 84% rename from lib/neo4j/extensions/aggregate/aggregate_node_mixin.rb rename to lib/neo4j/extensions/aggregate/node_aggregate_mixin.rb index 897d4003c..f1c941f45 100644 --- a/lib/neo4j/extensions/aggregate/aggregate_node_mixin.rb +++ b/lib/neo4j/extensions/aggregate/node_aggregate_mixin.rb @@ -2,27 +2,32 @@ module Neo4j::Aggregate - # Enables aggregation of an enumeration of nodes into groups. - # Each group is a neo4j node which contains aggregated properties of the underlying nodes in that group. + # Enables aggregation of nodes into groups. + # An aggregation is a node which in contains group nodes. + # A group node aggregates the properties of the nodes that belongs to its group. # - # Notice that the AggregateNodeMixin#aggregate method takes an Ruby Enumeration of neo4j nodes. - # That means that you can use for example the output from the Neo4j::NodeMixin#traverse as input to the aggregate method, or even + # There are two ways of creating an aggregate. + # * Providing an enumeration of nodes. + # * Register a Node class. All nodes of this class will (can) be part of the aggregate. + # + # One example of usage of providing an enumeration of nodes is by taking the output from + # the Neo4j::NodeMixin#traverse as input to the aggregate method, or even # create aggregates over aggregates. # # This mixin includes the Enumerable mixin. # + # There is also a different aggregation which aggregates on properties + # instead of nodes - Neo4j::Aggregate::PropsAggregateMixin + # # ==== Example - group by one property # # Let say we have nodes with properties :colour and we want to group them by colour: # # a = AggregateNode.new # - # a.aggregate(nodes).group_by(:colour) - # - # The following node structure will be created: - # - # [node a]----*>[node groups]----*>[node nodes] + # a.aggregate(nodes).group_by(:colour).execute # + # The execute method is only needed when providing nodes (instead of a NodeClass) for the aggregate method. # Print all three groups, one for each colour # # a.each{|n| puts n[:colour]} @@ -38,7 +43,7 @@ module Neo4j::Aggregate # # Get an enumeration of names of people having favorite colour 'red' # - # a.[:red][:name].to_a => ['bertil', 'adam', 'adam'] + # a[:red][:name].to_a => ['bertil', 'adam', 'adam'] # # ==== Example - group by a property value which is transformed # @@ -95,13 +100,13 @@ module Neo4j::Aggregate # # One example where this is needed is for having a tree structure of nodes with latitude and longitude grouped by a 'zoom' factor # - # create an aggrgeation of groups where members have the same latitude longitude integer values (to_i) + # create an aggregation of groups where members have the same latitude longitude integer values (to_i) # reg1 = agg_root.aggregate().group_by(:latitude, :longitude).map_value{|lat, lng| "#{(lat*1000).to_i}_#{(lng*1000).to_i}"} # # create another aggregation of groups where members have the same latitude longitude 1/10 value # reg2 = agg_root.aggregate(reg1).group_by(:latitude, :longitude).map_value{|lat, lng| "#{(lat*100).to_i}_#{(lng*100).to_i" } # - # Notice how the second aggreate uses the first aggregate (reg1). This will create the following structure with + # Notice how the second aggregate uses the first aggregate (reg1). This will create the following structure with # * node n1 - (latitude 42.1234 and longitude 12.1234) and # * node n2 (latitude 42.1299 and longitude 12.1298) # * node n3 (latitude 42.1333 and longitude 12.1298) @@ -117,28 +122,12 @@ module Neo4j::Aggregate # When the nodes n1,n2,n3 are added to the agg_root, e.g: # agg_root << n1 << n2 << n3 # - # ==== Example - aggregating over another aggregation - # - # a = AggregateNode.new - # a.aggregate.group_by(:colour) - # a << node1 << node2 - # - # b = AggregateNode.new - # b.aggregate.group_by(:age) - # node3[:colour] = 'green'; node3[:age] = 10 - # node4[:colour] = 'red'; node3[:age] = 11 - # - # b << node3 << node4 - # - # a << b - # - # a['green'][10] #=>[node3] - # # # ==== Example - Add and remove nodes by events # # We want to both create and delete nodes and the aggregates should be updated automatically - # This is done by registering the aggregate dsl method as an event listener + # This is done by providing a NodeClass for the aggregate method. + # (it registering the aggregate dsl method as an event listener # # Here is an example that update the aggregate a on all nodes of type MyNode # a = AggregateNode.new @@ -231,13 +220,24 @@ module Neo4j::Aggregate # a[:rev] => ["good", "good", "bad"] # a[:rev]["good"] => 2 # a[:rev]["bad"] => 1 - module AggregateNodeMixin + module NodeAggregateMixin include Neo4j::NodeMixin - property :aggregate_size # number of groups this aggregate contains include Enumerable + # The number of groups that this aggregate contains + def aggregate_size + internal_node.set_property("aggregate_size", 0) unless internal_node.has_property("aggregate_size") + self[:aggregate_size] + end + + # Internal method - set the number of groups that this node contains + # We can then use this property instead of traversing and counting each node in order to find out how many groups there are. + def aggregate_size=(value) # :nodoc: + self[:aggregate_size] = value + end + # Creates aggregated nodes by grouping nodes by one or more property values. # Raises an exception if the aggregation already exists. # @@ -255,8 +255,7 @@ module AggregateNodeMixin # :api: public def aggregate(nodes_or_filter=nil) # setting a property here using neo4j.rb might trigger events which we do not want - internal_node.set_property("aggregate_size", 0) unless internal_node.has_property("aggregate_size") - @aggregator = Aggregator.new(self, nodes_or_filter) + @aggregator = NodeAggregator.new(self, nodes_or_filter) end # Appends one or a whole enumeration of nodes to the existing aggregation. @@ -303,7 +302,7 @@ def include_node?(node) # :api: public def group_node(key) @aggregator.execute if @aggregator - relationships.outgoing(key).nodes.find{|n| n.kind_of? AggregateGroupNode} + relationships.outgoing(key).nodes.find{|n| n.kind_of? NodeGroup} end @@ -325,7 +324,7 @@ def get_property(key) def each @aggregator.execute if @aggregator - relationships.outgoing.nodes.each {|n| yield n if n.kind_of? AggregateGroupNode} + relationships.outgoing.nodes.each {|n| yield n if n.kind_of? NodeGroup} end end diff --git a/lib/neo4j/extensions/aggregate/aggregator.rb b/lib/neo4j/extensions/aggregate/node_aggregator.rb similarity index 88% rename from lib/neo4j/extensions/aggregate/aggregator.rb rename to lib/neo4j/extensions/aggregate/node_aggregator.rb index 7d752293c..b563a353d 100644 --- a/lib/neo4j/extensions/aggregate/aggregator.rb +++ b/lib/neo4j/extensions/aggregate/node_aggregator.rb @@ -2,7 +2,7 @@ module Neo4j::Aggregate # Used to create a DSL describing how to aggregate an enumeration of nodes # # :api: public - class Aggregator + class NodeAggregator attr_accessor :root_dsl def initialize(root_node, dsl_nodes_or_filter) @@ -12,7 +12,7 @@ def initialize(root_node, dsl_nodes_or_filter) if dsl_nodes_or_filter.kind_of?(self.class) # we are chaining aggregates @child_dsl = dsl_nodes_or_filter - @child_dsl.root_dsl = self # the child has a pointer to the parent + @child_dsl.root_dsl = self # the child has a pointer to the parent elsif dsl_nodes_or_filter.kind_of?(Enumerable) # we are aggregating an enumerable set of nodes @nodes = dsl_nodes_or_filter @@ -62,16 +62,15 @@ def on_property_changed(node, prop_key, old_value, new_value) # :nodoc: # :api: private def on_node_deleted(node) # :nodoc: return if node.class != @filter - member_of = node.relationships.incoming(:aggregate).filter{start_node.property? :aggregate_size}.to_a - return if member_of.empty? - group_node = member_of[0].start_node - group_node.aggregate_size -= 1 - - # should we delete the whole group ? - delete_group(group_node) if (group_node.aggregate_size == 0) + node.relationships.incoming(:aggregate).filter{start_node.property? :aggregate_size}.each do |group_rel| + group_node = group_rel.start_node + group_node.aggregate_size -= 1 + # should we delete the whole group ? + delete_group(group_node) if (group_node.aggregate_size == 0) + end end - def delete_group(group_node) # :nodoc: + def delete_group(group_node) # :nodoc: # get parent aggregates and decrease the aggregate size group_node.relationships.incoming.nodes.each do |parent_group| next unless parent_group.respond_to? :aggregate_size @@ -82,7 +81,7 @@ def delete_group(group_node) # :nodoc: end - def on_prop_deleted(node, curr_node_values, old_node_values) # :nodoc: + def on_prop_deleted(node, curr_node_values, old_node_values) # :nodoc: old_group_keys = group_key_of(old_node_values) new_group_keys = group_key_of(curr_node_values) @@ -103,7 +102,7 @@ def on_prop_deleted(node, curr_node_values, old_node_values) # :nodoc: end - def on_prop_added(node, curr_node_values, old_node_values) # :nodoc: + def on_prop_added(node, curr_node_values, old_node_values) # :nodoc: old_group_keys = group_key_of(old_node_values) new_group_keys = group_key_of(curr_node_values) @@ -177,7 +176,7 @@ def create_groups(parent, node) # :api: private def create_group_for_key(parent, node, key) # find a group node for the given key - group_node = parent.relationships.outgoing(key).nodes.find{|n| n.kind_of? AggregateGroupNode} + group_node = parent.relationships.outgoing(key).nodes.find{|n| n.kind_of? NodeGroup} # if no group key is found create a new one group_node ||= create_group_node(parent, key) @@ -185,10 +184,10 @@ def create_group_for_key(parent, node, key) # check if it is the leaf node or not if (@child_dsl) # this is not the leaf aggregate dsl, let the child node add the node instead - @child_dsl.create_groups(group_node, node) + @child_dsl.create_groups(group_node, node) else # this IS a leaf aggregate dsl, add node to the group - rel_type = node.kind_of?(AggregateGroupNode)? key : :aggregate + rel_type = node.kind_of?(NodeGroup)? key : :aggregate rel = group_node.relationships.outgoing(rel_type) << node rel[:aggregate_group] = key # increase the size counter on this group @@ -198,7 +197,7 @@ def create_group_for_key(parent, node, key) # :api: private def create_group_node(parent, key) - new_node = AggregateGroupNode.create(key) + new_node = NodeGroup.create(key) rel = parent.relationships.outgoing(key) << new_node parent.aggregate_size += 1 # another group was created rel[:aggregate_group] = key diff --git a/lib/neo4j/extensions/aggregate/group_node.rb b/lib/neo4j/extensions/aggregate/node_group.rb similarity index 93% rename from lib/neo4j/extensions/aggregate/group_node.rb rename to lib/neo4j/extensions/aggregate/node_group.rb index 9af7fc270..7e57b3f42 100644 --- a/lib/neo4j/extensions/aggregate/group_node.rb +++ b/lib/neo4j/extensions/aggregate/node_group.rb @@ -5,14 +5,14 @@ module Neo4j::Aggregate # Overrides [] and []= properties, so that we can access aggregated properties or relationships. # # :api: private - class AggregateGroupNode #:nodoc: + class NodeGroup #:nodoc: include Neo4j::NodeMixin include Enumerable property :aggregate_group, :aggregate_size def self.create(aggregate_group) - new_node = AggregateGroupNode.new + new_node = NodeGroup.new new_node.aggregate_group = aggregate_group.kind_of?(Symbol)? aggregate_group.to_s : aggregate_group new_node.aggregate_size = 0 new_node diff --git a/lib/neo4j/extensions/aggregate/group_each_node.rb b/lib/neo4j/extensions/aggregate/prop_group.rb similarity index 56% rename from lib/neo4j/extensions/aggregate/group_each_node.rb rename to lib/neo4j/extensions/aggregate/prop_group.rb index 343eef9eb..b98f96684 100644 --- a/lib/neo4j/extensions/aggregate/group_each_node.rb +++ b/lib/neo4j/extensions/aggregate/prop_group.rb @@ -1,20 +1,19 @@ module Neo4j::Aggregate - class GroupEachNode + class PropGroup include Neo4j::NodeMixin include Enumerable +# alias :original_ignore_incoming_cascade_delete? :ignore_incoming_cascade_delete? has_one :aggregate, :cascade_delete => :incoming property :aggregate_group, :aggregate_size, :group_by - def each group_by.split(',').each do |group| yield aggregate[group] end end - # :api: private def get_property(key) value = super(key) @@ -22,7 +21,12 @@ def get_property(key) return nil unless aggregate aggregate[key] end - + + def ignore_incoming_cascade_delete? (relationship) + return true if super(relationship) #original_ignore_incoming_cascade_delete?(node,relationship) + return true if relationship.start_node.kind_of?(Neo4j::Aggregate::PropsAggregate) + end + end end \ No newline at end of file diff --git a/lib/neo4j/extensions/aggregate/props_aggregate.rb b/lib/neo4j/extensions/aggregate/props_aggregate.rb new file mode 100644 index 000000000..24846adaa --- /dev/null +++ b/lib/neo4j/extensions/aggregate/props_aggregate.rb @@ -0,0 +1,8 @@ +module Neo4j::Aggregate + + class PropsAggregate + include Neo4j::NodeMixin + include Neo4j::Aggregate::PropsAggregateMixin + end + +end \ No newline at end of file diff --git a/lib/neo4j/extensions/aggregate/aggregate_each_node_mixin.rb b/lib/neo4j/extensions/aggregate/props_aggregate_mixin.rb similarity index 65% rename from lib/neo4j/extensions/aggregate/aggregate_each_node_mixin.rb rename to lib/neo4j/extensions/aggregate/props_aggregate_mixin.rb index 173092acc..3987f894d 100644 --- a/lib/neo4j/extensions/aggregate/aggregate_each_node_mixin.rb +++ b/lib/neo4j/extensions/aggregate/props_aggregate_mixin.rb @@ -1,11 +1,13 @@ module Neo4j::Aggregate - module AggregateEachNodeMixin + # Aggregates properties on one or more nodes. + # Can also be used to apply functions (e.g. sum/average) on a set of properties. + # + module PropsAggregateMixin include Neo4j::NodeMixin include Enumerable has_list :groups, :counter => true #, :cascade_delete => :incoming - attr_reader :aggregate_id def init_node(*args) @aggregate_id = args[0] unless args.empty? @@ -21,8 +23,8 @@ def each groups.each {|sub_group| sub_group.each {|val| yield val}} end - def aggregate_each(nodes_or_class) - @aggregator = AggregatorEach.new(self, nodes_or_class) + def aggregate(agg_id) + @aggregator = PropsAggregator.new(self, agg_id.to_s) end end diff --git a/lib/neo4j/extensions/aggregate/aggregator_each.rb b/lib/neo4j/extensions/aggregate/props_aggregator.rb similarity index 78% rename from lib/neo4j/extensions/aggregate/aggregator_each.rb rename to lib/neo4j/extensions/aggregate/props_aggregator.rb index a796c9de7..4b236262f 100644 --- a/lib/neo4j/extensions/aggregate/aggregator_each.rb +++ b/lib/neo4j/extensions/aggregate/props_aggregator.rb @@ -1,13 +1,27 @@ module Neo4j::Aggregate - class AggregatorEach - def initialize(root_node, nodes_or_class) + class PropsAggregator + def initialize(root_node, agg_id) + @root_node = root_node + @agg_id = agg_id + end + + def on(nodes_or_class) if (nodes_or_class.kind_of?(Class) and nodes_or_class.ancestors.include?(Neo4j::NodeMixin)) Neo4j.event_handler.add(self) @filter = nodes_or_class + @nodes = nodes_or_class + elsif (!nodes_or_class.respond_to?(:each)) + @nodes = [nodes_or_class] + else + @nodes = nodes_or_class end - @root_node = root_node - @nodes = nodes_or_class + self + end + + def props(*properties) + @group_by = properties + self end # Unregisters this aggregate so that it will not be notified any longer @@ -26,7 +40,6 @@ def unregister Neo4j.event_handler.remove(self) end - # http://www.2paths.com/2009/06/22/visualizing-semantic-data-using-javascript-and-the-html-canvas-element/ # called from neo4j event handler # :api: private def on_property_changed(node, prop_key, old_value, new_value) # :nodoc: @@ -35,7 +48,7 @@ def on_property_changed(node, prop_key, old_value, new_value) # :nodoc: # for each aggregate the node belongs to delete it # we have to first create it and then deleted, otherwise cascade delete will kick in - group = node.aggregate_groups(@root_node.aggregate_id) + group = node.aggregate_groups(@agg_id) # recreate the aggregate group execute([node]) @@ -44,14 +57,6 @@ def on_property_changed(node, prop_key, old_value, new_value) # :nodoc: group.delete if group end - # Specifies which properties we should group on. - # All those properties can be combined to create a new group. - # - # :api: public - def group_by(*keys) - @group_by = keys - self - end def with(prop_key, &proc) @with_proc = proc @@ -61,11 +66,11 @@ def with(prop_key, &proc) def execute(nodes = @nodes) return unless nodes nodes.each do |node| - group_node = GroupEachNode.new + group_node = PropGroup.new group_node.group_by = @group_by.join(',') group_node.aggregate = node rel = group_node.relationships.outgoing(:aggregate)[node] - rel[:aggregate_group] = @root_node.aggregate_id.to_s + rel[:aggregate_group] = @agg_id @root_node.groups << group_node if @with_proc val = group_node.inject(0) {|sum, val| next sum if val.nil?; @with_proc.call(sum, val, 0)} diff --git a/lib/neo4j/extensions/reindexer.rb b/lib/neo4j/extensions/reindexer.rb index d46d3eccd..9a3785e78 100644 --- a/lib/neo4j/extensions/reindexer.rb +++ b/lib/neo4j/extensions/reindexer.rb @@ -6,11 +6,6 @@ module NodeMixin def ignore_incoming_cascade_delete? (node, relationship) return true if old_ignore_incoming_cascade_delete?(node,relationship) - 5.times {puts "-------------------------------"} - puts "ignore_incoming_cascade_delete" - node.print 1,:both - 5.times {puts "-------------------------------"} - # if it's an index node relationship then it should be allowed to cascade delete the node return relationship.other_node(node) == IndexNode.instance end diff --git a/lib/neo4j/mixins/node_mixin.rb b/lib/neo4j/mixins/node_mixin.rb index 607f0be27..29f315b2d 100644 --- a/lib/neo4j/mixins/node_mixin.rb +++ b/lib/neo4j/mixins/node_mixin.rb @@ -284,7 +284,7 @@ def delete # check node has no outgoing relationships no_outgoing = node.relationships.outgoing.empty? # check node has only incoming relationship with cascade_delete_incoming - no_incoming = node.relationships.incoming.find{|r| !ignore_incoming_cascade_delete?(node, r)}.nil? + no_incoming = node.relationships.incoming.find{|r| !node.ignore_incoming_cascade_delete?(r)}.nil? # only cascade delete incoming if no outgoing and no incoming (exception cascade_delete_incoming) relationships node.delete if no_outgoing and no_incoming end @@ -297,7 +297,7 @@ def delete # Specifies which relationships should be ignored when trying to cascade delete a node. # If a node does not have any relationships (except those specified here to ignore) it will be cascade deleted # - def ignore_incoming_cascade_delete?(node, relationship) + def ignore_incoming_cascade_delete?(relationship) # ignore relationship with property _cascade_delete_incoming relationship.property?(:_cascade_delete_incoming) end diff --git a/lib/neo4j/relationships/relationship_traverser.rb b/lib/neo4j/relationships/relationship_traverser.rb index 7fc4de8f2..007bfe6ad 100644 --- a/lib/neo4j/relationships/relationship_traverser.rb +++ b/lib/neo4j/relationships/relationship_traverser.rb @@ -121,14 +121,15 @@ def initialize(relationships) @relationships = relationships end - # TODO does not work together with filter, will be removed in JRuby 1.4 (not needed since it is included) + # TODO does not work together with filter def first - iter = @relationships.iterator - return nil unless iter.hasNext() - rel = Neo4j.instance.load_relationship(iter.next) - rel.other_node(@relationships.internal_node) + find {true} end + def empty? + first.nil? + end + def each @relationships.each do |relationship| yield relationship.other_node(@relationships.internal_node) diff --git a/test/extensions/aggregate_spec.rb b/test/extensions/aggregate/node_aggregate_spec.rb similarity index 63% rename from test/extensions/aggregate_spec.rb rename to test/extensions/aggregate/node_aggregate_spec.rb index 5f1f6cfe2..c4991a825 100644 --- a/test/extensions/aggregate_spec.rb +++ b/test/extensions/aggregate/node_aggregate_spec.rb @@ -1,19 +1,38 @@ -$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../../lib") -$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") +$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../../../lib") +$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../..") require 'neo4j' require 'neo4j/extensions/aggregate' -require 'neo4j/spec_helper' +require 'spec_helper' class MyNode include Neo4j::NodeMixin end -AggregateNode = Neo4j::Aggregate::AggregateNode +NodeAggregate = Neo4j::Aggregate::NodeAggregate -describe "Aggregates" do +describe Neo4j::Aggregate::NodeGroup do + it "should return an enumeration of all properties on outgoing nodes" do + Neo4j::Transaction.new + group = Neo4j::Aggregate::NodeGroup.new + node1 = Neo4j::Node.new{|n| n[:name] = 'node1'} + node2 = Neo4j::Node.new{|n| n[:name] = 'node2'} + + group.relationships.outgoing(:foo) << node1 + group.relationships.outgoing(:foo) << node2 + + group[:name].to_a.should include('node1', 'node2') + group[:name].to_a.size.should == 2 + Neo4j::Transaction.finish + end +end + + + + +describe Neo4j::Aggregate::NodeAggregateMixin do before(:each) do start Neo4j::Transaction.new @@ -25,9 +44,62 @@ class MyNode @registrations.each {|reg| reg.unregister} end - describe "updated on event" do - it "should add nodes to the aggreate when a new node is created" do - agg = AggregateNode.new + describe "#aggregate_size" do + it "should be 0 when an node aggregate is created" do + agg = NodeAggregate.new + agg.aggregate_size.should == 0 + end + + + it "should be 1 when a group is created" do + agg = NodeAggregate.new + @registrations << agg.aggregate(MyNode).group_by(:city) + + # when created + node = MyNode.new + node[:city] = 'malmoe' + agg.aggregate_size.should == 1 + + node2 = MyNode.new + node2[:city] = 'stockholm' + agg.aggregate_size.should == 2 + end + + it "should be 2 when two different groups has been created" do + agg = NodeAggregate.new + @registrations << agg.aggregate(MyNode).group_by(:city) + + # when two groups + node = MyNode.new + node[:city] = 'malmoe' + agg.aggregate_size.should == 1 + node2 = MyNode.new + node2[:city] = 'stockholm' + + # then + agg.aggregate_size.should == 2 + end + + it "should be 1 when one group is created containing two nodes" do + agg = NodeAggregate.new + @registrations << agg.aggregate(MyNode).group_by(:city) + + # when create one group with two members + node = MyNode.new + node[:city] = 'malmoe' + agg.aggregate_size.should == 1 + node2 = MyNode.new + node2[:city] = 'malmoe' + + # then + agg.aggregate_size.should == 1 + end + end + + + describe "#aggregate(Class).group_by(one property)" do + it "should create group for a node when its property is set" do + agg = NodeAggregate.new @registrations << agg.aggregate(MyNode).group_by(:city) agg.aggregate_size.should == 0 @@ -40,13 +112,11 @@ class MyNode agg['malmoe'].should include(node) end - it "should move aggregate group of a node when it changes a property" do - agg = AggregateNode.new + it "should move group of a node when its property is changed" do + agg = NodeAggregate.new @registrations << agg.aggregate(MyNode).group_by(:city) node = MyNode.new node[:city] = 'malmoe' - agg.aggregate_size.should == 1 - agg['malmoe'].should include(node) # when node[:city] = 'stockholm' @@ -57,9 +127,46 @@ class MyNode agg['stockholm'].should include(node) end - it "should put a node into two groups when it is grouped by two properties" do + it "should delete the group if the last node in that group is deleted" do + agg = NodeAggregate.new + @registrations << agg.aggregate(MyNode).group_by(:city) + node = MyNode.new + node[:city] = 'malmoe' + agg['malmoe'].should include(node) + + # when + node.delete + + # then + agg['malmoe'].should be_nil + end + + it "should not delete the group when a member node is deleted if there are more nodes in that group " do + agg = NodeAggregate.new + @registrations << agg.aggregate(MyNode).group_by(:city) + node = MyNode.new + node[:city] = 'malmoe' + + node2 = MyNode.new + node2[:city] = 'malmoe' + agg['malmoe'].should include(node, node2) + + # when + node.delete + + # then + agg['malmoe'].should include(node2) + agg['malmoe'].should_not include(node) + end + + end + + + describe "#aggregate(Class).group_by(two properties)" do + + it "should put each node into two groups" do # given an aggregate with groups by two properties - agg = AggregateNode.new + agg = NodeAggregate.new @registrations << agg.aggregate(MyNode).group_by(:city, :age) node = MyNode.new @@ -75,7 +182,7 @@ class MyNode it "should move group of a node when one property changes but keep the remaining groups" do # given an aggregate with groups by two properties - agg = AggregateNode.new + agg = NodeAggregate.new @registrations << agg.aggregate(MyNode).group_by(:city, :age) node = MyNode.new node[:city] = 'malmoe' @@ -91,53 +198,70 @@ class MyNode agg[7].should include(node) end - it "should work for several nodes" do - agg = AggregateNode.new - @registrations << agg.aggregate(MyNode).group_by(:age) + it "should delete all its groups when a node is deleted and it was the last node in those groups" do + # given an aggregate with groups by two properties + agg = NodeAggregate.new + @registrations << agg.aggregate(MyNode).group_by(:city, :age) + node = MyNode.new + node[:city] = 'malmoe' + node[:age] = 10 + agg['malmoe'].should include(node) + agg[10].should include(node) + agg.aggregate_size.should == 2 - # when - 10.times {|i| node = MyNode.new; node[:age] = i} + # delete + node.delete # then - agg.aggregate_size.should == 10 - - 10.times {|i| agg[i].to_a[0][:age].should == i} - 10.times {|i| agg[i].aggregate_size.should == 1} + agg.aggregate_size.should == 0 + agg['malmoe'].should be_nil + agg[10].should be_nil end - it "should also work for map_value" do - agg = AggregateNode.new - @registrations << agg.aggregate(MyNode).group_by(:age).map_value{|x| x * 2} + it "should delete only empty groups when a node is deleted" do + # given an aggregate with groups by two properties + agg = NodeAggregate.new + @registrations << agg.aggregate(MyNode).group_by(:city, :age) + node = MyNode.new + node[:city] = 'malmoe' + node[:age] = 10 + node2 = MyNode.new + node2[:city] = 'malmoe' - # when - node = MyNode.new; node[:age] = 10 + agg['malmoe'].should include(node, node2) + agg[10].should include(node) + agg.aggregate_size.should == 2 + + # delete + node.delete # then - agg[20].should include(node) + agg.aggregate_size.should == 1 + agg['malmoe'].should include(node2) + agg[10].should be_nil end + end - it "should delete nodes to the aggregate when a new node is created" do - agg = AggregateNode.new - @registrations << agg.aggregate(MyNode).group_by(:city) - node = MyNode.new - node[:city] = 'malmoe' - agg.aggregate_size.should == 1 + describe "#aggregate(Class).group_by(one property).map_value{}" do + + it "should map a single property to one group" do + agg = NodeAggregate.new + @registrations << agg.aggregate(MyNode).group_by(:age).map_value{|x| x * 2} # when - node.delete + node = MyNode.new; node[:age] = 10 # then - agg.aggregate_size.should == 0 - agg['malmoe'].should be_nil + agg[20].should include(node) end - it "should allow to create several groups from the same property" do + it "should map a single property to several groups" do # let say we both want to create groups young, old and groups for each age # given - agg = AggregateNode.new + agg = NodeAggregate.new @registrations << agg.aggregate(MyNode).group_by(:age).map_value{|age| [age < 6 ? "young" : "old", age / 5]} # when @@ -159,8 +283,11 @@ class MyNode agg.aggregate_size.should == 4 end - it "should work on a tree of aggregates" do - agg_root = AggregateNode.new + end + + describe "#aggregate(aggregate) - nested aggregates" do + it "should allow to aggregate nodes in a tree of aggregates" do + agg_root = NodeAggregate.new # create an aggregate where all the members have the same score # update the aggregate when a node of type MyNode changes @@ -197,8 +324,9 @@ class MyNode agg_root["good"][101].should include(n4, n5) end - it "should work on a tree of aggregates when nodes are deleted" do - agg_root = AggregateNode.new + + it "should delete parent aggregate group nodes when child aggregate group node is deleted" do + agg_root = NodeAggregate.new # create an aggregate where all the members have the same score # update the aggregate when a node of type MyNode changes @@ -237,8 +365,8 @@ class MyNode end - it "should work on a tree of aggregates when node property are changed" do - agg_root = AggregateNode.new + it "should move the node in both parent and child aggregate groups when its property is changed" do + agg_root = NodeAggregate.new # create an aggregate where all the members have the same score # update the aggregate when a node of type MyNode changes @@ -280,7 +408,8 @@ class MyNode end - describe "grouped by one property" do + + describe "#aggregate(nodes).group_by(one property)" do # Called before each example. before(:each) do @red=[] @@ -297,34 +426,24 @@ class MyNode names = [] 4.times { names += ('a' .. 'd').to_a} @all.each {|n| n[:name] = names.pop} - - - # For all nodes that have the same colour create a new aggregate node with property 'colour' - # The ColourAggregateNode has outgoing relationship of type blue, black, red to those aggegated nodes. - # Each aggregated node has a property colour with the value of its related nodes. There will be three aggregated nodes with property blue, black and red. - # Those nodes are connected to all nodes that have this property with the same relationship. - # - # @blue nodes<*----[aggregated node, prop colour='blue']<------[@aggnode] ------>[aggregated node, prop colour='black']--->@black nodes - # | - # V - # --->... - @agg_node = AggregateNode.new + @agg_node = NodeAggregate.new @agg_node.aggregate(@all).group_by(:colour).execute end - it "should have an enumeration of all groups" do + it "should create one group for each unique property value" do @agg_node.to_a.size.should == 3 end - it "should have group nodes with propertie aggregate_group" do + + it "should create groups with properties aggregate_group" do colours = @agg_node.inject([]) {|array, node| array << node.aggregate_group} colours.should include('red', 'blue', 'green') end - it "should have size property for how many groups there are" do + it "should set the aggregate_size property to the number of created groups" do @agg_node.aggregate_size.should == 3 end - it "should have size property for each group" do + it "should have aggregate_size property for each group" do @agg_node[:red].aggregate_size.should == 5 @agg_node[:blue].aggregate_size.should == 4 @agg_node[:green].aggregate_size.should == 3 @@ -338,7 +457,6 @@ class MyNode it "should not add nodes to the aggregation that does not have a group property" do # add a node that does not have the colour property - @agg_node.to_a.size.should == 3 @agg_node[:red].aggregate_size.should == 5 @agg_node[:blue].aggregate_size.should == 4 @@ -354,12 +472,12 @@ class MyNode end - describe "grouped by each property" do + describe "#aggregate(nodes).group_by(two properties)" do it "should create groups for each property value" do node1 = Neo4j::Node.new; node1[:colour] = 'red'; node1[:type] = 'A' node2 = Neo4j::Node.new; node2[:colour] = 'red'; node2[:type] = 'B' - agg_node = AggregateNode.new + agg_node = NodeAggregate.new @registrations << agg_node.aggregate([node1, node2]).group_by(:colour, :type) agg_node['red'].aggregate_size.should == 2 @@ -375,7 +493,7 @@ class MyNode end end - describe "grouped by one property" do + describe "#aggregate(nodes).group_by(one property).map_value{}" do before(:each) do @people = [] 6.times {@people << Neo4j::Node.new} @@ -389,7 +507,7 @@ class MyNode # group 1 (5-9) - three people # group 2 (10-14) - one person - @aggregate_node = AggregateNode.new + @aggregate_node = NodeAggregate.new end it "should allow to create several groups from the same property" do @@ -432,18 +550,18 @@ class MyNode end - describe "x and y coordinates into squares" do + describe "Spatial Index using Aggregates, x and y into tiles" do before(:each) do # create positions (0,0), (1,2), (2,4), (3,6) ... @positions = [] 6.times {@positions << Neo4j::Node.new} @positions.each_with_index {|p, index| p[:x] = index} @positions.each_with_index {|p, index| p[:y] = index*2} - @aggregate_node = AggregateNode.new + @aggregate_node = NodeAggregate.new @registrations << @aggregate_node.aggregate(@positions).group_by(:x, :y).map_value{|x, y| (x/3)*3+(y/3)} end - it "should traverse all positions in a square" do + it "should traverse all positions in a tile" do # find all coordinates in the square 0 - |0,0 2,0| # |0,2 2,2| @aggregate_node[0].should include(@positions[0], @positions[1]) @@ -456,19 +574,19 @@ class MyNode end - it "should work with aggregates on aggregates" do - agg_root = AggregateNode.new + it "should allow to aggregate (index) tiles in tiles" do + agg_root = NodeAggregate.new n1 = MyNode.new; n1[:latitude] = 10.3; n1[:longitude] = 5.2 n2 = MyNode.new; n2[:latitude] = 5.94; n2[:longitude] = 52.4 n3 = MyNode.new; n3[:latitude] = 5.24; n3[:longitude] = 52.9 - # create an aggrgeation of groups where members have the same latitude longitude integer values (to_i) + # create an aggregate of groups where members have the same latitude longitude integer values (to_i) reg1 = agg_root.aggregate().group_by(:latitude, :longitude).map_value{|lat, lng| "#{(lat*10).to_i}_#{(lng*10).to_i}"} @registrations << reg1 # create another aggregation of groups where members have the same latitude longitude 1/10 value - @registrations << agg_root.aggregate(reg1).group_by(:latitude, :longitude).map_value{|lat, lng| "#{lat.to_i}_#{lng.to_i}" } + @registrations << agg_root.aggregate(reg1).group_by(:latitude, :longitude).map_value{|lat, lng| "#{lat.to_i}_#{lng.to_i}" } # when agg_root << n1 << n2 << n3 @@ -485,7 +603,8 @@ class MyNode end - describe "append nodes" do + + describe "#<< operator" do before(:each) do @red=[] @blue=[] @@ -501,8 +620,8 @@ class MyNode # aggregate all nodes into colour groups end - it "should add node into existing groups using the << operator" do - agg_node = AggregateNode.new + it "should add node into existing groups" do + agg_node = NodeAggregate.new @registrations << agg_node.aggregate(@all).group_by(:colour) new_node = Neo4j::Node.new @@ -517,8 +636,8 @@ class MyNode agg_node[:green].should include(new_node) end - it "should add node into new groups using the << operator" do - agg_node = AggregateNode.new + it "should add node into new groups" do + agg_node = NodeAggregate.new @registrations << agg_node.aggregate(@all).group_by(:colour) new_node = Neo4j::Node.new @@ -533,8 +652,8 @@ class MyNode agg_node[:black].should include(new_node) end - it "should allow to add node into an empty aggregation using << operator" do - agg_node = AggregateNode.new + it "should allow to add node into an empty aggregation" do + agg_node = NodeAggregate.new @registrations << agg_node.aggregate.group_by(:colour) new_node = Neo4j::Node.new @@ -550,7 +669,7 @@ class MyNode end it "should implement an effecient include_node? method, by only searching in the relevant group" do - agg_node = AggregateNode.new + agg_node = NodeAggregate.new @registrations << agg_node.aggregate.group_by(:colour) new_node = Neo4j::Node.new @@ -564,184 +683,6 @@ class MyNode end end - - describe "Neo4j::NodeMixin#aggregates and aggregate" do - before(:each) do - @set = [] - 4.times {@set << Neo4j::Node.new} - @set[0][:colour] = 'red'; @set[0][:name] = "a" - @set[1][:colour] = 'red'; @set[1][:name] = "b" - @set[2][:colour] = 'red'; @set[2][:name] = "c" - @set[3][:colour] = 'blue'; @set[3][:name] = "d" - - - # aggregate first on name - @agg1 = AggregateNode.new - @agg1.aggregate(@set).group_by(:name).execute - - #use this name aggregate and aggregate on colour - # - # agg1 set agg2 - # a -- @set[0] --+ - # b -- @set[1] --+-- red - # c -- @set[2] --+ - # d -- @set[3] ---- blue - # - @agg2 = AggregateNode.new - @agg2.aggregate(@set).group_by(:colour).execute - end - - it "should know which aggregate it belongs to" do - @set[0].aggregates.to_a.size.should == 2 - @set[1].aggregates.to_a.size.should == 2 - @set[0].aggregates.should include(@agg1, @agg2) - end - - it "should know which aggregate group it belongs to" do - # set[0] should belong to group agg1[a] and agg2[red] - @set[0].aggregate_groups.to_a.size.should == 2 - @set[0].aggregate_groups.should include(@agg1['a'], @agg2['red']) - - # set[2] should belong to group agg1[c] and agg2[red] - @set[2].aggregate_groups.to_a.size.should == 2 - @set[2].aggregate_groups.should include(@agg1['c'], @agg2['red']) - - # set[3] should belong to group agg[d] and agg2[blue] - @set[3].aggregate_groups.to_a.size.should == 2 - @set[3].aggregate_groups.should include(@agg1['d'], @agg2['blue']) - end - - it "should find the group direct by node.aggregate_group()" do - @set[0].aggregate_groups('a').should == @agg1['a'] - @set[2].aggregate_groups('c').should == @agg1['c'] - @set[2].aggregate_groups('red').should == @agg2['red'] - end - - end - - - describe "the << operator" do - before(:each) do - @set = [] - 4.times {@set << Neo4j::Node.new} - @set[0][:colour] = 'red'; @set[0][:name] = "a" - @set[1][:colour] = 'blue'; @set[1][:name] = "b" - @set[2][:colour] = 'red'; @set[2][:name] = "c" - @set[3][:colour] = 'blue'; @set[3][:name] = "d" - - # given - # agg set - # red --+-- @set[0] - # | - # +-- @set[2] - # - # blue +-- @set[1] - # | - # +-- @set[3] - # - @agg = AggregateNode.new - @agg.aggregate(@set).group_by(:colour).execute - end - - it "should allow to append one node to an existing aggregate group" do - new_node1 = Neo4j::Node.new - new_node1[:colour] = 'blue' - - # when - @agg << new_node1 - - # then - - @agg[:blue].aggregate_size.should == 3 - @agg[:blue].should include(new_node1) - end - - - it "should allow to append one node to a new aggregate group" do - new_node1 = Neo4j::Node.new - new_node1[:colour] = 'black' - - @agg.aggregate_size.should == 2 # only - - # when - @agg << new_node1 - - # then - @agg.aggregate_size.should == 3 - @agg[:black].should include(new_node1) - end - - it "should allow to append an enumeration of nodes" do - new_node1 = Neo4j::Node.new - new_node1[:colour] = 'black' - new_node2 = Neo4j::Node.new - new_node2[:colour] = 'red' - - @agg.aggregate_size.should == 2 # only - - # when - @agg << [new_node1, new_node2] - - # then - @agg.aggregate_size.should == 3 - @agg[:black].should include(new_node1) - @agg[:red].should include(new_node2) - end - - end - - describe "Aggregates, over another aggregate" do - before(:each) do - @set = [] - 4.times {@set << Neo4j::Node.new} - @set[0][:colour] = 'red'; @set[0][:name] = "a" - @set[1][:colour] = 'red'; @set[1][:name] = "b" - @set[2][:colour] = 'red'; @set[2][:name] = "c" - @set[3][:colour] = 'blue'; @set[3][:name] = "d" - end - - it "should allow to aggregate aggregate groups" do - # given - # agg2 agg1 set - # +-- a -- @set[0] - # red --|-- b -- @set[1] - # +-- c -- @set[2] - # blue ---- d -- @set[3] - # - agg1 = AggregateNode.new - @registrations << agg1.aggregate(@set).group_by(:name) - - # when - agg2 = AggregateNode.new - @registrations << agg2.aggregate(agg1).group_by(:colour) - - # then - agg2.aggregate_size.should == 2 - agg2['red'].aggregate_size.should == 3 - agg2['red'].should include(agg1['a'], agg1['b'], agg1['c']) - - agg2['blue'].aggregate_size.should == 1 - agg2['blue'].should include(agg1['d']) - end - - it "should know which aggregates it belongs to" do - agg1 = AggregateNode.new - @registrations << agg1.aggregate(@set).group_by(:name) - - # when - agg2 = AggregateNode.new - agg2.aggregate(agg1).group_by(:colour).execute - - # then - @set[0].aggregates.to_a.size.should == 2 - agg1['a'].aggregates.to_a.size.should == 2 - # - @set[0].aggregates.to_a.should include(agg1) - agg1['a'].aggregates.to_a.should include(agg1, agg2) - end - - end - end diff --git a/test/extensions/aggregate/node_mixin_spec.rb b/test/extensions/aggregate/node_mixin_spec.rb new file mode 100644 index 000000000..9865aa219 --- /dev/null +++ b/test/extensions/aggregate/node_mixin_spec.rb @@ -0,0 +1,107 @@ +$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../../../lib") +$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../..") + + +require 'neo4j' +require 'neo4j/extensions/aggregate' +require 'spec_helper' + +NodeAggregate = Neo4j::Aggregate::NodeAggregate + +describe Neo4j::NodeMixin do + + before(:each) do + start + Neo4j::Transaction.new + end + + after(:each) do + stop + end + + describe "#aggregates" do + it "returns an empty enumerable when nodes does not belong to any aggregates" do + node = Neo4j::Node.new + node.aggregates.should be_empty + end + + it "returns one aggregate node when it only belongs to one" do + node = Neo4j::Node.new + node[:colour] = 'green' + + agg_node = NodeAggregate.new + agg_node.aggregate([node]).group_by(:colour).execute + + node.aggregates.first.should == agg_node + node.aggregates.to_a.size.should == 1 + end + + it "returns two aggregates when one nodes belongs to two aggregates" do + node = Neo4j::Node.new + node[:colour] = 'green' + + agg_node1 = NodeAggregate.new + agg_node1.aggregate([node]).group_by(:colour).execute + + agg_node2 = NodeAggregate.new + agg_node2.aggregate([node]).group_by(:colour).execute + + node.aggregates.should include(agg_node1, agg_node2) + node.aggregates.to_a.size.should == 2 + end + + end + + describe "#aggregate_groups" do + it "should return an empty enumerable when node does not belong to a group" do + node = Neo4j::Node.new + node.aggregate_groups.should be_empty + end + + it "should return one group when nodes belongs to one" do + node = Neo4j::Node.new + node[:colour] = 'green' + agg_node = NodeAggregate.new + + agg_node.aggregate([node]).group_by(:colour).execute + + node.aggregate_groups.to_a.size.should == 1 + node.aggregate_groups.first.should be_kind_of(Neo4j::Aggregate::NodeGroup) + end + + it "should return two groups when nodes belongs to two" do + node = Neo4j::Node.new + node[:colour] = 'green' + node[:name] = 'andreas' + agg_node = NodeAggregate.new + + agg_node.aggregate([node]).group_by(:colour, :name).execute + + node.aggregate_groups.to_a.size.should == 2 + node.aggregate_groups.to_a[0].should be_kind_of(Neo4j::Aggregate::NodeGroup) + node.aggregate_groups.to_a[1].should be_kind_of(Neo4j::Aggregate::NodeGroup) + end + + it "should return the given group if found" do + node = Neo4j::Node.new + node[:colour] = 'green' + agg_node = NodeAggregate.new + + agg_node.aggregate([node]).group_by(:colour).execute + + node.aggregate_groups('green').should be_kind_of(Neo4j::Aggregate::NodeGroup) + end + + it "should return nil if given a group name that is not found" do + node = Neo4j::Node.new + node[:colour] = 'green' + agg_node = NodeAggregate.new + + agg_node.aggregate([node]).group_by(:colour).execute + + node.aggregate_groups('black').should be_nil + end + + end + +end diff --git a/test/extensions/aggregate/props_aggregate_spec.rb b/test/extensions/aggregate/props_aggregate_spec.rb new file mode 100644 index 000000000..eb6818788 --- /dev/null +++ b/test/extensions/aggregate/props_aggregate_spec.rb @@ -0,0 +1,324 @@ +$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../../../lib") +$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../..") + +require 'neo4j' +require 'neo4j/extensions/aggregate' +require 'spec_helper' + + +PropsAggregate = Neo4j::Aggregate::PropsAggregate + + +describe Neo4j::Aggregate::PropGroup do + before(:each) { Neo4j::Transaction.new} + after(:each) {stop} + + it "should return an enumeration of all properties on one node" do + # given + group_node = Neo4j::Aggregate::PropGroup.new + group_node.group_by = "name,age" + group_node.aggregate = Neo4j::Node.new {|n| n[:name] = 'kalle'; n[:age] = 23} + + # then + group_node.should include('kalle', 23) + end + +end + + +describe Neo4j::Aggregate::PropsAggregate do + before(:each) do + start + Neo4j::Transaction.new + @registrations = [] + end + + after(:each) do + stop + @registrations.each {|reg| reg.unregister} + end + + + describe "#aggregate_size" do + it "should be 0 when a property aggregate is created" do + pa = PropsAggregate.new + pa.aggregate_size.should == 0 + end + + it "should be 1 when one node is aggregated" do + node = Neo4j::Node.new + node[:name] = 'andreas' + + # when + pa = PropsAggregate.new + pa.aggregate(:q1).props(:name).on(node).execute + + # then + pa.aggregate_size.should == 1 + end + + it "should be 1 when one node is aggregated, even if there are no properties to aggregate" do + node = Neo4j::Node.new + node[:name] = 'andreas' + + # when + pa = PropsAggregate.new + pa.aggregate(:q1).props(:plong).on(node).execute + + # then + pa.aggregate_size.should == 1 + end + + it "should be 2 when two nodes are aggregated" do + node1 = Neo4j::Node.new + node1[:name] = 'andreas' + node2 = Neo4j::Node.new + node2[:name] = 'kalle' + + # when + pa = PropsAggregate.new + pa.aggregate(:q1).props(:name).on([node1, node2]).execute + + # then + pa.aggregate_size.should == 2 + end + + end + + + describe "#aggregate(id).props(properties).on(some nodes).execute" do + it "should create one group on one node" do + node = Neo4j::Node.new + node[:name] = 'andreas' + node[:colour] = 'blue' + + # when + q1 = PropsAggregate.new + q1.aggregate(:q1).props(:colour, :name).on(node).execute + + # then + node.aggregate_groups(:q1).should include("andreas", "blue") + end + + it "should create two groups for two nodes" do + node1 = Neo4j::Node.new + node1[:name] = 'andreas' + node1[:colour] = 'blue' + + node2 = Neo4j::Node.new + node2[:name] = 'kalle' + node2[:colour] = 'blue' + + # when + q1 = PropsAggregate.new + q1.aggregate(:q1).props(:colour, :name).on([node1, node2]).execute + + # then + node1.aggregate_groups(:q1).should include("andreas", "blue") + node2.aggregate_groups(:q1).should include("kalle", "blue") + end + + it "should return an enumeration of all properties on the aggregate node" do + node1 = Neo4j::Node.new + node1[:name] = 'andreas' + node1[:colour] = 'blue' + + node2 = Neo4j::Node.new + node2[:name] = 'kalle' + node2[:colour] = 'blue' + + # when + q1 = PropsAggregate.new + q1.aggregate(:q1).props(:colour, :name).on([node1, node2]).execute + + # then + q1.should include("andreas", "blue", "kalle") + q1.to_a.size.should == 4 + end + + it "should allow to create different aggregate groups from the same aggregate node" do + node = Neo4j::Node.new + node[:surename] = 'andreas' + node[:lastname] = 'ronge' + node[:phone] = '1234' + node[:mobile] = '5678' + + # when + ag = PropsAggregate.new + ag.aggregate(:name).props(:surename, :lastname).on(node).execute + ag.aggregate(:tel).props(:phone, :mobile).on(node).execute + + # then + node.aggregate_groups(:name).should include("andreas", "ronge") + node.aggregate_groups(:tel).should include("1234", "5678") + end + + it "should delete all groups if node is deleted" do + node = Neo4j::Node.new + node[:surename] = 'andreas' + node[:lastname] = 'ronge' + node[:phone] = '1234' + node[:mobile] = '5678' + ag = PropsAggregate.new + ag.aggregate(:name).props(:surename, :lastname).on(node).execute + ag.aggregate(:tel).props(:phone, :mobile).on(node).execute + + # when + node.delete + + # then + node.aggregate_groups(:name).should be_nil + node.aggregate_groups(:tel).should be_nil + end + end + + + describe "#aggregate(id).props(properties).on(Class)" do + before(:all) do + class Company + include Neo4j::NodeMixin + property :month, :revenue + end + end + + after(:all) do + undefine_class :Company + end + + before(:each) { @registrations = []} + after(:each) { @registrations.each {|r| r.unregister}} + + it "should create aggregate groups when nodes/properties of the specified class is created" do + # given + pa = PropsAggregate.new + @registrations << pa.aggregate(:q1).on(Company).props(:jan, :feb, :mars) + @registrations << pa.aggregate(:q2).on(Company).props(:april, :may, :june) + + # when + c1 = Company.new + c1[:jan] = 100 + c1[:feb] = 200 + c1[:mars] = 300 + c1[:april] = 400 + c1[:may] = 500 + c1[:june] = 600 + + c2 = Company.new + c2[:jan] = 1100 + c2[:feb] = 1200 + c2[:mars] = 1300 + c2[:april] = 1400 + c2[:may] = 1500 + c2[:june] = 1600 + + # then + c1.aggregate_groups(:q1).should include(100, 200, 300) + c2.aggregate_groups(:q2).should include(1400, 1500, 1600) + + pa.should include(100, 200, 300, 1100, 1200, 1300) + pa.should include(400, 500, 600, 1400, 1500, 1600) + end + + it "should update the aggregate when a node changes" do + q1 = PropsAggregate.new + @registrations << q1.aggregate(:q1).on(Company).props(:jan, :feb, :mars) + + # given + c1 = Company.new + c1[:jan] = 100 + c1[:feb] = 200 + q1.should include(100, 200) + + # when + c1[:feb] = 42 + + # then + q1.should_not include(200) + q1.should include(42) + end + + it "should delete the group when the node is deleted" do + q1 = PropsAggregate.new + @registrations << q1.aggregate(:q1).on(Company).props(:jan, :feb, :mars) + + # given + c1 = Company.new + c1[:jan] = 100 + c1[:feb] = 200 + q1.should include(100, 200) + q1.groups.size.should == 1 + Neo4j.load(q1.neo_node_id).should_not be_nil + + # when + c1.delete + + # then + q1.groups.size.should == 0 + q1.should_not include(100) + q1.should_not include(200) + end + + end + + describe "#groups" do + it "should contain one group for each node that has been aggregated" do + node1 = Neo4j::Node.new + node1[:name] = 'andreas' + node1[:colour] = 'blue' + + node2 = Neo4j::Node.new + node2[:name] = 'kalle' + node2[:colour] = 'blue' + + # when + pa = PropsAggregate.new + pa.aggregate(:foo).props(:colour, :name).on([node1, node2]).execute + + # then + pa.groups.size.should == 2 + pa.groups.should include(node1.aggregate_groups(:foo)) + pa.groups.should include(node2.aggregate_groups(:foo)) + end + end + + describe "#aggregate(id).props(properties).on(nodes).with(property){...}" do + + before(:all) do + class Company + include Neo4j::NodeMixin + property :month, :revenue + end + end + + after(:all) do + undefine_class :Company + end + + + it "should be possible to sum the values of a set of properties" do + # given + q1 = PropsAggregate.new(:q1) + q1.aggregate(:q1).on(Company).props(:jan, :feb, :mars).with(:sum){|sum, val, prev_val| sum + val - prev_val} + q2 = PropsAggregate.new(:q2) + q2.aggregate(:q2).on(Company).props(:april, :may, :june).with(:sum){|sum, val, prev_val| sum + val - prev_val} + + # when + c1 = Company.new + c1[:jan] = 100 + c1[:feb] = 200 + c1[:mars] = 300 + c1[:april] = 400 + c1[:may] = 500 + c1[:june] = 600 + + # then + c1.aggregate_groups(:q1)[:sum].should == 100+200+300 + c1.aggregate_groups(:q2)[:sum].should == 400+500+600 + end + + end +end + +# +# +#end \ No newline at end of file diff --git a/test/extensions/aggregate_each_spec.rb b/test/extensions/aggregate_each_spec.rb deleted file mode 100644 index 3a5ba51a2..000000000 --- a/test/extensions/aggregate_each_spec.rb +++ /dev/null @@ -1,252 +0,0 @@ -$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/../../lib") -$LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") - -require 'neo4j' -require 'neo4j/extensions/aggregate' -require 'neo4j/spec_helper' - - -AggregateEachNode = Neo4j::Aggregate::AggregateEachNode - -describe "Aggregate each node" do - before(:all) do - class Company - include Neo4j::NodeMixin - property :month, :revenue - end - end - - after(:all) do - undefine_class :Company - end - - before(:each) do - start - Neo4j::Transaction.new - @registrations = [] - end - - after(:each) do - stop - @registrations.each {|reg| reg.unregister} - end - - describe "Access aggregated node properties" do - before(:each) do -# Neo4j::Transaction.new - @nodes = [] - 4.times {@nodes << Neo4j::Node.new} - @nodes[0][:colour] = 'red'; @nodes[0][:name] = "a"; @nodes[0][:age] = 0 - @nodes[1][:colour] = 'red'; @nodes[1][:name] = "b"; @nodes[1][:age] = 1 - @nodes[2][:colour] = 'red'; @nodes[2][:name] = "c"; @nodes[2][:age] = 2 - @nodes[3][:colour] = 'blue'; @nodes[3][:name] = "d"; @nodes[3][:age] = 3 - - # when - @aggregate_node = AggregateEachNode.new - @aggregate_node.aggregate_each(@nodes).group_by(:colour, :name).execute - end - - it "can be retrieved as a group Neo4j::NodeMixin#aggregate_groups" do - Neo4j::Transaction.new - # then we should have one group - @nodes[0].aggregate_groups.to_a.size.should == 1 - group = @nodes[0].aggregate_groups.to_a[0] - - # and that group should contain the node property values - group.should include('red', 'a') - group.to_a.size.should == 2 - group[:age].should == 0 # group for @nodes[0] - end - - it "can all be retrieved from the aggregate node" do - # there are total 8 property values - @aggregate_node.to_a.sort.should == ["blue", "d", "red", "c", "red", "b", "red", "a"].sort - end - - it "can be retrieved from the aggregate node as an group" do - # there are total 4 groups - @aggregate_node.aggregate_size.should == 4 - - # so that we know which group is which group we put it in a age_groups map - age_groups = {} - @aggregate_node.groups.each {|g| age_groups[g[:age]] = g} - - age_groups[3].should include('blue','d') - age_groups[0].should include('red','a') - end - end - - it "should delete group if the node is deleted" do - nodes = [] - 4.times {nodes << Neo4j::Node.new} - nodes[0][:colour] = 'red'; nodes[0][:name] = "a"; nodes[0][:age] = 0 - nodes[1][:colour] = 'red'; nodes[1][:name] = "b"; nodes[1][:age] = 1 - nodes[2][:colour] = 'red'; nodes[2][:name] = "c"; nodes[2][:age] = 2 - nodes[3][:colour] = 'blue'; nodes[3][:name] = "d"; nodes[3][:age] = 3 - - agg1 = AggregateEachNode.new - agg1.aggregate_each(nodes).group_by(:colour, :name) - agg1.to_a.size.should == 8 - agg1.aggregate_size.should == 4 - agg1.groups.size.should == 4 - - - # when - n = nodes[2].aggregate_groups.to_a[0] - n.delete - - Neo4j::Transaction.finish - Neo4j::Transaction.new - - # then - agg1.to_a.size.should == 6 - agg1.aggregate_size.should == 3 - end - - - it "should create new groups for each node, group by quarter" do - revenue1 = Neo4j::Node.new - revenue2 = Neo4j::Node.new - revenue1[:jan] = 1; revenue1[:feb] = 2; revenue1[:mars] = 3; revenue1[:april] = 4; revenue1[:may] = 5; revenue1[:june] = 6 - revenue2[:jan] = 11; revenue2[:feb] = 12; revenue2[:mars] = 13; revenue2[:april] = 14; revenue2[:may] = 15; revenue2[:june] = 16 - - # when - q1 = AggregateEachNode.new - q1.aggregate_each([revenue1, revenue2]).group_by(:jan, :feb, :mars) - - q2 = AggregateEachNode.new - q2.aggregate_each([revenue1, revenue2]).group_by(:april, :may, :june) - - # then there should be two groups, one for each revenue node - q1.aggregate_size.should == 2 - # with each 3 values, total 6 (2*3) - q1.to_a.sort.should == [1, 2, 3, 11, 12, 13] - q2.to_a.sort.should == [4, 5, 6, 14, 15, 16] - end - - - it "should allow to find the aggregates a node belongs to based on a id" do - q1 = AggregateEachNode.new(:q1) - q2 = AggregateEachNode.new(:q2) - - # when - c1 = Company.new - c1[:jan] = 100 - c1[:feb] = 200 - c1[:mars] = 300 - c1[:april] = 400 - c1[:may] = 500 - c1[:june] = 600 - - c2 = Company.new - c2[:jan] = 1100 - c2[:feb] = 1200 - c2[:mars] = 1300 - c2[:april] = 1400 - c2[:may] = 1500 - c2[:june] = 1600 - - q1.aggregate_each([c1, c2]).group_by(:jan, :feb, :mars).execute - q2.aggregate_each([c1, c2]).group_by(:april, :may, :june).execute - - # then - q1.groups.should include(c1.aggregate_groups(:q1)) - q1.groups.should include(c2.aggregate_groups(:q1)) - q2.groups.should include(c1.aggregate_groups(:q2)) - q2.groups.should include(c2.aggregate_groups(:q2)) - end - - it "should allow to register nodes classes to be part of aggregates" do - # given - q1 = AggregateEachNode.new(:q1) - @registrations << q1.aggregate_each(Company).group_by(:jan, :feb, :mars) - q2 = AggregateEachNode.new(:q2) - @registrations << q2.aggregate_each(Company).group_by(:april, :may, :june) - - # when - c1 = Company.new - c1[:jan] = 100 - c1[:feb] = 200 - c1[:mars] = 300 - c1[:april] = 400 - c1[:may] = 500 - c1[:june] = 600 - - c2 = Company.new - c2[:jan] = 1100 - c2[:feb] = 1200 - c2[:mars] = 1300 - c2[:april] = 1400 - c2[:may] = 1500 - c2[:june] = 1600 - - # then - q1.should include(100, 200, 300, 1100, 1200, 1300) - q2.should include(400, 500, 600, 1400, 1500, 1600) - end - - it "should update the aggregate when a node changes" do - q1 = AggregateEachNode.new(:q1) - @registrations << q1.aggregate_each(Company).group_by(:jan, :feb, :mars) - - # given - c1 = Company.new - c1[:jan] = 100 - c1[:feb] = 200 - q1.should include(100, 200) - - # when - c1[:feb] = 42 - - # then - q1.should_not include(200) - q1.should include(42) - end - - it "should delete the group when the node is deleted" do - pending - q1 = AggregateEachNode.new(:q1) - @registrations << q1.aggregate_each(Company).group_by(:jan, :feb, :mars) - - # given - c1 = Company.new - c1[:jan] = 100 - c1[:feb] = 200 - q1.should include(100, 200) - q1.groups.size.should == 1 - Neo4j.load(q1.neo_node_id).should_not be_nil - - # when - c1.delete - - # then - q1.groups.size.should == 0 - q1.should_not include(100) - q1.should_not include(200) - end - - it "should allow to register nodes classes to be part of aggregates" do -# pending - # given - q1 = AggregateEachNode.new(:q1) - q1.aggregate_each(Company).group_by(:jan, :feb, :mars).with(:sum){|sum, val, prev_val| sum + val - prev_val} - q2 = AggregateEachNode.new(:q2) - q2.aggregate_each(Company).group_by(:april, :may, :june).with(:sum){|sum, val, prev_val| sum + val - prev_val} - - # when - c1 = Company.new - c1[:jan] = 100 - c1[:feb] = 200 - c1[:mars] = 300 - c1[:april] = 400 - c1[:may] = 500 - c1[:june] = 600 - - # then - puts "C1 GROUP Q1: #{c1.aggregate_groups(:q1).neo_node_id}" - puts "C1 GROUP Q2: #{c1.aggregate_groups(:q2).neo_node_id}" - - c1.aggregate_groups(:q1)[:sum].should == 100+200+300 - c1.aggregate_groups(:q2)[:sum].should == 400+500+600 - end -end \ No newline at end of file diff --git a/test/extensions/find_path_spec.rb b/test/extensions/find_path_spec.rb index ddc685b9b..1798eab0d 100644 --- a/test/extensions/find_path_spec.rb +++ b/test/extensions/find_path_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' require 'neo4j/extensions/find_path' class Currency diff --git a/test/extensions/graph_algo_spec.rb b/test/extensions/graph_algo_spec.rb index 648a5e842..6e10e9f94 100644 --- a/test/extensions/graph_algo_spec.rb +++ b/test/extensions/graph_algo_spec.rb @@ -3,7 +3,7 @@ require 'neo4j' require 'neo4j/extensions/graph_algo' -require 'neo4j/spec_helper' +require 'spec_helper' include Neo4j diff --git a/test/extensions/tx_tracker_spec.rb b/test/extensions/tx_tracker_spec.rb index 688687bf2..74cd185a3 100644 --- a/test/extensions/tx_tracker_spec.rb +++ b/test/extensions/tx_tracker_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' describe "TxTracker (TxNodeList)" do diff --git a/test/neo4j/cascade_delete_spec.rb b/test/neo4j/cascade_delete_spec.rb index 3f4fb4f0c..b33f343d0 100644 --- a/test/neo4j/cascade_delete_spec.rb +++ b/test/neo4j/cascade_delete_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' describe "Cascade Delete for raw relationships" do diff --git a/test/neo4j/has_list_spec.rb b/test/neo4j/has_list_spec.rb index 459bbc574..b098a631e 100644 --- a/test/neo4j/has_list_spec.rb +++ b/test/neo4j/has_list_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' class XNode diff --git a/test/neo4j/has_n_spec.rb b/test/neo4j/has_n_spec.rb index 08fb82671..c6850e1c4 100644 --- a/test/neo4j/has_n_spec.rb +++ b/test/neo4j/has_n_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' diff --git a/test/neo4j/has_one_spec.rb b/test/neo4j/has_one_spec.rb index 40ad442f0..6eef1339b 100644 --- a/test/neo4j/has_one_spec.rb +++ b/test/neo4j/has_one_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' describe "Neo4j::NodeMixin#has_one " do diff --git a/test/neo4j/indexer_spec.rb b/test/neo4j/indexer_spec.rb index f9b3ae546..ea6cdce9b 100644 --- a/test/neo4j/indexer_spec.rb +++ b/test/neo4j/indexer_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' include Neo4j diff --git a/test/neo4j/neo_spec.rb b/test/neo4j/neo_spec.rb index 0515f0e1b..38720a3f3 100644 --- a/test/neo4j/neo_spec.rb +++ b/test/neo4j/neo_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' diff --git a/test/neo4j/node_lucene_spec.rb b/test/neo4j/node_lucene_spec.rb index cd509d8ea..320e998f2 100644 --- a/test/neo4j/node_lucene_spec.rb +++ b/test/neo4j/node_lucene_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' describe "Neo4j-Lucene" do before(:all) do diff --git a/test/neo4j/node_mixin_spec.rb b/test/neo4j/node_mixin_spec.rb index 82043055f..180e44f06 100644 --- a/test/neo4j/node_mixin_spec.rb +++ b/test/neo4j/node_mixin_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' diff --git a/test/neo4j/node_traverser_spec.rb b/test/neo4j/node_traverser_spec.rb index be29e2065..da6e7ce64 100644 --- a/test/neo4j/node_traverser_spec.rb +++ b/test/neo4j/node_traverser_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' diff --git a/test/neo4j/order_spec.rb b/test/neo4j/order_spec.rb index 30164e6f7..3100bda89 100644 --- a/test/neo4j/order_spec.rb +++ b/test/neo4j/order_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' diff --git a/test/neo4j/person_spec.rb b/test/neo4j/person_spec.rb index 3e229b9c3..6de0f1e55 100644 --- a/test/neo4j/person_spec.rb +++ b/test/neo4j/person_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' diff --git a/test/neo4j/property_spec.rb b/test/neo4j/property_spec.rb index b66ba21be..e9e3ccb9e 100644 --- a/test/neo4j/property_spec.rb +++ b/test/neo4j/property_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' require 'date' class MyPropertyData diff --git a/test/neo4j/readme_spec.rb b/test/neo4j/readme_spec.rb index b61d3290e..b0e0db354 100644 --- a/test/neo4j/readme_spec.rb +++ b/test/neo4j/readme_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' describe "Readme Examples" do diff --git a/test/neo4j/ref_node_spec.rb b/test/neo4j/ref_node_spec.rb index e8aa5ee47..833a3640d 100644 --- a/test/neo4j/ref_node_spec.rb +++ b/test/neo4j/ref_node_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' describe 'ReferenceNode' do before(:all) do diff --git a/test/neo4j/reindexer_spec.rb b/test/neo4j/reindexer_spec.rb index a6d026577..0499ce577 100644 --- a/test/neo4j/reindexer_spec.rb +++ b/test/neo4j/reindexer_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' diff --git a/test/neo4j/relation_traverser_spec.rb b/test/neo4j/relation_traverser_spec.rb index 5f6aa488a..62153623a 100644 --- a/test/neo4j/relation_traverser_spec.rb +++ b/test/neo4j/relation_traverser_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' diff --git a/test/neo4j/transaction_spec.rb b/test/neo4j/transaction_spec.rb index 900806d0a..9bf48975a 100644 --- a/test/neo4j/transaction_spec.rb +++ b/test/neo4j/transaction_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' class BaseNode diff --git a/test/neo4j/value_object_spec.rb b/test/neo4j/value_object_spec.rb index d57c7d5a8..0e862567b 100644 --- a/test/neo4j/value_object_spec.rb +++ b/test/neo4j/value_object_spec.rb @@ -2,7 +2,7 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + "/..") require 'neo4j' -require 'neo4j/spec_helper' +require 'spec_helper' diff --git a/test/spec_helper.rb b/test/spec_helper.rb new file mode 100644 index 000000000..0bf31a661 --- /dev/null +++ b/test/spec_helper.rb @@ -0,0 +1,75 @@ +# +# Helper methods for specs +# + +require 'fileutils' +require 'tmpdir' + +# suppress all warnings +$NEO_LOGGER.level = Logger::ERROR + +NEO_STORAGE = Dir::tmpdir + "/neo_storage" +LUCENE_INDEX_LOCATION = Dir::tmpdir + "/lucene" + +Lucene::Config[:storage_path] = LUCENE_INDEX_LOCATION +Lucene::Config[:store_on_file] = false +Neo4j::Config[:storage_path] = NEO_STORAGE + +def delete_db + # delete db on filesystem + FileUtils.rm_rf Neo4j::Config[:storage_path] # NEO_STORAGE + FileUtils.rm_rf Lucene::Config[:storage_path] unless Lucene::Config[:storage_path].nil? +end + +def reset_config + # reset configuration + Lucene::Config.delete_all + Lucene::Config[:storage_path] = LUCENE_INDEX_LOCATION + Lucene::Config[:store_on_file] = false + + Neo4j::Config.delete_all + Neo4j::Config[:storage_path] = NEO_STORAGE +end + +def start + # stop it - just in case + stop + + delete_db + + reset_config + + # start neo + Neo4j.start +end + + +def stop + # make sure we finish all transactions + Neo4j::Transaction.finish if Neo4j::Transaction.running? + + Neo4j.stop + + delete_db + + reset_config +end + + +def undefine_class(*clazz_syms) + clazz_syms.each do |clazz_sym| + Object.instance_eval do + begin + Neo4j::Indexer.remove_instance const_get(clazz_sym) + remove_const clazz_sym + end if const_defined? clazz_sym + end + end +end + + +def clazz_from_symbol(classname_as_symbol) + classname_as_symbol.to_s.split("::").inject(Kernel) do |container, name| + container.const_get(name.to_s) + end +end \ No newline at end of file