cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

Why doesn't the reverse relationship query work?

lingvisa
Graph Fellow

I have a query:
MATCH p=(prod:_product)-[r:hasBrand]->(brand {_name:'Apple'}) RETURN prod._name LIMIT 3

This gives me a list of products for the brand Apple. However, if I reverse the direction, I got empty results:
MATCH p=(prod:_product)<-[r:hasBrand]-(brand {_name:'Apple'}) RETURN prod._name LIMIT 3

I remember the query can be issued in any direction, but why it doesn't work?

13 REPLIES 13

Relationships in Neo4j are always directed. If you only have (:_product)-[:hasBrand]->(brand) patterns in your db, then it is impossible for them to MATCH to a pattern with the relationship pointing in the other direction, since there are no :hasBrand relationships incoming to :_product nodes in your graph.

We can traverse relationships in either direction, but that doesn't mean matching to nonexistent patterns. It means that we can traverse a relationship outgoing (from prod to brand in your graph) or incoming (from brand to prod).

The existing direction of the relationship doesn't enforce which way we are able to traverse. So if the relationship is pointing in one direction, that doesn't mean that we must always expand one-way. We can expand from the other direction too. But none of that is actually handled by you. The planner decides where to find the start nodes in the query, and how to traverse.

Now if you want to MATCH to a pattern and the direction doesn't matter to you, only that a relationship exists, then you can omit the directionality of the relationship in your MATCH pattern: (prod)-[r:hasBrand]-(brand)

"So if the relationship is pointing in one direction, that doesn't mean that we must always expand one-way. We can expand from the other direction too. "

The statement is true on the condition that the query must written in this un-directional form:
(prod)-[r:hasBrand]-(brand)

If we want the flexibility of travelling in both directions, we shouldn't use a direction in our query, because normally only one-direction is built in graph.

We're talking about different things here. Let me give you a simplified example.

Say that you draw this on a whiteboard, with circles for the nodes and a line and arrow for the relationship:

(:A)-[:REL]->(:B)

Now you need to perform MATCH (:A)-[:REL]->(:B)

We can do this one of two ways:

  1. Start at A (place a finger on it)
  2. Traverse the relationship between A and B (the relationship must be outgoing from A, incoming to B; we follow the relationship line with our finger)
  3. We have arrived at B, the path has been found.

Or we can do this:

  1. Start at B (place a finger on it)
  2. Traverse the relationship between B and A (the relationship must be outgoing from A, incoming to B; we follow the relationship line with our finger)
  3. We have arrived at A, the path has been found.

In either of these cases, we can start at A or B and traverse the relationship in either direction, but no matter what direction we traverse this relationship, the relationship itself must be outgoing from A, and incoming to B. So the direction of the relationship has NOTHING to do with which direction we traverse the nodes during expansion, but it is a restriction on the pattern.

Now, say that we try MATCH (:A)<-[:REL]-(:B), a pattern which doesn't actually exist in the graph. There are two ways to do this:

  1. Start at A (place a finger on it)
  2. We can't traverse any relationship, since there ARE no relationships in the graph that are incoming to A to follow
  3. No paths can be found, nothing is returned.

Or we can do this:

  1. Start at B (place a finger on it)
  2. We can't traverse any relationship, since there ARE no relationships in the graph that are outgoing from B to follow
  3. No paths can be found, nothing is returned.

  1. Start at B (place a finger on it)
  2. Traverse the relationship between B and A (the relationship must be outgoing from A, incoming to B; we follow the relationship line with our finger)
  3. We have arrived at A, the path has been found.

You start at 'B', but the relationship is outgoing from "A". Isn't this contradictory? It seem when you stand at one end of a bridge, you want to start from the other end.

That's what I'm trying to convey. The direction of the relationship in the graph has NOTHING to do with HOW you traverse the relationship, provided that the direction of the relationship in the graph matches the direction of the relationship in the requested MATCH pattern.

So whether we're starting at A and going to B, or starting at B and going to A, what really matters is that in the graph, you have (:A)-[:REL]->(:B), and in your MATCH pattern you have (:A)-[:REL]->(:B). That's all. Cypher doesn't dictate how the pattern is to be found, whether it starts on A or B, it just cares about finding all paths that match the pattern.

The direction is not an indicator restricting how you move across the graph as you attempt to find patterns that match.

As another example, say that you're following a map and walking in a city, and you're trying to figure out if this exists:

Intersection A -> Intersection B

Let's say the intersections are labeled, and there is a one-way street between the two intersections. Note that you yourself are on foot and not subject to traffic signs.

You are looking for the existence of a path that fits this pattern: Intersection A -> Intersection B

There are two ways to do this:

  1. You start at Intersection A. You see that there is an OUTGOING one way street toward an intersection, but you don't know what the other intersection is.
  2. You walk toward the other intersection, in the direction of the OUTGOING one way street.
  3. You arrive at Intersection B. Yes! You've found a path that matches the pattern!

OR

  1. You start at Intersection B. You see that there is an INCOMING one way street from an intersection, but you don't know what the other intersection is.
  2. You walk toward the other intersection, in the direction of the INCOMING one way street.
  3. You arrive at Intersection A. Yes! You've found a path that matches the pattern!

But now, assume that we're looking for a path that fits this pattern, which doesn't actually exist: Intersection A <- Intersection B

There are two ways to do this:

  1. You start at Intersection A. You see that there is no INCOMING one way street from an intersection. There is an OUTGOING one way street (to B), but that isn't what you're looking for according to the match pattern.
  2. No path exists that match the pattern you're looking for.

OR

  1. You start at Intersection B. You see that there is no OUTGOING one way street to another intersection. There is an INCOMING one way street (from A), but that isn't what you're looking for according to the match pattern.
  2. No path exists that match the pattern you're looking for.

Can you please take a look at this answer, in which the accepted answer just reversed the direction of the query. How that is possible based on discussion here?

https://stackoverflow.com/questions/21383476/what-is-the-query-to-access-the-nodes-in-reverse-to-a-r...

For one, the START syntax is old and has been removed from Neo4j for some time, so don't use it. This is an old answer using an old syntax.

Also, that Stack Overflow answer is flawed.

The reason for that is likely the confusing form of that question. Here, look at the two patterns noted in the question:

$ start n = node:users(username='user1') match n-[r:HAS_COMMENT] -> a return a;    

from the user node (n), they want to get the comments (a) where the pattern (n)-[:HAS_COMMENT]->(a) exists.

and

$ start n = node:comments(_id='c101') match n-[r:HAS_COMMENT] -> a return a;

from the comment(!) (n), they want to get the user (a), their pattern here is wrong, since in this one it's looking for a :HAS_COMMENT relationship in the wrong direction (comments do NOT have outgoing :HAS_COMMENT relationships to users!)

Note that they changed their starting label, but kept the variables the same, so n in the first query is for the user, but in the second query it's the comment! In the first query, a is the comment, but in the second a is now the user! Since they kept the variables the same, but didn't change their direction, it specified a pattern that doesn't exist, so they got no results back.

FrobberOfBits is actually a great answerer of Neo4j questions on Stack Overflow, but they typo'd a bit on this answer. Here's what they likely meant to put:

start n = node:comments(_id='c101') match n<-[r:HAS_COMMENT] - a return a;

start n = node:comments(_id='c101') match a-[r:HAS_COMMENT] -> n return a;

Note that in both cases n is the comment, and a is the user. The pattern can be specified in any order, as long as the relationship direction is going from a user (a) to a comment (n)

That makes sense. So at the end of the day, if I want to guarantee the relationship results between two nodes, and it's known that there is only one direction of the relationship, I should leave the direction unspecified in the query.

I wouldn't say that.

If you know the relationship can only be in a certain direction, or if for a particular query you're interested only in a certain direction, then keep the the relationship directional in the MATCH pattern. Having the direction present may help improve the speed of the query, in that it may reduce the relationships it has to check and filter.

Only leave off the direction if you don't know or don't care about the relationship's direction.

For example, if you have a graph of people with :KNOWS relationships between them, the direction doesn't really matter, since it really implies that the people know each other, so you can omit the direction in your MATCH pattern.

But if you have :LOVES relationships in the graph, then depending on the question you're asking, the direction may matter greatly, as it means the difference between "do I love someone" and "does someone love me", depending, and these relationships don't imply reciprocation.

If we're querying a hierarchy and you know the direction of the relationships, there's no way for it to go the other direction, then keep the direction in the MATCH pattern.

lingvisa
Graph Fellow

Then, still taking this as one example:

(prod)-[r:hasBrand]->(brand)

Given a product, to find brand is fine; what about given a brand, I want to find their products? What's the query?

It's the same pattern, just a different WHERE clause (and likely a different RETURN).

So for examples:

MATCH (prod:Product)-[:hasBrand]->(brand:Brand)
WHERE prod.name = "Chocolate"
RETURN brand

vs

MATCH (prod:Product)-[:hasBrand]->(brand:Brand)
WHERE brand.name = "Twix"
RETURN prod

Also it doesn't matter if you use the above pattern, or reverse it:

(brand:Brand)<-[:hasBrand]-(prod:Product)

It's great, and thanks!