For reading about traversals, see Chapter 8, The Traversal Framework.
For more examples of traversals, see Chapter 9, Domain Modeling Gallery.
This is the first node space we want to traverse into:
Friends and friends of friends.
private static Traverser getFriends( final Node person ) { return person.traverse( Order.BREADTH_FIRST, StopEvaluator.END_OF_GRAPH, ReturnableEvaluator.ALL_BUT_START_NODE, RelTypes.KNOWS, Direction.OUTGOING ); }
Let’s perform the actual traversal and print the results:
String output = neoNode.getProperty( "name" ) + "'s friends:\n"; Traverser friendsTraverser = getFriends( neoNode ); for ( Node friendNode : friendsTraverser ) { output += "At depth " + friendsTraverser.currentPosition().depth() + " => " + friendNode.getProperty( "name" ) + "\n"; }
Which will give us the following output:
Thomas Anderson's friends: At depth 1 => Trinity At depth 1 => Morpheus At depth 2 => Cypher At depth 3 => Agent Smith
Who coded the Matrix?
private static Traverser findHackers( final Node startNode ) { return startNode.traverse( Order.BREADTH_FIRST, StopEvaluator.END_OF_GRAPH, new ReturnableEvaluator() { @Override public boolean isReturnableNode( final TraversalPosition currentPos ) { return !currentPos.isStartNode() && currentPos.lastRelationshipTraversed() .isType( RelTypes.CODED_BY ); } }, RelTypes.CODED_BY, Direction.OUTGOING, RelTypes.KNOWS, Direction.OUTGOING ); }
Print out the result:
Traverser traverser = findHackers( getNeoNode() ); for ( Node hackerNode : traverser ) { output += "At depth " + traverser.currentPosition().depth() + " => " + hackerNode.getProperty( "name" ) + "\n"; }
Now we know who coded the Matrix:
Hackers: At depth 4 => The Architect
Full source code: Matrix.java
Note | |
---|---|
The following examples use a new experimental traversal API. It shares the underlying implementation with the old traversal API, so performance-wise they should be equal. However, expect the new API to evolve and thus undergo changes. |
The traversals from the Matrix example above, this time using the new traversal API:
Friends and friends of friends.
private static Traverser getFriends( final Node person ) { TraversalDescription td = Traversal.description() .breadthFirst() .relationships( RelTypes.KNOWS, Direction.OUTGOING ) .evaluator( Evaluators.excludeStartPosition() ); return td.traverse( person ); }
Let’s perform the actual traversal and print the results:
Traverser friendsTraverser = getFriends( neoNode ); for ( Path friendPath : friendsTraverser ) { System.out.println( "At depth " + friendPath.length() + " => " + friendPath.endNode() .getProperty( "name" ) ); }
Who coded the Matrix?
private static Traverser findHackers( final Node startNode ) { TraversalDescription td = Traversal.description() .breadthFirst() .relationships( RelTypes.CODED_BY, Direction.OUTGOING ) .relationships( RelTypes.KNOWS, Direction.OUTGOING ) .evaluator( Evaluators.returnWhereLastRelationshipTypeIs( RelTypes.CODED_BY ) ); return td.traverse( startNode ); }
Print out the result:
Traverser traverser = findHackers( getNeoNode() ); for ( Path hackerPath : traverser ) { System.out.println( "At depth " + hackerPath.length() + " => " + hackerPath.endNode() .getProperty( "name" ) ); }
Full source code: MatrixNewTravTest.java
This example shows how to use a path context holding a representation of a path.
Create a toy graph.
Node A = db.createNode(); Node B = db.createNode(); Node C = db.createNode(); Node D = db.createNode(); A.createRelationshipTo( B, REL1 ); B.createRelationshipTo( C, REL2 ); C.createRelationshipTo( D, REL3 ); A.createRelationshipTo( C, REL2 );
Now, the order of relationships (REL1
→ REL2
→ REL3
) is stored in an ArrayList
.
Upon traversal, the Evaluator
can check against it to ensure that only paths are included and returned that have the predefined order of relationships:
Walk the path.
final ArrayList<RelationshipType> orderedPathContext = new ArrayList<RelationshipType>(); orderedPathContext.add( REL1 ); orderedPathContext.add( withName( "REL2" ) ); orderedPathContext.add( withName( "REL3" ) ); TraversalDescription td = Traversal.description().evaluator( new Evaluator() { @Override public Evaluation evaluate( final Path path ) { if ( path.length() == 0 ) { return Evaluation.EXCLUDE_AND_CONTINUE; } RelationshipType expectedType = orderedPathContext.get( path.length() - 1 ); boolean isExpectedType = path.lastRelationship().isType( expectedType ); boolean included = path.length() == orderedPathContext.size() && isExpectedType; boolean continued = path.length() < orderedPathContext.size() && isExpectedType; return Evaluation.of( included, continued ); } } ); Traverser t = td.traverse( db.getNodeById( 1 ) ); for ( Path path : t ) { System.out.println( path ); }
Full source code: OrderedPathTest.java
This example is demonstrating the use of node uniqueness. Below an imaginary domain graph with Principals that own pets that are descendant to other pets.
In order to return all descendants
of Pet0
which have the relation owns
to Principal1
(Pet1
and Pet3
),
the Uniqueness of the traversal needs to be set to
NODE_PATH
rather than the default NODE_GLOBAL
so that nodes
can be traversed more that once, and paths that have
different nodes but can have some nodes in common (like the
start and end node) can be returned.
final Node target = data.get().get( "Principal1" ); TraversalDescription td = Traversal.description() .uniqueness( Uniqueness.NODE_PATH ) .evaluator( new Evaluator() { @Override public Evaluation evaluate( Path path ) { if ( path.endNode().equals( target ) ) { return Evaluation.INCLUDE_AND_PRUNE; } return Evaluation.EXCLUDE_AND_CONTINUE; } } ); Traverser results = td.traverse( start );
This will return the following paths:
(3)--[descendant,0]-->(1)<--[owns,3]--(5) (3)--[descendant,2]-->(4)<--[owns,5]--(5)
In the default path.toString()
implementation, (1)--[knows,2]-->(4)
denotes
a node with ID=1 having a relationship with ID 2 or type knows
to a node with ID-4.
Let’s create a new TraversalDescription
from the old one,
having NODE_GLOBAL
uniqueness to see the difference.
Tip | |
---|---|
The |
TraversalDescription nodeGlobalTd = td.uniqueness( Uniqueness.NODE_GLOBAL ); results = nodeGlobalTd.traverse( start );
Now only one path is returned:
(3)--[descendant,0]-->(1)<--[owns,3]--(5)
Note | |
---|---|
The following example uses the new experimental traversal API. |
Social networks (know as social graphs out on the web) are natural to model with a graph. This example shows a very simple social model that connects friends and keeps track of status updates.
The data model for a social network is pretty simple: Persons
with names and StatusUpdates
with timestamped text.
These entities are then connected by specific relationships.
Person
friend
: relates two distinct Person
instances (no self-reference)
status
: connects to the most recent StatusUpdate
StatusUpdate
next
: points to the next StatusUpdate
in the chain, which was posted before the current one
The StatusUpdate
list for a Person
is a linked list.
The head of the list (the most recent status) is found by following status
.
Each subsequent StatusUpdate
is connected by next
.
Here’s an example where Andreas Kollegger micro-blogged his way to work in the morning:
To read the status updates, we can create a traversal, like so:
TraversalDescription traversal = Traversal.description(). depthFirst(). relationships( NEXT ). filter( Traversal.returnAll() );
This gives us a traverser that will start at one StatusUpdate
, and will follow the chain of updates until they run out.
Traversers are lazy loading, so it’s performant even when dealing with thousands of statuses - they are not loaded until we actually consume them.
Once we have friends, and they have status messages, we might want to read our friends status' messages, in reverse time order - latest first. To do this, we go through these steps:
Animated, the sequence looks like this.
The code looks like:
PositionedIterator<StatusUpdate> first = statuses.get(0); StatusUpdate returnVal = first.current(); if ( !first.hasNext() ) { statuses.remove( 0 ); } else { first.next(); sort(); } return returnVal;
Full source code: socnet
Copyright © 2012 Neo Technology