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.