Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.
04-29-2020 11:37 AM
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?
04-29-2020 11:45 AM
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)
04-29-2020 01:32 PM
"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.
04-29-2020 02:23 PM
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:
Or we can do this:
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:
Or we can do this:
04-29-2020 02:39 PM
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.
04-29-2020 03:01 PM
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.
04-29-2020 03:11 PM
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:
OR
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:
OR
04-29-2020 03:28 PM
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?
04-29-2020 03:45 PM
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)
04-29-2020 04:34 PM
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.
04-29-2020 05:03 PM
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.
04-29-2020 05:47 PM
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?
04-29-2020 06:09 PM
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)
04-29-2020 06:40 PM
It's great, and thanks!
All the sessions of the conference are now available online