cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

Prevent relationship isomorphism in exists clause

wgevaert
Node Link

I am building an application that should accept quite general input and be able to generate quite elaborate Cypher queries. I get stuck on the following:

Suppose we have the following example database:

CREATE (John {name:'John Doe', age:30})-[:HAS_AUTOGRAPH]->(Joe {name:'Joe'})-[:IS_NICKNAME_OF]->({name:'Joseph Quinn'}), (John)-[:LIVES_NEARBY]->(Joe)

I would now like to find all persons that either (have age > 50) or (live near Joseph and have Joseph's autograph), for which I thought of building the following query:

MATCH (n) WHERE
n.age > 50 OR EXISTS {
  MATCH
    (n)-[:HAS_AUTOGRAPH]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'}),
    (n)-[:LIVES_NEARBY]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})
} RETURN n.name

which should return John Doe. However, it does not, because the IS_NICKNAME_OF relationship cannot be used more than once in the EXISTS match. (Yes I did think of implementing the IS_NICKNAME_OF in a different way but that gave other difficulties)

The standard way to circumvent this (in this case unwanted) effect of relationship isomorphism is by using extra matches: Instead of 

MATCH
  (n)-[:HAS_AUTOGRAPH]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'}),
  (n)-[:LIVES_NEARBY]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})
RETURN n

one writes

MATCH (n)-[:HAS_AUTOGRAPH]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})
MATCH (n)-[:LIVES_NEARBY]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})
RETURN n

to obtain the result without the use of relationship isomorphism.

However, this does not work in an EXISTS clause, and the following gives a syntax error:

MATCH (n) WHERE
n.age > 50 OR EXISTS {
  MATCH (n)-[:HAS_AUTOGRAPH]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})
  MATCH (n)-[:LIVES_NEARBY]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})
} RETURN n.name

Any suggestions on how to do this are very much appreciated!

1 ACCEPTED SOLUTION

wgevaert
Node Link

The answer is obvious after a little bit of pondering.
Instead of this:
WHERE n.age > 50 OR EXISTS {
    MATCH (n)-[:HAS_AUTOGRAPH]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})
    MATCH (n)-[:LIVES_NEARBY]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})

} RETURN n.name

I just do this:
WHERE n.age > 50 OR (EXISTS {
    MATCH (n)-[:HAS_AUTOGRAPH]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})
} AND EXISTS {
    MATCH (n)-[:LIVES_NEARBY]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})

}) RETURN n.name

Which does generalize to all cases that my application needs to handle.

View solution in original post

4 REPLIES 4

wgevaert
Node Link

The answer is obvious after a little bit of pondering.
Instead of this:
WHERE n.age > 50 OR EXISTS {
    MATCH (n)-[:HAS_AUTOGRAPH]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})
    MATCH (n)-[:LIVES_NEARBY]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})

} RETURN n.name

I just do this:
WHERE n.age > 50 OR (EXISTS {
    MATCH (n)-[:HAS_AUTOGRAPH]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})
} AND EXISTS {
    MATCH (n)-[:LIVES_NEARBY]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})

}) RETURN n.name

Which does generalize to all cases that my application needs to handle.

MATCH (n) WHERE
n.age > 50 
OR 
(EXISTS ((n)-[:HAS_AUTOGRAPH]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'}))
 AND
 EXISTS ((n)-[:LIVES_NEARBY]->()-[:IS_NICKNAME_OF*0..1]->({name:'Joseph Quinn'})))
RETURN n.name

This does seem to be similar to what I typed, but you used parentheses "()" instead of curly brackets "{}". Is there any reason why you prefer parentheses over curly brackets in the exists clause?

The curly brackets represent a subquery.  Your subquery contains a 'match'.  In my syntax, I am using the 'exists' as a method and passing it a pattern expression as a parameter.  My syntax works for a single pattern match. You would need a subquery if the 'existence' requires serval match statements. They should realize the same result. 

Nodes 2022
Nodes
NODES 2022, Neo4j Online Education Summit

All the sessions of the conference are now available online