Chris Beach
Chris Beach

Reputation: 4392

Neo4J traversal in correct order for two different relationships?

I'm using the Neo4J Traversal API and trying to traverse from "1" to find nodes "2" and "3" fitting the pattern below:

1-[:A]-2-[:B]-3

However, in my traversal, I'm getting caught out because the following relationship also exists:

1-[:B]-3

As far as I understand, my TraversalDescription needs to specify both relationship types, but I'm unsure of the most elegant way to traverse the :A relationship first, and then branch out to the :B relationship. Unfortunately the relationship Direction can't be used to differentiate in my case.

My Scala code is:

db.traversalDescription()
    .evaluator(isAThenBRelationship)
    .breadthFirst()
    .relationships(A)
    .relationships(B)

private val isAThenBRelationship = new PathEvaluator.Adapter() {
override def evaluate(path: Path, state: BranchState[Nothing]): Evaluation = {
  if (path.length == 0) {
    EXCLUDE_AND_CONTINUE
  } else if (path.length == 1) {
    Evaluation.of(false, path.relationships().iterator().next().getType.name() == A.toString)
  } else {
    Evaluation.of(path.relationships().iterator().next().getType.name() == B.toString, false)
  }
}

}

As an aside, what's a better way of comparing relationships than this?

path.relationships().iterator().next().getType.name() == MyRelationship.toString

Upvotes: 2

Views: 425

Answers (2)

Chris Beach
Chris Beach

Reputation: 4392

Additionally to @StefanArmbruster's answer, here's the equivalent Scala code:

db.traversalDescription()
  .breadthFirst()
  .expand(isAThenBRelationship)

private val isAThenBRelationship = 
   new PathExpander[Object]() {
     override def expand(path: Path, state: BranchState[Object]) =
       path.length() match {
         case 0 => path.endNode().getRelationships(DynamicRelationshipType.withName("A"))
         case 1 => path.endNode().getRelationships(DynamicRelationshipType.withName("B"))
         case _ => Iterables.empty()
       }

     override def reverse(): PathExpander[Object] = ???
   }

Note that the expander must come after the relationships.

Upvotes: 1

Stefan Armbruster
Stefan Armbruster

Reputation: 39915

Using relationships() multiple times does not imply an order. Instead there is a internal list which relationships() adds something to.

To limit a certain relationship type to a certain depth, you need to implement and use your own PathExpander. The example below uses Java and implements the PathExpander using an anonymous inner class:

traversalDescription.expand(new PathExpander<Object>() {
    @Override
    public Iterable<Relationship> expand(Path path, BranchState<Object> state) {
        switch (path.length()) {
            case 0:
                return path.endNode().getRelationships(
                    DynamicRelationshipType.withName("A") );
            case 1:
                return path.endNode().getRelationships(
                    DynamicRelationshipType.withName("B") );
            default:
                return Iterables.empty();
        }
    }

    @Override
    public PathExpander<Object> reverse() {
        // not used for unidirectional traversals
        throw new UnsupportedOperationException();
    }
});

Regarding your second question:

Neo4j contains a very convenient class IteratorUtils. With that your snippet can be written as (assuming MyRelationship is a instance of RelationshipType:

 IteratorUtil.first(path.relationships()).getType().equals(MyRelationship)

Upvotes: 2

Related Questions