Neo4j and unidirectional relationships
I am new to neo4j
. I just read some information about this tool, installed it on Ubuntu and made a bunch of requests. And at this point I have to admit that I really like it. However, there is something (very simple and intuitive I think) that I don't know how to implement. So, I created three such nodes:
CREATE (n:Object {id:1}) RETURN n
CREATE (n:Object {id:2}) RETURN n
CREATE (n:Object {id:3}) RETURN n
And I created a hierarchical relationship between them:
MATCH (a:Object {id:1}), (b:Object {id:2}) CREATE (a)-[:PARENT]->(b)
MATCH (a:Object {id:2}), (b:Object {id:3}) CREATE (a)-[:PARENT]->(b)
So, I think this simple hierarchy should look like this:
(id:1)
-> (id:2)
-> (id:3)
Now I want to get the path from any node. For example, if I want to have a path from node (id: 2), I will get (id: 2) → (id: 3). And if I want to get the path from node (id: 1), I get (id: 1) -> (id: 2) -> (id: 3). I tried this query:
MATCH (n:Object {id:2})-[*]-(children) return n, children
which should return the path (id: 2) -> (id: 3), but unexpectedly (just for me) it returns (id: 1) -> (id: 2) -> (id: 3). So what am I doing wrong and what is the correct query to use?
source to share
All relationships in neo4j are directed. When you speak (n)-[:foo]->(m)
, this relationship only goes one way: from n
to m
.
Now the difficulty is that you can navigate both paths. It doesn't make the relationship bi-directional, it never will - it means you can look at it in any direction.
When you write this request:, (n:Object {id:2})-[*]-(children)
you have not put an arrow on this link, so it children
can refer to something either downstream or upstream of node.
In other words, the expression (n)-[:test]-(m)
matches both (n)<-[:test]-(m)
and (n)-[:test]->(m)
.
So it children
can refer to object ID 1 or object ID 2.
source to share
Return of children only
To answer your question,
Your request
MATCH (n:Object {id:2})-[*]-(children) return n, children
matches not only the FROM (n {id:2})
TO relationship of its children, but also the TO (n {id:2})
FROM relationship of its parents.
You need to additionally indicate the direction you want. This returns the expected results:
MATCH (n:Object {id:2})-[*]->(children) return n, children
Example problems
I would like to respond to your comment about unidirectional versus bidirectional relationships, but first solve a couple of problems with an example.
Using the correct labels
Repeat your example:
(:Object {id:1})-[:PARENT]->(:Object {id:2})-[:PARENT]->(:Object {id:3})
It makes no sense to use such labels as :Object
, :Node
, :Thing
. If you really don't care, don't use the shortcut at all!
In this case, it looks like we are talking about humans, although it could easily be motherboards and daughterboards or something else!
Let's use "People" instead of "Objects":
(:Person {id:1})-[:PARENT]->(:Person {id:2})-[:PARENT]->(:Person {id:3})
ID in Neo4j
Neo4j stores its own IDs for each node and relationship. You can get these IDs with id(nodeOrRelationship)
and access by ID with suggestion WHERE
or by specifying them as a starting point for your match. START n=node(2) MATCH (n)-[*]-(children) return n, children
is equivalent to the original query MATCH (n:Object {id:2})-[*]-(children) return n, children
.
Let's keep something useful about nodes instead of identifiers, like names:
(:Person {name:'Bob'})-[:PARENT]->(:Person {name:'Mary'})-[:PARENT]->(:Person {name:'Tom'})
Ambiguity of relationship
Finally, let's get the relationship out of alignment. Does it mean PARENT
"parent" or "has this parent"? It may be clear to you what you mean, but someone unfamiliar with your system might have the opposite interpretation.
I think you meant "is the parent", so let's make it clear:
(:Person {name:'Bob'})-[:PARENT_OF]->(:Person {name:'Mary'})-[:PARENT_OF]->(:Person {name:'Tom'})
More information on unidirectional and bidirectional communications in Neo4j
Now that we've covered a few basic issues with the example, let's turn to the directionality of relationships in Neo4j and charts in general.
There are several ways that we could express the relationship in this example. Let's take a look at a few.
Indirect / bi-directional relationship
Let's abstract the parenting relationship we used above for discussion purposes:
(bob)-[:KIN]-(mary)-[:KIN]-(tom)
Here the relationship KIN
indicates that they are related, but we don't know exactly who the parent is. Is Tom Mary's child or vice versa?
Note that I have not used any arrows. In the above plot pseudocode, the relationship KIN
is a bidirectional or undirected relationship.
Relationships in Neo4j, however, are always directed. If attitude KIN
was really how you wanted to keep track of things, then you would create a directional connection, but always ignore direction in your requests MATCH
, for example. MATCH (a)-[:KIN]-(b)
rather than MATCH (a)-[:KIN]->(b)
.
But is an attitude really the KIN
best way to store this information? We can make this more specific. Let's go back to the relationships PARENT_OF
we used earlier.
Directional / unidirectional ratio
Let's go back to the example. We know that Bob is the parent of Mary, who is Tom's parent:
(bob)-[:PARENT_OF]->(mary)-[:PARENT_OF]->(tom)
Obviously, the consequence of this is:
(bob)<-[:CHILD_OF]-(mary)<-[:CHILD_OF]-(tom)
Or, equivalently:
(tom)-[:CHILD_OF]->(mary)-[:CHILD_OF]->(bob)
So, whether we should continue and build relationships PARENT_OF
and CHILD_OF
between our (bob), (mary) and (tom) sites?
The answer is no. We can choose one of these relationships, depending on which is the best model of the idea, and still be able to search in both directions.
Using just a relation :PARENT_OF
, we can do
MATCH (mary {name:'Mary'})-[:PARENT_OF]->(children) RETURN children
to find children, or
MATCH (mary {name:'Mary'})<-[:PARENT_OF]-(parents) RETURN parents
to find parents using (mary) as a starting point every time.
For more information see this fantastic article from GraphAware
source to share