Looking for nodes without a specific relation

person-[:executes]-transaction

sql equivalent would be
select * from person where not exists (select * from transaction where transaction.owner = person.id)

You're looking for a :person node without an :executes relationship to a :transaction?

The simplest form would be:

MATCH (p:person)
WHERE NOT (p)-[:executes]-(:transaction)
RETURN p

If all :executes relationships from :person nodes always connect to :transaction nodes, then you can leave off the :transaction label and the plan will use a degree check of :executes relationships on :person nodes, which will be much more efficient:

MATCH (p:person)
WHERE NOT (p)-[:executes]-()
RETURN p
6 Likes

thx, solved my problem

This is an elegant way to express a matching relationship. Is there an easy way to get the negation of it? (i.e. without the WHERE clause?)

MATCH (p:Person)-[:WROTE]->(:Movie {title: 'Speed Racer'}) RETURN p.name

Are you referring to a way to express the negation of a relationship? There's no way to do that only using the MATCH clause, it has to be in the WHERE clause.

If you're looking to negate (or filter on the presence of) a pattern that requires a WHERE clause (such as if there's a range filter or something that can't be captured just in the pattern) then you should use existential subqueries, introduced in Neo4j 4.x:

1 Like

Hello,

What if instead of a single :person node, we're interested in writing one cypher query that lists more than 2 nodes without a specific relationship?

Let's say we have the nodes :person, :transaction, :city and we want the results like this table:

Name of Node | Number of Disconnected Nodes |
__________________________________
   person      |               55|
   transaction |               43| 
   city        |               21|

Writing the cypher query to obtain the number of disconnected nodes was straight forward, but I'm struggling with the formatting now so that I can get my results like the table above.

I got a little close to my desired results by following the instructions on this thread

I wrote this cypher query but the format is not the desired results even though it shows the correct values:

MATCH(p:person)
WHERE NOT (p)-[:executes]->(:transaction)
RETURN COUNT(DISTINCT p) AS `Disconnected Person Nodes`, NULL AS `Disconnected Transaction Nodes`, NULL AS `Disconnected City Nodes`
UNION
MATCH(p:transaction)
WHERE NOT (p)<-[:contains]-(:bank)
RETURN NULL AS `Disconnected Person Nodes`, COUNT(DISTINCT p) AS `Disconnected Transaction Nodes`, NULL AS `Disconnected City Nodes`
UNION
MATCH(p:city)
WHERE NOT (p)<-[:lives_in]-(:person)
RETURN NULL AS `Disconnected Person Nodes`, NULL AS `Disconnected Transaction Nodes`, COUNT(DISTINCT p) AS `Disconnected City Nodes`

This query gives me the following table:

Disconnected Person Nodes | Disconnected Transaction Nodes | Disconnected City Nodes |
____________________________________________________________________________________
   55                     |                            null|                   null| 
   null                   |                              43|                   null|
   null                   |                            null|                     21|

What would be the best way to format the output as desired?

Thanks,
Tony

Try this:

MATCH(p:person)
WHERE NOT (p)-[:executes]->(:transaction)
RETURN labels(p) as lbl, COUNT(DISTINCT p) AS `Disconnected Nodes`
UNION
MATCH(p:transaction)
WHERE NOT (p)<-[:contains]-(:bank)
RETURN labels(p) as lbl, COUNT(DISTINCT p) AS `Disconnected Nodes`
UNION
MATCH(p:city)
WHERE NOT (p)<-[:lives_in]-(:person)
RETURN labels(p) as lbl, COUNT(DISTINCT p) AS `Disconnected Nodes'
1 Like

Thank you! Works perfectly in other queries too where I'm implementing similar logic.