简体   繁体   中英

Neo4j - Find nodes who never had any relationship with a different node

Assuming that I have an example with nodes and relationships like this:

(:Node_A) -[:rel_a]-> (:Node_B) -[:rel_b]-> (:Node_C)

I want to find all the nodes of Node_A who never had relationship with a (:Node_C {label:'A'}) .

I tried:

MATCH (a:Node_A) -[:rel_a]-> (b:Node_B), (c:Node_C {label:'A'})
WHERE NOT (b) -[:rel_b]-> (c)
RETURN a

but I didn't get the expected result.

In case I have these nodes and relationships, I don't want node_a to be returned.

(node_a:Node_A) -[:rel_a]-> (b1:Node_B),
(node_a:Node_A) -[:rel_a]-> (b2:Node_B),

(b1:Node_B) -[:rel_b]-> (c:Node_C {label:'A'}),
(b1:Node_B) -[:rel_b]-> (c1:Node_C {label:'B'}),

(b2:Node_B) -[:rel_b]-> (c2:Node_C {label:'C'})

How can I match nodes that never had any relationship with a (:Node_C {label:'A'}) ?

The reason you didn't get the expected result is because there IS a path matching the given pattern (a Node_A node connected to a Node_B node such that the Node_B node isn't connected to a Node_C node with label:'A' ). This is because, according to the description of your example graph, you have 2 :Node_B nodes connected to your single :Node_A node. One of them is connected to two :Node_C nodes, one of which is the :Node_C node that you're trying to avoid (and in that case THAT path with that :Node_B node gets filtered out because of your WHERE clause), and the other :Node_B node is connected to the safe :Node_C node with the label 'C', and this is the path that fits your query and is returned.

There are a few ways you can get the filtering you want.

One is to define the full pattern you want excluded entirely into the WHERE clause, and leaving the :Node_B part out of the MATCH clause:

MATCH (a:Node_A), (c:Node_C {label:'A'})
WHERE NOT (a) -[:rel_a]-> (:Node_B) -[:rel_b]-> (c)
RETURN a

If you don't know or don't care about the intermediate nodes or relationships between a and c , you can omit them from the pattern:

MATCH (a:Node_A), (c:Node_C {label:'A'})
WHERE NOT (a) --> () --> (c)
RETURN a

You can express the same thing using variable-length relationships where we know c can only be 2 hops away:

MATCH (a:Node_A), (c:Node_C {label:'A'})
WHERE NOT (a) -[*2]-> (c)
RETURN a

EDIT

stdob-- correctly points out that for all these approaches, we've assumed that a :Node_C node with label:'A' exists in the graph. If this isn't guaranteed, and it so happens that there IS no such node, then these queries won't return anything back.

If we have to cope with that potential case, then it's best to move this from the MATCH into the WHERE clause:

MATCH (a:Node_A)
WHERE NOT (a) -[*2]-> (:Node_C {label:'A'})
RETURN a

In fact, that's probably a good thing to do in any case, as if we had multiple nodes like this instead of only one it could mess up our results if we kept this in the MATCH clause.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM