cancel
Showing results forΒ
Did you mean:Β

## Negation of Relationship

Node Clone
``````match  (m:Movie)
where m.title contains "Matrix"
match (c:Person)-[:ACTED_IN]-(d)
where not (c)-[:ACTED_IN]->(m)
return distinct c.name
order by c.name asc
``````

Trying to find all people who didnt act in a Matrix movie. Is there an easier way?

8 REPLIES 8
Neo4j

Actually your query finds people who didn't act in one specific matrix movie but they could have acted in others.

``````match  (m:Movie) where m.title contains "Matrix" with collect(m) as movies
match (c:Person) where size[(c)-[:ACTED_IN]->(d) WHERE d IN movies] = 0
return c.name
order by c.name asc
``````

or

``````match  (m:Movie) where m.title contains "Matrix" with collect(m) as movies
match (c:Person)-[:ACTED_IN]->(d)
with c, collect(d) as actorMovies where none(m in movies where m in actorMovies)
return c.name
order by c.name asc``````
Node Clone

Hmm...But the first two statements collect all the Movies with a title that contains "Matrix" in it - if you return right after that, that's the result you get.

Then, if you run this:
match (m:Movie)
where m.title contains "Matrix"
match (c:Person)-[:ACTED_IN]-> (m)
return c.name, m.title

You get a list of all actors/actresses of all Matrix movies because of the first two statements. So, how is my resulting query only specific to one Matrix movie?

Also, both your queries return an error

(few minutes later)
Modified your second query and seems to work -

``````match  (m:Movie) where m.title contains "Matrix" with collect(m) as movies
match (c:Person)-[:ACTED_IN]->(d)
with d, c, movies, collect(d) as actorMovies where none(m in movies where m in actorMovies)
return c.name
order by c.name asc
``````

But it returns the wrong results. Keanu Reeves, for example, shows up in that result.

Neo4j

remove the `d` you don't want to aggregate by the movie. but only by the person

``````match  (m:Movie) where m.title contains "Matrix" with collect(m) as movies
match (c:Person)-[:ACTED_IN]->(d)
with c, movies, collect(d) as actorMovies where none(m in movies where m in actorMovies)
return c.name
order by c.name asc``````
Node Clone

Can you explain why the query I have doesnt work? That was really my question. Why every other part seems to return what I am looking for but the last few statements do not. See explanation below:

Hmm...But the first two statements collect all the Movies with a title that contains "Matrix" in it - if you return right after that, that's the result you get.

Then, if you run this:
match (m:Movie)
where m.title contains "Matrix"
match (c:Person)-[:ACTED_IN]-> (m)
return c.name, m.title

You get a list of all actors/actresses of all Matrix movies because of the first two statements. So, how is my resulting query only specific to one Matrix movie?

Neo4j

Because the match is executed per row.

Imagine an actor only played in one matrix movie.

so you get 3 rows of matrix movies

and for each of them the person is checked against one
with the negation filtering it out, per movie

so the person is filtered out once and kept twice

so in the end you have the person still returned.

Neo4j

The thing to keep in mind is to only treat a variable as a list of values if it's a collection (either you've used `collect()` to generate it or a pattern comprehension or from a procedure or function that returns lists).

If it's just a variable to a node match (even if potential several nodes match), that variable will only represent a single possible value per row, it can't be treated as a list of all matching variables.

Graph Steward

Just as I start to think I understand Cypher, then I encounter a new thing...

Is there a way to make this Query simpler? Or is there something missing in the Cypher language that would allow this to be more easily expressed?

I find it a bit difficult to puzzle through.

I wasn't able to come up with the query myself. If there was a simpler variant, I think I could understand this better.

(Sort like with a foreign language, it's easy to understand something said to you than trying to produce a complex sentence.)

With Neo4j 4.x we now have existential subqueries, that might be a more readable alternative:

``````MATCH (c:Person)
WHERE NOT EXISTS {
MATCH (c)-[:ACTED_IN]->(m:Movie)
WHERE m.title CONTAINS "Matrix"
}
RETURN c.name
ORDER BY c.name ASC
``````

That said, this won't be as efficient as other queries that begin with matching to and collecting Matrix movies (provided there's an index on :Movie(title) ).

Here's another approach, still collecting Matrix movies, but using a list predicate over a pattern comprehension to make sure that none of the movies the person acted in are matrix movies.

``````MATCH (m:Movie)
WHERE m.title CONTAINS "Matrix"
WITH collect(m) as movies
MATCH (c:Person)
WHERE none(m in [(c)-[:ACTED_IN]->(m) | m] WHERE m IN movies)
RETURN c.name
ORDER BY c.name ASC
``````

EDIT fixed up the ordering clause

Nodes 2022

NODES 2022, Neo4j Online Education Summit

OnΒ November 16 and 17 for 24 hours across all timezones, youβll learn about best practices for beginners and experts alike.

Neo4j Resources