0
votes

I was practicing with the Movie Database from Neo4j in order to practice and I have done the next query:

MATCH (a:Person)-[:ACTED_IN]->(m:Movie)<-[:DIRECTED]-(a)
RETURN a

This query returns 3 rows but If I go to the graph view on the web editor and expand the "Tom Hanks" node I, of course, have one movie such that Tom Hanks directed and acted in that movie but the rest of the connected nodes only have the ACTED_IN relation. What I want to do is to, in this case, filter and remove Tom Hanks from the result since he has at least one connection such that it has only one relation (either ACTED_IN or DIRECTED)

PD: My expected result would be only the row representing node "Clint Eastwood"

2

2 Answers

1
votes

So you only want results where the person acted in and directed the same movies, but never simply acted in, without directing, or directed, without acting.

You could use this approach:

MATCH (a:Person)-[:ACTED_IN]->(m:Movie)<-[:DIRECTED]-(a)
WITH a, count(m) as actedDirectedCount
WHERE size((a)-[:ACTED_IN]->()) = actedDirectedCount AND size((a)-[:DIRECTED]->()) = actedDirectedCount
RETURN a

Though you can simplify this a bit by combining the relationship types in the pattern used in your WHERE clause like so:

MATCH (a:Person)-[:ACTED_IN]->(m:Movie)<-[:DIRECTED]-(a)
WITH a, count(m) as actedDirectedCount
WHERE size((a)-[:ACTED_IN|DIRECTED]->()) = actedDirectedCount * 2
RETURN a

If the actedDirectedCount = 3 movies, then there must be at a minimum 3 :ACTED_IN relationships and 3 :DIRECTED relationships, so a minimum of 6 relationships using either relationship. If there are any more than this, then there are additional movies that they either acted in or directed, so we'd filter that out.

0
votes

There options come to my mind:

1.

MATCH (m:Movie)<-[:DIRECTED]-(a:Person)
with a, collect(distinct m) as directedMovies 
match (a)-[:ACTED_IN]->(m:Movie)
with a, directedMovies, collect(distinct m) as actedMovies
with  a where all(x in directedMovies where x in actedMovies) and all(x in actedMovies where x in directedMovies)
return a

2.

MATCH (m:Movie)<-[:DIRECTED]-(a:Person) 
with * order by id(m)
with a, collect(distinct m) as directedMovies 
match (a)-[:ACTED_IN]->(m:Movie) 
with a, directedMovies, m order by id (m)
with a, directedMovies, collect(distinct m) as actedMovies
with  a where actedMovies=directedMovies
return a
  1. MATCH (m:Movie)<-[:DIRECTED]-(a:Person) 
    with a, collect(distinct m) as directedMovies 
    with * where all(x in directedMovies where (a)-[:ACTED_IN]->(x))
    MATCH (m:Movie)<-[:ACTED_IN]-(a) 
    with a, collect(distinct m) as actedMovies 
    with * where all(x in actedMovies where (a)-[:DIRECTED]->(x))
    return a
    

The first two are equally expensive and the last one is a bit more expensive.