Traversal Framework Examples

Here are some examples to highlight how one might use the Traversal Framework. The source code for the examples can be found at TraversalExample.java.

See the graph below which illustrates a small group of friends:

traversal framework example

With the definition of the RelationshipTypes as

private enum Rels implements RelationshipType
{
    LIKES, KNOWS
}

the graph can be traversed with, for example, the following traverser starting at the node with the name = 'Joe':

for ( Path position : db.traversalDescription()
        .depthFirst()
        .relationships( Rels.KNOWS )
        .relationships( Rels.LIKES, Direction.INCOMING )
        .evaluator( Evaluators.toDepth( 5 ) )
        .traverse( node ) )
{
    output += position + "\n";
}

The traversal will thus output:

(0)
(0)<-[LIKES,1]-(5)
(0)<-[LIKES,1]-(5)-[KNOWS,6]->(1)
(0)<-[LIKES,1]-(5)-[KNOWS,6]->(1)<-[KNOWS,5]-(6)
(0)<-[LIKES,1]-(5)-[KNOWS,6]->(1)-[KNOWS,4]->(4)
(0)<-[LIKES,1]-(5)-[KNOWS,6]->(1)-[KNOWS,4]->(4)-[KNOWS,3]->(3)
(0)<-[LIKES,1]-(5)-[KNOWS,6]->(1)-[KNOWS,4]->(4)-[KNOWS,3]->(3)-[KNOWS,2]->(2)

Since a TraversalDescription is immutable, it is useful to create template descriptions that include common settings shared by different traversals. For example, start with this traverser:

friendsTraversal = db.traversalDescription()
        .depthFirst()
        .relationships( Rels.KNOWS )
        .uniqueness( Uniqueness.RELATIONSHIP_GLOBAL );

It should yield the following output (starting from the node with the name = 'Joe'):

(0)
(0)-[KNOWS,0]->(2)
(0)-[KNOWS,0]->(2)<-[KNOWS,2]-(3)
(0)-[KNOWS,0]->(2)<-[KNOWS,2]-(3)<-[KNOWS,3]-(4)
(0)-[KNOWS,0]->(2)<-[KNOWS,2]-(3)<-[KNOWS,3]-(4)<-[KNOWS,4]-(1)
(0)-[KNOWS,0]->(2)<-[KNOWS,2]-(3)<-[KNOWS,3]-(4)<-[KNOWS,4]-(1)<-[KNOWS,6]-(5)
(0)-[KNOWS,0]->(2)<-[KNOWS,2]-(3)<-[KNOWS,3]-(4)<-[KNOWS,4]-(1)<-[KNOWS,5]-(6)

Then create a new traverser from it, restricting depth to three:

for ( Path path : friendsTraversal
        .evaluator( Evaluators.toDepth( 3 ) )
        .traverse( node ) )
{
    output += path + "\n";
}

This should be the output:

(0)
(0)-[KNOWS,0]->(2)
(0)-[KNOWS,0]->(2)<-[KNOWS,2]-(3)
(0)-[KNOWS,0]->(2)<-[KNOWS,2]-(3)<-[KNOWS,3]-(4)

In case you want to traverse from depth two to four:

for ( Path path : friendsTraversal
        .evaluator( Evaluators.fromDepth( 2 ) )
        .evaluator( Evaluators.toDepth( 4 ) )
        .traverse( node ) )
{
    output += path + "\n";
}

This will give the following output:

(0)-[KNOWS,0]->(2)<-[KNOWS,2]-(3)
(0)-[KNOWS,0]->(2)<-[KNOWS,2]-(3)<-[KNOWS,3]-(4)
(0)-[KNOWS,0]->(2)<-[KNOWS,2]-(3)<-[KNOWS,3]-(4)<-[KNOWS,4]-(1)

To see other useful evaluators, see Using Evaluators.

The Traverser also has a nodes() method that will return an Iterable of all the nodes in the paths:

for ( Node currentNode : friendsTraversal
        .traverse( node )
        .nodes() )
{
    output += currentNode.getProperty( "name" ) + "\n";
}

This will give the following output:

Joe
Sara
Peter
Dirk
Lars
Lisa
Ed

You can do this with relationships as well. Here is an example:

for ( Relationship relationship : friendsTraversal
        .traverse( node )
        .relationships() )
{
    output += relationship.getType().name() + "\n";
}
KNOWS
KNOWS
KNOWS
KNOWS
KNOWS
KNOWS

User-defined procedure with a Traversal Framework

Below is an example of how to implement a user-defined procedure using the Traversal Framework. The transaction and logger are made available through the Procedure Framework:

@Context
public Transaction tx;

@Context
public Log log;

@Procedure(value = "traverse.findPeople")
@Description("Finds all the known people to the given Person")
public Stream<PathResult> findFriends(@Name("person") Node person) {

    final Traverser traverse = tx.traversalDescription()
            .breadthFirst()
            .relationships(RelationshipType.withName("KNOWS"), Direction.OUTGOING)
            .evaluator(Evaluators.toDepth(5))
            .evaluator(new PathLogger())
            .traverse(person);

    return stream(traverse.iterator()).map(PathResult::new);
}

private final class PathLogger implements Evaluator {

    @Override
    public Evaluation evaluate(Path path) {
        log.info(path.toString());
        return Evaluation.INCLUDE_AND_CONTINUE;
    }
}

This allows the Traversal Framework to be used side by side with Cypher:

MATCH (p:Person { name: 'Joe' })
CALL traverse.findPeople(p) YIELD path RETURN [friend IN nodes(path) | friend.name] AS friends