Cypher gotchas: multiple-match vs comma operator

Even as a long term Neo4j user with a 10y+ experience I’ve stumbled over something being new to me. Therefore I thought it’s worth sharing.

In Cypher there’s the comma operator to describe a graph pattern consisting of multiple parts, e.g.

MATCH (a:Person{name:'a'})-[:KNOWS]->(b:Person),
 (b)-[:KNOWS]->(c:Person)
RETURN a, b, c

In the example above I could have written the pattern in one single MATCH as well:

MATCH (a:Person{name:'a'})-[:KNOWS]->(b:Person)-[:KNOWS]->(c:Person)
RETURN a, b, c

However there are patterns that you cannot encode in a linear way:

MATCH (a:Person{name:'a'})-[:KNOWS]->(b)-[:KNOWS]->(c),
   (b)-[:OWNS]-(i:Item)
RETURN a, b, c,i

When teaching Cypher classes I always tell people that with a MATCH you basically “draw” nodes and relationships. The comma operator instructs to move the pen up and continue drawing at an already known node – b in the example above. So you create a composite pattern with the comma operator.

In a lot of cases using multiple-match or comma doesn’t make a difference. To prove differently consider this example:

Now let’s write a quey using comma operator:

MATCH (a:Person{name:'a'})-[:KNOWS]->(b),
 (b)-[:KNOWS]->(a),
 (a)-[:KNOWS]->(b)
RETURN *

This will return an empty result set. With multiple-match instead:

MATCH (a:Person{name:'a'})-[:KNOWS]->(b)
MATCH (b)-[:KNOWS]->(a)
MATCH (a)-[:KNOWS]->(b)
RETURN *

we’re getting a result back.

The nifty difference is that the comma variant considers one pattern whereas the second considers multiple separate patterns.

One important property of a pattern is that the very same relationship is never used more than once. So in the comma variant the first and last KNOWS relationships need to be different whereas in the multiple MATCH variant they could be the very same relationship.

So it’s about relationship uniqueness along a pattern. I hope this clarifies the difference between multiple matches and usage of the comma operator.

2 comments

  1. Say I want nodes whoes fields are OPTIONALLY matching with text as well related nodes’ fields are OPTIONALLY matching with the same text.

    For example, I have node: Qualifications with name and description fields. I have node:Tag with tag field. Relation betwwen these 2 is (:Qalification)-[:TAGGED_AS]->(:Tag)

    I need all Node:Qualification if qualification.name OR qualidication.description OR (if qualification is tagged, tag.tag) CONTAINS $text

    I tried below. But it does not match if NO RELATION found.

    match (q:Qualification)-[ta:TAGGED_AS]->(t:TAG), (q1:Qualification) WHERE (toLower(q.name) CONTAINS “derm” OR toLower(q.description) CONTAINS “derm” OR t.searchableTag CONTAINS “derm”) AND (toLower(q1.name) CONTAINS “derm” OR toLower(q1.description) CONTAINS “derm”) return q,ta,t,q1

  2. “`match (q:Qualification) WHERE (toLower(q.name) CONTAINS “derm” OR toLower(q.description) CONTAINS “derm” OR t.searchableTag CONTAINS “derm”) AND (toLower(q1.name) CONTAINS “derm” OR toLower(q1.description) CONTAINS “derm”) optional match (q)-[ta:TAGGED_AS]->(t:TAG)
    return q,ta,t,q1“`
    should do the trick.

Leave a comment

Your email address will not be published.