Jacobian
Jacobian

Reputation: 10802

neo4j and uni-directional relationship

I'm new to neo4j. I've just read some information on this tool, installed it on Ubuntu and made a bunch of queries. And at this moment, I must confess, that I realy like it. However, there is something (I think very simple and intuitive), which I do not know how to implement. So, I created three nodes like so:

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)

What I want now is to get a 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 a path from node (id:1), I will get (id:1)->(id:2)->(id:3). I tried this query:

MATCH (n:Object {id:2})-[*]-(children) return n, children

which I though should return a path (id:2)->(id:3), but unexpectedly (just for me) it returns (id:1)->(id:2)->(id:3). So, what I'm doing wrong and what is the right query to use?

Upvotes: 3

Views: 360

Answers (2)

Codebling
Codebling

Reputation: 11382

Returning only children

To directly answer your question,

Your query

MATCH (n:Object {id:2})-[*]-(children) return n, children

matches not only relationships FROM (n {id:2}) TO its children, but also relationships TO (n {id:2}) FROM its parents.

You need to additionally specify the direction that you'd like. This returns the results you expect:

MATCH (n:Object {id:2})-[*]->(children) return n, children

Issues with the example

I'd like to answer your comment about uni-directional and bi-directional relationships, but let's first resolve a couple of issues with the example.

Using correct labels

Let's revisit your example:

(:Object {id:1})-[:PARENT]->(:Object {id:2})-[:PARENT]->(:Object {id:3})

There's no point to using labels like :Object, :Node, :Thing. If you really don't care, don't use a label at all!

In this case, it looks we're talking about people, although it could easily also 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})

IDs in Neo4j

Neo4j stores its own IDs of every node and relationship. You can retrieve those IDs with id(nodeOrRelationship), and access by ID with a WHERE clause or by specifying them as a start point for your match. START n=node(2) MATCH (n)-[*]-(children) return n, children is equivalent to your original query MATCH (n:Object {id:2})-[*]-(children) return n, children.

Let's, instead of IDs, store something useful about the nodes, like names:

(:Person {name:'Bob'})-[:PARENT]->(:Person {name:'Mary'})-[:PARENT]->(:Person {name:'Tom'})

Relationship ambiguity

Lastly, let's disambiguate the relationships. Does PARENT mean "is the parent of", or "has this parent"? It might be clear to you which one you meant, but someone unfamiliar with your system might have the opposite interpretation.

I think you meant "is the parent of", so let's make that clear:

(:Person {name:'Bob'})-[:PARENT_OF]->(:Person {name:'Mary'})-[:PARENT_OF]->(:Person {name:'Tom'})

More information about uni-directional and bi-directional relationships in Neo4j

Now that we've taken care of a few basic issues with the example, let's address the directionality of relationships in Neo4j and graphs in general.

There are several ways we could have expressed the relationships this example. Let's look at a few.

Undirected/bidirectional relationship

Let's abstract the parent relationship that we used above, for the purposes of discussion:

(bob)-[:KIN]-(mary)-[:KIN]-(tom)

Here the relationship KIN indicates that they are related but we don't know exactly who is the parent of whom. Is Tom the child of Mary, or vice-versa?

Notice that I didn't use any arrows. In the graph pseudo-code above, the KIN relationship is a bidirectional or undirected relationship.

Relationships in Neo4j, however, are always directional. If the KIN relationship was really how you wanted to track things, then you'd create a directional relationship, but always ignore the direction in your MATCH queries, e.g. MATCH (a)-[:KIN]-(b) and not MATCH (a)-[:KIN]->(b).

But is the KIN relationship really the best way to store this information? We can make it more specific. Let's go back to the PARENT_OF relationship that we were using earlier.

Directed/unidirectional relationship

Back to the example. We know that Bob is the parent of Mary who is the parent of Tom:

(bob)-[:PARENT_OF]->(mary)-[:PARENT_OF]->(tom)

Obviously, the corollary of this is:

(bob)<-[:CHILD_OF]-(mary)<-[:CHILD_OF]-(tom)

Or, equivalently:

(tom)-[:CHILD_OF]->(mary)-[:CHILD_OF]->(bob)

So, should we go ahead and create both the PARENT_OF and the CHILD_OF relationships between our (bob), (mary) and (tom) nodes?

The answer is no. We can pick one of those relationships, whichever best models the idea, and still be able to search both ways.

Using only the :PARENT_OF relationship, we can do

MATCH (mary {name:'Mary'})-[:PARENT_OF]->(children) RETURN children

to find the children, or

MATCH (mary {name:'Mary'})<-[:PARENT_OF]-(parents) RETURN parents

to find the parents, using (mary) as the starting point each time.


For more information, see this fantastic article from GraphAware

Upvotes: 1

FrobberOfBits
FrobberOfBits

Reputation: 18002

All relationships in neo4j are directed. When you say (n)-[:foo]->(m), that relationship goes only one way, from n to m.

Now what's tricky about this is that you can navigate the relationship both ways. This doesn't make the relationship bi-directional, it never is -- it only means that you can look at it in either direction.

When you write this query: (n:Object {id:2})-[*]-(children) you didn't put an arrow head on that relationship, so children could refer to something either downstream or upstream of the node in question.

In other words, saying (n)-[:test]-(m) is the same thing as matching both (n)<-[:test]-(m) and (n)-[:test]->(m).

So children could refer to the ID 1 object or ID 2 object.

Upvotes: 3

Related Questions