How to use the result of another query? - neo4j

I was making some requests on Neo4j, and I've a little trouble at making one in particular.
These are the nodes and relationships I have on my database
Two of the elements:
I want to return the persons who are driving ("conduire") the same "Voiture" as Alice.
This is what I wrote:
MATCH ((a:Personne)-[p:Conduire]->(b:Vehicule))
WHERE b.`Type`='Voiture' AND id(b) in [MATCH ((a:Personne)-[p:Conduire]->(b:Vehicule))
WHERE a.Nom='Alice'
RETURN id(b)]
RETURN a.Nom

Here is the query to return the persons who are driving the same car as Alice. Note that this will not return Alice because the match on b will not repeat the match made on a.
MATCH (a:Personne)-[:Conduire]->(:Vehicule)<-[:Conduire]-(b:Personne)
WHERE a.Nom='Alice'
RETURN b.Nom

Related

Return nodes with only one specified relationship

i have a graph like this:
What i wanna do, is to return actors, which only played in one specified film.
So for example if I pass an Iron Man 2 id, it should return only Robert Downey. Scarlett Johansson is skipped because she has played in 3 films, and also she has a "IS_FRIEND_WITH" relationship.
This code which i wrote, return all the actors that played in film of id 11 in this case
MATCH (a)
WHERE ID(a) = 11
MATCH (b:Actor)-[:ACTED_IN]-(a:Movie)
RETURN b
If Iron Man 2 would be of Id = 11 it would return Scarlett and Robert Downey. I just want it to return only Robert Downey.
You need to apply another filter to select only nodes that have a single ACTED_IN relationship.
MATCH (a:Movie)
WHERE ID(a) = 11
MATCH (b:Actor)-[:ACTED_IN]-(a)
// apply filter for nodes that have only a single ACTED_IN relationship
WITH b, size((b)-[:ACTED_IN]->()) as number_of_movies
WHERE number_of_movies = 1
RETURN b
I would do
WITH 11 AS movieId
MATCH (a:Actor)
WHERE [(a)-[:ACTED_IN]->(m:Movie) | id(m)] = [movieId]
RETURN a
As an alternative to the other answers you could do
MATCH (a:Movie)
WHERE ID(a) = 11
MATCH (b:Actor)-[:ACTED_IN]->(a)
// Make sure there are no ACTED_IN relationships apart from the one to a
AND NOT (:Movie)<-[:ACTED_IN]-(b)-[:ACTED_IN]->(a)
RETURN b
This might have a minor advantage in that it doesn't first collect all the :ACTED_IN relationships of the actor, but instead can quit the query as soon as it finds an additional relationship.
If you also want to remove the ones that have an IS_FRIENDS_WITH relationship then you can modify the query to
MATCH (a:Movie)
WHERE ID(a) = 11
MATCH (b:Actor)-[:ACTED_IN]->(a)
// Make sure no other relationships exist
AND NOT ()<-[]-(b)-[:ACTED_IN]->(a)
RETURN b
Here is my suggestion:
we can specify the title of the movie to filter the query results,
then we use with to specify the variables that we will pass to the next part, among the variable we pass the number of movies which an actor acted in .
In the second part of the query we verify that the actor has acted only in the given movie.
Finally we collect these actors and return them as list along with the movie.
MATCH(a:Person)-[:ACTED_IN]->(m:Movie {title:'The Matrix'})
WITH size((a)-[:ACTED_IN]->(:Movie)) AS uniqueMovieActor, m as theMovie
MATCH()
WHERE uniqueMovieActor = 1
RETURN collect(DISTINCT actor) as uniqueMovieActors, theMovie

Nodes with relationship to multiple nodes

I want to get the Persons that know everyone in a group of persons which know some specific places.
This:
MATCH (:Place {name:'Breiter Weg'})<-[:knows]-(b:Person)-[:knows]->(:Place {name:'Buchhandel'})
WITH collect(DISTINCT b) as persons
Match (a:Person)
WHERE ALL(b in persons WHERE (a)-[:knows]->(b))
RETURN a
works, but for the second part does a full nodelabelscan, before applying the where clause, which is extremely slow - in a bigger db it takes 8~9 seconds. I also tried this:
MATCH (:Place {name:'Breiter Weg'})<-[:knows]-(b:Person)-[:knows]->(:Place {name:'Buchhandel'})
Match (a:Person)-[:knows]->(b)
RETURN a
This only needs 2ms, however it returns all persons that know any person of group b, instead of those that know everyone.
So my question is: Is there a effective/fast query to get what i want?
We have a knowledge base article for this kind of query that show a few approaches.
One of these is to match to :Persons known by the group, and then count the number of times each of those persons shows up in the results. Provided there aren't multiple :knows relationships between the same two people, if the count is equal to the collection of people from your first match, then that person must know all of the people in the collection.
MATCH (:Place {name:'Breiter Weg'})<-[:knows]-(b:Person)-[:knows]->(:Place {name:'Buchhandel'})
WITH collect(b) as persons
UNWIND persons as b // so we have the entire list of persons along with each person
WITH size(persons) as total, b
MATCH (a:Person)-[:knows]->(b)
WITH total, a, count(a) as knownCount
WHERE total = knownCount
RETURN a
Here is a simpler Cypher query that also compares counts -- the same basic idea used by #InverseFalcon.
MATCH (:Place {name:'Breiter Weg'})<-[:knows]-(b:Person)-[:knows]->(:Place {name:'Buchhandel'}), (a:Person)-[:knows]->(b)
WITH COLLECT({a:a, b:b}) as data, COUNT(DISTINCT b) AS total
UNWIND data AS d
WITH total, d.a AS a, COUNT(d.b) AS bCount
WHERE total = bCount
RETURN a

Multiple Match queries in one query

I have the following records in my neo4j database
(:A)-[:B]->(:C)-[:D]->(:E)
(:C)-[:D]->(:E)
I want to get all the C Nodes and all the relations and related Nodes. If I do the query
Match (p:A)-[o:B]->(i:C)-[u:D]->(y:E)
Return p,o,i,u,y
I get the first to match if I do
Match (i:C)-[u:D]->(y:E)
Return i,u,y
I get the second to match.
But I want both of them in one query. How do I do that?
The easiest way is to UNION the queries, and pad unused variables with null (because all cyphers UNION'ed must have the same return columns
Match (p:A)-[o:B]->(i:C)-[u:D]->(y:E)
Return p,o,i,u,y
UNION
Match (i:C)-[u:D]->(y:E)
Return NULL as p, NULL as o,i,u,y
In your example though, the second match actually matches the last half of the first chain as well, so maybe you actually want something more direct like...
MATCH (c:C)
OPTIONAL MATCH (connected)
WHERE (c)-[*..20]-(connected)
RETURN c, COLLECT(connected) as connected
It looks like you're being a bit too specific in your query. If you just need, for all :C nodes, the connected nodes and relationships, then this should work:
MATCH (c:C)-[r]-(n)
RETURN c, r, n

neo4j - match nodes and add relationships to them

how do i add relationships to nodes returned by a cypher query?
I have written a query that returns me all the person nodes who have the same surname who live at the same address. I now want to add a relationship between these person nodes to indicate they are the same person. The query below returns me 3 person nodes and I want to add a relationship from the first node (returned by the ORDER BY) to the other 2.
MATCH (a:Address) <-[LIVES_AT]-(p:Person)
WITH a as addnode, p.surname as psurname, COUNT(p.name_urn) as c
WHERE c > 1
MATCH (a2:Address{address_urn:addnode.address_urn})<-[LIVES_AT]- (p2:Person{surname:psurname})
WITH p2 as p2node
ORDER BY CASE
WHEN p2node.master_record = 'Y'
THEN
1
ELSE
2
END
WITH collect(p2node) as colp2node
RETURN colp2node
Hope this makes sense? Please advise if there is a better way of doing this.
Something like this should work for you:
MATCH (a:Address)<-[LIVES_AT]-(p:Person)
WITH a, p.surname AS psurname, COUNT(p.name_urn) AS c
WHERE c > 1
MATCH (a2:Address { address_urn:a.address_urn })<-[LIVES_AT]-(p2:Person { surname:psurname })
WITH p2
ORDER BY CASE WHEN p2.master_record = 'Y' THEN 1 ELSE 2 END
WITH collect(p2) AS colp2
WITH colp2[0] AS master, colp2[1..] AS others
UNWIND others AS other
MERGE (master)-[:HAS_ALIAS]->(other);
I use MERGE to avoid duplicate relationships.
By the way, just because two people have the same surname and live at the same address, that does not normally mean they are the same person. I hope you are sure that what you are doing is appropriate.

How can I make the following Cypher query have the results that setting auto-complete control does?

I am doing the following Cypher query:
MATCH (a) WHERE a.itemId IN ['Q2', 'Q24', 'Q30', 'Q23', 'Q16', 'Q20'] RETURN a
I'd like it to return the same results as when I set the Auto-Complete switch. This question was asked and answered 1 but I don't understand the answer. Can this be performed in one query, and if so, what would the modified query be?
Yeah, you should be able to do it all in one query. To get the nodes in question along with their relationships you can do:
MATCH (a)
WHERE a.itemId IN ['Q2', 'Q24', 'Q30', 'Q23', 'Q16', 'Q20']
OPTIONAL MATCH (a)-[rel]-()
RETURN a, collect(rel)
That will return you, for each node, an array of the relationships.
Depending on your library that you're using for Neo4j, that may or may not return you something useful to give you the startnode/endnode. You could also do:
MATCH (a)
WHERE a.itemId IN ['Q2', 'Q24', 'Q30', 'Q23', 'Q16', 'Q20']
OPTIONAL MATCH (a)-[rel]-()
RETURN a, collect({rel: rel, start_node: startNode(rel), end_node: endNode(rel)})
That would give you an array for each node containing objects/maps of the data.
Got some separate clarification on the question about how to only return the relationships between the nodes that are matched. This should be possible like so:
MATCH (a)
WHERE a.itemId IN {itemIds}
OPTIONAL MATCH (a)-[rel]-(b)
WHERE b.itemId IN {itemIds}
RETURN a, collect(rel)
Again, you also might want to return the startNode/endNode of the relationships

Resources