Dave Shepard
Dave Shepard

Reputation: 527

Neo4J Cypher Exclude nodes that connect to a specific node

I'm trying to find all nodes that don't connect to a specific node. I have an app where students doing an assignment discover themes in a story, and then write explications. Then, other students do peer reviews of these explications. My data looks like this:

Assignment-hasTheme->Theme-hasChild->Theme
Annotation-theme->Theme
Explication-owner->User
Explication-annotation->Annotation
PeerReview-explication->Explication

As part of the application, when a user has to do a peer review, I have to find all the explications written by other users. It seems to me like this query should work:

MATCH
  (u),
  (a)-[:hasTheme]->(:Theme)
     -[:hasChild*]->(:Theme)
     <-[:theme]-(ann:Annotation)
     <-[:annotation]-(e:Explication)
OPTIONAL MATCH
  (e)<-[:explication]-(p:PeerReview)
WHERE id(a)=7 AND id(u)=4
  AND (e)-[:owner]->(u)
RETURN e, count(e) AS explicationCount
ORDER BY explicationCount ASC

The problem is that it doesn't: I get all the explications that all users have written. That includes the explications the user wrote. Can anyone tell me how to exclude those?

Upvotes: 1

Views: 237

Answers (1)

InverseFalcon
InverseFalcon

Reputation: 30417

The problem is that the WHERE clause is only associated with one other clause...the preceding MATCH, OPTIONAL MATCH, or WITH. In your query, it's associated with the OPTIONAL MATCH.

If you re-read your query knowing this, you can see that the first MATCH has no WHERE clause, so it's matching on all assignments and all users, finding all explications.

THEN it does the optional match to get :PeerReviews matching on the given assignment and user ids where the explication owner is the user with the given id. The WHERE is only affecting which :PeerReviews (variable p) are matched.

A couple other things I can see...you're introducing a variable ann on the :Annotations matched in the pattern, and a variable p for the :PeerReview, but you're not actually doing anything with these in the query. This also makes your OPTIONAL MATCH useless, you're not returning or operating on the matched :PeerReviews.

My recommendation is to remove those variables and remove your OPTIONAL MATCH completely.

MATCH
  (u),
  (a)-[:hasTheme]->(:Theme)
     -[:hasChild*]->(:Theme)
     <-[:theme]-(:Annotation)
     <-[:annotation]-(e:Explication)
WHERE id(a)=7 AND id(u)=4
  AND (e)-[:owner]->(u)
RETURN e, count(e) AS explicationCount
ORDER BY explicationCount ASC

If you do want to add in the OPTIONAL MATCH and use the matched :PeerReview, ensure that it's below the WHERE affecting the MATCH, like so:

MATCH
  (u),
  (a)-[:hasTheme]->(:Theme)
     -[:hasChild*]->(:Theme)
     <-[:theme]-(:Annotation)
     <-[:annotation]-(e:Explication)
WHERE id(a)=7 AND id(u)=4
  AND (e)-[:owner]->(u)
OPTIONAL MATCH
  (e)<-[:explication]-(p:PeerReview)
RETURN e, count(e) AS explicationCount, p
ORDER BY explicationCount ASC

EDIT

In response to the comments where the desired result is each :Explication and the count of all linked :PeerReviews, you would use this query:

MATCH
  (u),
  (a)-[:hasTheme]->(:Theme)
     -[:hasChild*0..]->(:Theme)
     <-[:theme]-(:Annotation)
     <-[:annotation]-(e:Explication)
WHERE id(a)=7 AND id(u)=4
  AND (e)-[:owner]->(u)
OPTIONAL MATCH
  (e)<-[:explication]-(p:PeerReview)
RETURN e, count(p) as peerReviewCount
ORDER BY peerReviewCount ASC

EDIT

Updated the above query so it will find annotations on the parent theme as well instead of just its children.

Upvotes: 2

Related Questions