neo4jcypherneo4jrb

Cypher Query where 2 different labels do not contain a relationship to a 3rd label/node


I have 3 labels, A, B, and Z. A & B both have a relationship to Z. I want to find all the A nodes that do not have share any of nodes Z in common with B

Currently, doing a normal query where the relationship DOES exist, works.

MATCH (a:A)-[:rel1]->(z:Z)<-[:rel2]-(b:B { uuid: {<SOME ID>} })
RETURN DISTINCT a

But when I do

MATCH (a:A)
WHERE NOT (a)-[:rel1]->(z:Z)<-[:rel2]-(b:B { uuid: {<SOME ID>} }))
RETURN DISTINCT a

It throws an error

Neo4j::Server::CypherResponse::ResponseError: z not defined

Not sure if the syntax for this is incorrect, I tried WHERE NOT EXIST() but no luck.

The query is part of a larger one called through a rails app using neo4jrb / (Neo4j::Session.query)


Solution

  • This is a problem to do with the scope of your query. When you describe a node in a MATCH clause like the below

    MATCH (n:SomeLabel)
    

    You're telling cypher to look for a node with the label SomeLabel, and assign it to the variable n in the rest of the query, and at the end of the query, you can return the values stored in this node using RETURN n (unless you drop n by not including it in a WITH clause).

    Later on in you query, if you want to MATCH another node, you can do it in reference to n, so for example:

    MATCH (m:SomeOtherLabel)-[:SOME_RELATIONSHIP]-(n)
    

    Will match a variable connected (in any direction) to the node n, with a label SomeOtherLabel, and assign it to the variable m for the rest of the query.

    You can only assign nodes to variables like this in MATCH, OPTIONAL MATCH, MERGE, CREATE and (sort of) in WITH and UNWIND clauses (someone correct me here if I've missed one, I suppose you also do this in list comprehensions and FOREACH clauses).

    In your second query, you are trying to find a node with the label A, which is not connected to a node with the label Z. However, the way you have written the query means that you are actually saying find a node with label A which is not connected via a rel1 relationship to the node stored as z. This will fail (and as shown, neo complains that z is not defined), because you can't create a new variable like this in the WHERE clause.

    To correct your error, you need to remove the reference to the variable z, and ensure you have also defined the variable b containing your node before the WHERE clause. Now, you keep the label in the query, like the below.

    MATCH (a:A)
    MATCH (b:B { uuid: {<SOME ID>} })
    WHERE NOT (a)-[:rel1]->(:Z)<-[:rel2]-(b) // changed this line
    RETURN DISTINCT a
    

    And with a bit of luck, this will now work.