Question

I am trying to impose restrictions on my path match pattern. I would like to match the next relationship based on the type of the previous used relationship.

Here is an example of a simplified Database:

(A)-1-(B)-2-(C)-1-(E)-2-(F)
       |           |
       3----(D)----3

Query 1:

start n=node(A), m=node(F) 
match p=n-[r*]-m
return p

should result both paths

(A)-1-(B)-2-(C)-1-(E)-2-(F)
(A)-1-(B)-3-(D)-3-(E)-2-(F)

However, when running the query starting from node (F):

start m=node(F),n=node(A)
match p=m-[r*]-n
return p

The result should be only:

(F)-2-(E)-1-(C)-2-(B)-1-(A)

Path

(F)-2-(E)-3-(D)-3-(B)-1-(A)

should not be valid, since it violates these constrains:

  1. Coming from a -1- type relationship you can proceed to either a -2- or -3- relationship.
  2. Coming from a -2- or -3- type relationship you can only proceed to a -1- relationship.

These paths are valid:

()-1-()-2-()
()-1-()-3-()
()-2-()-1-()
()-3-()-1-()

These path are not valid:

()-3-()-2-()
()-2-()-3-()
Was it helpful?

Solution

First, upvote for the very detailed, specific, and well laid out question.

Unfortunately, I don't think it's possible to do what you want to do with Cypher, I think you need the Traversal API to do this. Cypher is a declarative language; that is, you tell it what you want, and it goes and gets it for you. Using the traversal API is an imperative approach to query; that is, you tell neo4j exactly how to traverse the graph.

Your query here imposes constraints about the order in which relationships get traversed, and what makes a valid path. Nothing wrong with that, but I believe that imposing constraints on the order of traversal implicitly means you're telling cypher which way to traverse, and you just can't do that with cypher because it's declarative. Another common example of the declarative vs. imperative thing is breadth-first vs. depth-first search. If you're looking for certain nodes, you can't tell cypher to traverse breadth-first vs. depth-first; you just tell it which nodes you want, and it goes and gets them.

Now, paths can be treated like collections in cypher via the relationships() function. And you can use the filter and reduce functions to work with individual relationships. But your query is harder in that you need code that says something like "If the first relationship in a path is a 1, then the next must be a 2 or a 3". This is exactly the sort of thing that you can do with Evaluators in the Traversal API. Check the interface and you can see how you could write your own method that would implement exactly the logic you're talking about via Evaluator#evaluate(Path path).

As a general note, because declarative query (cypher) hides traversal details from you, IMHO it's always better to use declarative query for ease, if you can specify what you want declaratively. But there are cases where you have to control the order of traversal, and for that you need traversal. (I have had cases where I need all nodes connected to something else, via breadth-first search only, to a maximum depth of 3, along complex relationship criteria -- I couldn't use cypher for that).

To give you a way forward, check the link I provided on the traversal framework. Perhaps you can describe your query as a TraversalDescription, and then hand it off to neo4j to run.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top