Finding nodes with 2 relationships - neo4j

Not sure if the title explains everything but I'm learning Cypher using Neo4j and the Movie Database (from here: https://neo4j.com/developer/example-data/) and I'm trying to craft a query that will give me every movie whose director also acted in it.
To give an example of such a movie, Pulp Fiction - Quentin Tarantino both acted in and directed the movie.
I came up with this query for the above example:
match (m:Movie)-[:ACTS_IN]-(d:Director)-[:DIRECTED]->(n:Movie)
where d.name STARTS WITH 'Q'
return *
But it doesn't seem to work the way I expected it to. I get all the movies he's directed and acted in, whereas I only want the ones where he's done both.

You already had d and m declared, so you don't assign labels in the second MATCH statement again. The correct query would look like this
MATCH (d:Director)-[:DIRECTED]->(m:Movie)
WHERE d.name STARTS WITH 'Q'
MATCH (d)-[:ACTS_IN]->(m)
RETURN d, collect(m) AS movies
Note that based on your initial question, I've modified the return statement as well.

Oh wow I just figured it out:
match (d:Director)-[:DIRECTED]->(m:Movie)
match (d:Director)-[:ACTS_IN]->(m:Movie)
where d.name STARTS WITH 'Q'
return *
I was honestly expecting to have to use some kind of operator to join the two, but I guess I was wrong.

Related

Neo4j: Find Person who acted in and directed any movies

I want to display all Person who act as well as direct a movie. It does not matter if the person direct a movie but does not act in the movie. As long as there are edges ACTED_IN and DIRECTED exist on a node, the query will display the result.
I have tried several Cypher queries. This one I believe show the nearest result I intend to:
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE exists( (p)-[:DIRECTED]->() )
RETURN distinct *
Now, the issue is, one of the result shows "James Marshall" ACTED_IN "A Few Good Men" but he also DIRECTED two different movies which are "Ninja Assasin" and "V for Vendetta".
My current outcome only display "James Marshall" ACTED_IN "A Few Good Men" and does not show the other two movies he DIRECTED. So, how can I improve my Cypher?
You can first match on persons that have the relationships you need (this will be a degree check), then MATCH on the pattern using both relationships at once (which would be an OR match for the relationships in question otherwise):
MATCH (p:Person)
WHERE (p)-[:ACTED_IN]->() AND (p)-[:DIRECTED->()
MATCH path = (p)-[:ACTED_IN | DIRECTED]->(m:Movie)
RETURN path
You can use the solution in this post
The key point is to add another relationships in the other way around (right to the left to the movie), and then add in the WHERE clause the condition.
MATCH (a1:Person)-[:ACTED_IN]->(m:Movie)<-[:ACTED_IN]-(a2:Person)
WHERE exists( (a2)-[:DIRECTED]->(m) ) RETURN a2, m

Neo4j Cypher query - relationship with an "or"

I'm trying to get a named relationship with an or in the query. I'm thinking the query should look similar to:
MATCH (A:person)-[B (:ACTED_IN|:DIRECTED)]->(C:person) RETURN A, B, C
but no matter how I put in the parens I get an error. I suppose a UNION would do the trick but was hoping that there was some way of doing it similar to the above. TIA.
EDIT: This does what I want but seems not the way to do it.
MATCH (A:person)-[B]->(C:person) WHERE type(B)="ACTED_IN" OR type(B)="DIRECTED" RETURN A,B,C
I am a new user so I do not have the option to comment on questions yet. I am guessing you are trying to get the person who either acted in or directed the movie. Its described in official Cypher Documentation: Match on multiple relationship types.
With Demo Movie Data on Neo4j to get persons from The Matrix movie, I would use this:
MATCH (TheMatrix { title: 'The Matrix' })<-[rel:ACTED_IN|:DIRECTED]-(person)
RETURN person.name, rel

AND operator is not working with type(r) in Neo4j

In the Movie Graph which comes with Neo4j as Sample graph, I want to query : Get the movie which has been directed and written by the same person who directed The Matrix movie.
Here is the query which i am using
Match(m:Movie{title:'The Matrix'})<-[r:DIRECTED]-(p:Person)-[r2]->(n:Movie)
where type(r2)='DIRECTED' and type(r2)='WROTE'
return p,n
This query is not working but if i will put OR clause instead of AND clause. It will work. Using OR clause giving me the list of movie which is written or directed by the same director who directed The Matrix movie. Please let me know why AND clause is not working with type(r).
If you have both relationships DIRECTED and WROTE between person and movie you will have two result lines, not one.
Like
a |type(r) |b
a |DIRECTED|b
a |WROTE |b
So when you set or, you get both lines. But when you say and you actually get nothing becaues Directed <>(Directed and Wrote) at the same time
If you mean that there should be both DIRECTED and WROTE relationships between person and Movie, query should be like
Match (m:Movie{title:'The Matrix'})<-[r:DIRECTED]-(p:Person)-[r2:DIRECTED]->(n:Movie),
(p)-[r3:WROTE]->(n)
return p,n
This is probably the most straightforward query for finding all movies that were both written and directed by the director of "The Matrix":
MATCH (:Movie{title:'The Matrix'})<-[:DIRECTED]-(p:Person)
MATCH (n:Movie)<-[:WROTE]-(p)-[:DIRECTED]->(n)
RETURN p,n;

Neo4J contains all Query

I'm brand new to neo4j and graph databases.
I'm trying to create a query I would describe as a 'contains all' however I think I'm very far away and not sure how to progress
MATCH (movie:Movie {name:'tropic thunder'})-[:stars_in]-(actors)
-[:guest_stars_in]-(movie2)
RETURN movie2.name
Let's say
MATCH (movie:Movie{name:'tropic thunder'})-[:stars_in]-(actors)
returns 5 actors
I'm looking to match exactly (all 5 actors -> same 5 actors as guest stars) or as a subset (all 5 actors are a subset of a movie which has 10 guest stars).
Hope that makes sense. Thanks for your help :D
The first thing I would point out is that you should call the variable actor instead of actors. It may seem picky, but it's a common confusion with Cypher. With the MATCH you are matching one sub-pattern at a time.
So to start out let's find each movie2 and get an array of the actors in question:
MATCH (movie:Movie {name:'tropic thunder'})-[:stars_in]-(actor)
-[:guest_stars_in]-(movie2)
RETURN movie2.name, collect(actor)
A first instinct might be to extend the path like so:
MATCH (movie:Movie {name:'tropic thunder'})-[:stars_in]-(actor)
-[:guest_stars_in]-(movie2)-[:guest_starts_in]-(actor2)
But again, we're matching every possible match of that path in the database. So for each actor, we're going to match all possible actor2s, which would lead to duplicates.
What we can do, though, is to take our first query and change the RETURN to a WITH in order to pass our data onto a second part of the query:
MATCH (movie:Movie {name:'tropic thunder'})-[:stars_in]-(actor)
-[:guest_stars_in]-(movie2)
WITH movie2, collect(actor) AS original_movie_actors
MATCH movie2-[:guest_stars_in]-(guest_star)
RETURN movie2.name, original_movie_actors, collect(guest_star) AS guest_stars
This gives us
a list of movies in question
the list of the actors who both stared in "tropic thunder" and guest stared in the movie in question
all guest stars for the movie in question
From here you could probably figure it out in your programming language of choice. But we can figure this out in Cypher too:
MATCH (movie:Movie {name:'tropic thunder'})-[:stars_in]-(actor)
-[:guest_stars_in]-(movie2)
WITH movie2, collect(actor) AS original_movie_actors
MATCH movie2-[:guest_stars_in]-(guest_star)
WITH movie2, original_movie_actors, collect(guest_star) AS guest_stars
RETURN
movie.name,
ALL(guest_star IN guest_stars WHERE guest_star IN original_movie_actors) AS all_matched,
length(original_movie_actors) / length(guest_stars) AS percentage_match
I threw in a percentage_match as a double-check and in case that's useful

Cypher path querying (using Neo4j)

I have a graph datebase so that there is in it some pattern like this one:
(n1)-[:a]->(n2),
(n1)-[:b]->(n2),
(n1)-[:c]->(n2),
(n1)-[:e]->(n2),
(n1)-[:d]->(n3),
(n2)-[:b]->(n4)
And I want to have all graph with this pattern
MATCH p={
(n3)<-[:d]-(n1)-[:a]->(n2)-[:b]->(n4),
(n1)-[:b]->(n2)<-[:c]-(n1),
(n1)-[:e]->(n2)
}
RETURN p
Is it possible? I've search a little but I haven't found how to do it.
I know we can use "|" for a type like this
()-[:a|b]->()
but there is no "&" and the path assigning only works on pattern which are written without ",".
Thanks
EDIT:
If it could help, here is another example of what I'm seeking:
In a database with movies, person and relations like ACTED_IN, KNOWS, FRIEND and HATE
I want all the graphs containing an actor "Actor1" (who ACTED_IN a movie "M") who KNOWS "Person1", FRIEND "Person2" and HATE "Person3" which ACTED_IN the same movie "M".
An UNION like the one in the answer of "Michael Hunger" does not work because we have multiple subgraphs and not graphs. Moreover, some subgraph might not be correct answers for the bigger pattern.
Your query will be very inefficient, as you don't restrict your search to a set of start nodes neither with labels or label+property combinations !!!!
You can use UNION for that:
MATCH p=(n3)<-[:d]-(n1)-[:a]->(n2)-[:b]->(n4) RETURN p
UNION
MATCH p=(n1)-[:b]->(n2)<-[:c]-(n1) RETURN p
UNION
MATCH p=(n1)-[:e]->(n2) RETURN p

Resources