Disjunction on Match (match only on patterns specified)

I have a part of a query query:

 OPTIONAL MATCH (chunk: CHUNK) 
WHERE (chunk)-[:CONTAINS_TOKEN]->(token1) AND 
(chunk)-[:CONTAINS_TOKEN]->(token2)

above this specified two relations to the chunk of CONTAINS_TOKEN

often the chunk node returned is has more than two relations; it is related to token1, token2, and also other tokens.

How can I specify that the chunk only has CONTAINS_TOKEN relations to the nodes specified and no other nodes?

Let's simplify the Cypher a bit first...

MATCH (token1)<-[:CONTAINS_TOKEN]-(chunk: CHUNK)-[:CONTAINS_TOKEN]->(token2) 

... that'll do the same thing, though it may be less efficient if you have MANY chunks to one token, but it's not quite what we need. The easiest thing to do is count the rels. We want 2 and only 2.

MATCH (chunk: CHUNK)-[r:CONTAINS_TOKEN]->()  
WHERE count(r) = 2 
   AND (chunk)-[:CONTAINS_TOKEN]->(token1)
   AND (chunk)-[:CONTAINS_TOKEN]->(token2)

...that'll do it, depending on the rest of your script.

Why are you doing an optional match?

1 Like

Provided that you have the tokens in a list, you can use pattern comprehensions to ensure that a chunk only has relationships to the given tokens, and no others.

... // assume we've already matched to and collected tokens into a `tokens` variable
WITH tokens, head(tokens) as first
MATCH (chunk:CHUNK)-[:CONTAINS_TOKEN]-(first)
WITH tokens, [(chunk)-[:CONTAINS_TOKEN]-(token) | token] as otherTokens
WHERE size(tokens) = size(otherTokens) AND all(token IN tokens WHERE token IN otherTokens)
...

So with this, we find :CHUNK nodes not by doing a label scan, but a traversal from one of the tokens we already have (so we know at least this one is connected). Then we use a pattern comprehension to gather all the contained tokens for the chunk (we're assuming there is at most one relationship to a given token from a chunk). Then we need to make sure the lists contain the same elements, so we ensure the sizes are the same, and that the tokens in one collection are all in the other.

If you have APOC Procedures installed, then you can replace those predicates with WHERE apoc.coll.isEqualCollection(tokens, otherTokens).

1 Like