I have a simple domain of Students and Courses. A student can be registered for multiple courses.
I want to find all students registered for course 1234 that were not registered for course 5678.
So I tried this attempt:
MATCH (s:Student)
MATCH (c:Course)
MATCH (s)-[r:REGISTRATION]->(c {code: "1234"})
WHERE NOT (s)-[r:REGISTRATION]->(c {code: "5678"})
RETURN s
But it seems like the last WHERE NOT makes no difference to the query? I can still find students from the query result that are also registered for the second course :/
What am I doing wrong here?
You can do:
MATCH (sUnwnated:Student)-[r:REGISTRATION]->(:Course{code: "5678"}) // get all students in "5678"
WITH COLLECT(sUnwnated) AS sUnwnated // group them to a list
MATCH (sWanted:Student)-[r:REGISTRATION]->(:Course{code: "1234"}) // get all students in "1234"
WHERE NOT sWanted IN sUnwnated // but keep only these that are not in the list
RETURN sWanted
Apparently, I was missing only one important keyword/function, exists().
This works:
MATCH (s:Student)
MATCH (c:Course)
MATCH (s)-[r:REGISTRATION]->(c {code: "1234"})
WHERE NOT exists((s)-[r:REGISTRATION]->(c {code: "5678"}))
RETURN s
Related
Relationship between two users can be found out using below query.
RETURN EXISTS( (:User{_id:'User/123'})-[:Link]-(:User{_id:'User/567'}) )
What if it needs to be run for the list of queries like for below query i want to check if relationship exists
MATCH p=(u:User{_id:'User/8199'})-[r:Link]-(u1:User)
WHERE u1._id in ['12317291','User/09563','User/392942','User/24974','User/720']
RETURN p
You could match the first use and then iterate through the lest and test for existence for each other user in the list.
MATCH (u:User {_id: 'User/8199'})
WITH u
UNWIND ['12317291','User/09563','User/392942','User/24974','User/720'] | u1] AS other_user
RETURN other_user, exists((u)-[:Link]->(:Test {name: other_user}))
Or you could do something like this if you wanted the actual users matched afterwards. Match the first user, test for a realtioship to a list of users and return a collection of matches.
MATCH (u:User {_id: 'User/8199'})
RETURN [(u)-[:Link]->(u1:User)
WHERE u1._id IN
['12317291','User/09563','User/392942','User/24974','User/720'] | u1] AS matches
I am querying neo4j with cypher and am getting two rows per match:
MATCH (g:Group {Name: "Goliath_Treasury237"})-[w:MEMBER]->(a:Account)<-[y:ACCOUNT]-(p:Person)-[MANAGER*0..1]->(b:Person)
WHERE NOT p.staffID = b.staffID
MATCH (g:Group {Name: "Goliath_Treasury237"})-[j:MEMBER]->(v:Account)<-[x:ACCOUNT]-(p:Person)-[f:DEP*0..1]->(d:Department)
RETURN p.GivenName, p.Surname, p.staffID, p.CorpT, b.staffID, d.Name
I'm trying to get the information for both the department and the boss of a person which I'm struggling with declaratively unless I do the double match. This returns two rows for each match where a person has a boss, one with their ID as their bosses ID and one with the correct bosses id. For people without a boss I get one row back but the bosses id is their own.
If I remove the variable length path for boss then I get one row for each individual but no row where someone doesn't have a boss.
I'm at a loss now, any help would be great!
There's several things we can fix here.
For one, you don't need the second match to start over from the beginning, you can reuse the same p node without needing all the stuff before. You also don't need to have a variable on every node and relationship if you're not going to use or return it in some way.
You can use an OPTIONAL MATCH when you don't want the match to filter out rows, newly-introduced variables in the OPTIONAL MATCH will be null if the match fails.
Something like this should work:
MATCH (:Group {Name: "Goliath_Treasury237"})-[:MEMBER]->(:Account)<-[:ACCOUNT]-(p:Person)
OPTIONAL MATCH (p)-[MANAGER]->(b:Person)
WHERE NOT p.staffID = b.staffID
OPTIONAL MATCH (p)-[:DEP]->(d:Department)
RETURN p.GivenName, p.Surname, p.staffID, p.CorpT, b.staffID, d.Name
I'm using the MATCH query below(refering to users reviewing books):
MATCH (u:User {id:15})-[r:REVIEW]->(b:Book)
WITH u,b
MATCH (t:User)-[r:REVIEW]->(b)
RETURN distinct t
This cypher firstly matches the books reviewed by user with id=15. Then it uses another MATCH command to find any other user that has reviewed any book that has been reviewed by the user with id=15. However, the users returned include user with id=15 too. How can i exclude him?
Note that i used 'distinct' command in order that i don't get the same user 2 or more times. F.e. if user(id=15) has reviewed 2 books and another user has reviewed those 2 books too, i would get the last user 2 times without 'distinct'.
Another option is to capture what you want in a single pattern, like so:
MATCH (u:User{id:15})-[:REVIEW]->(b:Book)<-[:REVIEW]-(t:User)
RETURN distinct t
When you have a single MATCH clause and pattern like this, because we differentiate the u and t variables, it will never match t to u.
If you want a shorthand version (provided that :Book nodes are the only things that :Users can :REVIEW, and that :Users are the only types of nodes that can :REVIEW :Books), then you can shorten this to:
MATCH (u:User{id:15})-[:REVIEW*2]-(t:User)
RETURN distinct t
You just need to add a where clause to the second match that excludes users that are the same as the first user that was matched.
MATCH (u:User{id:15})-[r:REVIEW]->(b:Book)
with u,b
MATCH (t:User)-[r:REVIEW]->(b)
WHERE t <> u
RETURN distinct t
I have a node (:Person) that can have the following relations.
[:LIKE] an (:Artist) who [:PERFORMS] at an (:Event)
[:LIKE] a (:Host) who is [:HOSTING] an (:Event)
[:ATTENDS] an (:Event)
I'm trying to write a cypher query that returns all the Events that either
is being attended by the user
(me:Person)-[:ATTEND]->(gig:Event)
has an artist that the user likes performing
(me:Person)-[:LIKE]->(a:Artist)-[:PERFORMS]->(gig:Event)
is being hosted by a host that the user likes
(me:Person)-[:LIKE]->(h:Host)-[:HOSTING]->(gig:Event)
I tried using a query like match (me {emailId: 'abc#xyz.com'})-[attend:ATTEND]->(gig), (me)-[:LIKE]->(n)-[:PERFORM|HOSTING]-(gig2) return gig,gig2 but this only returns the desired results if the first part of the match clause, which matches the :ATTEND relation, has a non null result. If for instance the user does not ATTEND any event, but has Artists and Hosts that s/he LIKES then I'd assume the query to still return results based on the second MATCH clause but that isn't happening.
What am I missing here?
-
Thanks,
A cypher nube
If MATCH does not return anything then the query stops and that is why you have a problem. Solution is OPTIONAL MATCH so that it does not stop if the return is empty. You can try changing your query to this, so no matter if user attended the gig or not you will get results if he likes artists that performed on the gig.
optional match (me {emailId: 'abc#xyz.com'})-[attend:ATTEND]->(gig),
(me)-[:LIKE]->(n)-[:PERFORMS|HOSTING]-(gig2) return gig,gig2
Query from Tomaž Bratanič working. But there is a more compact alternative with a variable path length:
match (me:Person {emailId: 'abc#xyz.com'})
-[:LIKE|ATTEND|PERFORMS|HOSTING*1..2]->
(e:Event)
return e
UNION might be your best bet here, especially since the only thing you want are the :Event nodes, with no further processing.
MATCH (me:Person {emailId: 'abc#xyz.com'})-[:ATTEND]->(gig:Event)
RETURN gig
UNION
MATCH (me:Person {emailId: 'abc#xyz.com'})-[:LIKE]->(a:Artist)-[:PERFORMS]->(gig:Event)
RETURN gig
UNION
MATCH (me:Person {emailId: 'abc#xyz.com'})-[:LIKE]->(h:Host)-[:HOSTING]->(gig:Event)
RETURN gig
Hello I am trying to match neo4j relationships using 'WHERE AND'
My example relationiship is: 'User Visits Country'
I create it as so...
MATCH (c:Country{Name:Country}) MERGE (u:User{Email:Email,UserID: UserID}) MERGE (u)-[r:Visits]->(c)
//Countries are previously created and Users may or may not exist
Then I query (This Works):
MATCH (u:User)-[r:Visits]->(c:Country) where c.Name='France' or c.Name='Spain' return u
Result: Shows me all users who have visited Spain OR France, even if they have only visited one of two countries.
BUT What Im trying to do is the same exact query, but with 'AND' instead of 'OR'. In which I can get users that have visited both 'France' and 'Spain'.
MATCH (u:User)-[r:Visits]->(c:Country) where c.Name='France' AND c.Name='Spain' return u
Result: 0 nodes and relationship found..
What can I do?
In your query you are matching a single country node and saying the name of that node has to be France and has to be Spain.
What you want is to find all of the users that have vistied both France and Spain. There are a couple of ways you can go...
//match both countries against the same user and identify them separtely
//making two matches in a single query
MATCH (u:User)-[:VISITS]->(c1:Country), (u)-[:VISITS]->(c2:Country)
WHERE c1.name = "France"
AND c2.name = "Spain"
RETURN u.name
//match all users that have been to either and only return the one that have been to both
MATCH (u:User)-[r:VISITS]->(c:Country)
WHERE (c.name IN [ "France", "Spain"])
WITH u, count(*) AS num
WHERE num = 2
RETURN u.name, num
It think number one is better as it is more precise and probably more efficient.
If you only care about 2 countries. this query would also work, in addition to the options provided by #DaveBennett.
MATCH (c1)<-[:VISITS]-(u)-[:VISITS]->(c2)
WHERE c1.name = "France" AND c2.name = "Spain"
RETURN u.name;
What's wrong in your query
MATCH (u:User)-[r:Visits]->(c:Country)
where c.Name='France'
AND c.Name='Spain'
return u
This will always return no rows because you are trying to check two values for the same node's property.
Solution
MATCH (c1:Country)<-[r:Visits]-(u:user)-[r1:Visits]->(c2:Country)
WHERE c1.name = 'France' AND c2.name = 'Spain'
RETURN u.name;
This will return what you need.
Here is one short and useful reference doc of Neo4j:
http://neo4j.com/docs/2.1.2/cypher-refcard/