module Neo4j
module NodeRelationship
include ToJava
# A more powerful alternative of #outgoing, #incoming and #both method.
# You can use this method for example to only traverse nodes based on properties on the relationships
#
# ==== Example
#
# some_node.expand { |n| n._rels.find_all { |r| r[:age] > 5 } }.depth(:all).to_a
#
# The above traverse all relationships with a property of age > 5
#
def expand(&expander)
NodeTraverser.new(self).expander(&expander)
end
# Returns the outgoing nodes for this node.
#
# ==== Returns
# a Neo4j::NodeTraverser which can be used to further specify which nodes should be included
# in traversal by using the depth, filter and prune methods.
#
# ==== Examples
# # Find all my friends (nodes of depth 1 of type friends)
# me.outgoing(:friends).each {|friend| puts friend[:name]}
#
# # Find all my friends and their friends (nodes of depth 1 of type friends)
# # me.outgoing(:friends).depth(2).each {|friend| puts friend[:name]}
#
# # Find all my friends and include my self in the result
# me.outgoing(:friends).depth(4).include_start_node.each {...}
#
# # Find all my friends friends friends, etc. at any depth
# me.outgoing(:friends).depth(:all).each {...}
#
# # Find all my friends friends but do not include my friends (only depth == 2)
# me.outgoing(:friends).depth(2).filter{|path| path.length == 2}
#
# # Find all my friends but 'cut off' some parts of the traversal path
# me.outgoing(:friends).depth(42).prune(|path| an_expression_using_path_returning_true_false }
#
# # Find all my friends and work colleges
# me.outgoing(:friends).outgoing(:work).each {...}
#
# Of course all the methods outgoing, incoming, both, depth, include_start_node, filter, and prune can be combined.
#
def outgoing(type)
if type
NodeTraverser.new(self).outgoing(type)
else
raise "Not implemented getting all types of outgoing relationship. Specify a relationship type"
end
end
# Returns the incoming nodes of given type(s).
#
# See #outgoing
#
def incoming(type)
if type
NodeTraverser.new(self).incoming(type)
else
raise "Not implemented getting all types of incoming relationship. Specify a relationship type"
end
end
# Returns both incoming and outgoing nodes of given types(s)
#
# If a type is not given then it will return all types of relationships.
#
# See #outgoing
#
def both(type=nil)
if type
NodeTraverser.new(self).both(type)
else
NodeTraverser.new(self) # default is both
end
end
# Returns an enumeration of relationship objects.
# It always returns relationship of depth one.
#
# See Neo4j::Relationship
#
# ==== Examples
# # Return both incoming and outgoing relationships
# me.rels(:friends, :work).each {|relationship|...}
#
# # Only return outgoing relationship of given type
# me.rels(:friends).outgoing.first.end_node # => my friend node
#
def rels(*type)
RelationshipTraverser.new(self, type, :both)
end
# Returns the only relationship of a given type and direction that is attached to this node, or null.
# This is a convenience method that is used in the commonly occuring situation where a node has exactly zero or
# one relationships of a given type and direction to another node.
# Typically this invariant is maintained by the rest of the code: if at any time more than one such relationships
# exist, it is a fatal error that should generate an unchecked exception. This method reflects that semantics and
# returns either:
#
# * nil if there are zero relationships of the given type and direction,
# * the relationship if there's exactly one, or
# * raise an exception in all other cases.
def rel(dir, type)
result = _rel(dir, type)
result && result.wrapper
end
# Same as rel but does not return a ruby wrapped object but instead returns the Java object.
def _rel(dir, type)
get_single_relationship(type_to_java(type), dir_to_java(dir))
end
# Returns the raw java neo4j relationship object.
def _rels(dir=:both, *types)
if types.size > 1
java_types = types.inject([]) { |result, type| result << type_to_java(type) }.to_java(:'org.neo4j.graphdb.RelationshipType')
get_relationships(java_types)
elsif types.size == 1
get_relationships(type_to_java(types[0]), dir_to_java(dir))
elsif dir == :both
get_relationships(dir_to_java(dir))
else
raise "illegal argument, does not accept #{dir} #{types.join(',')} - only dir=:both for any relationship types"
end
end
# Check if the given relationship exists
# Returns true if there are one or more relationships from this node to other nodes
# with the given relationship.
#
# ==== Parameters
# type:: the key and value to be set, default any type
# dir:: optional default :both (either, :outgoing, :incoming, :both)
#
# ==== Returns
# true if one or more relationships exists for the given type and dir
# otherwise false
#
def rel? (type=nil, dir=:both)
if type
hasRelationship(type_to_java(type), dir_to_java(dir))
else
hasRelationship
end
end
end
end