Search code examples
neo4jcypher

Neo4j Cypher: How to stop checking multiple OPTIONAL MATCH clauses if one match is found?


I am trying to check if any one of a combination of relationships exist, but I only care if there is at least one. Since the traversals increase in complexity, it would be ideal if I could break and return the query on the first match.

Example:

MATCH (p:Person { id: "user_1" }), (m:Movie { id: "movie_x" })
OPTIONAL MATCH (p)-[acted:ACTED_IN]->(m)
OPTIONAL MATCH (p)-[directed:DIRECTED]->(m)
OPTIONAL MATCH (p)-[:ACTED_IN]->(:Movie)<-[:PRODUCED_BY]-(:Studio)-[sibling_actor:PRODUCED_BY]->(m)
OPTIONAL MATCH (p)-[:DIRECTED]->(:Movie)-[:OF_TYPE]->(:Genre)<-[sibling_director:OF_TYPE]-(m)
RETURN count(acted) > 0 AS actedInMovie, count(directed) > 0 AS directedMovie, count(sibling_actor) > 0 AS actedInSibling, count(sibling_director) > 0 AS directedSibling

This will return the truthiness of the existence of EACH of these patterns, but since I only care if ONE matches, I would love to not run all four Optional Matches if the first one finds a match.


Solution

  • To test the presence of the patterns you are looking for, use the EXISTS subquery:

    MATCH (p:Person { id: "user_1" }), (m:Movie { id: "movie_x" })
    RETURN 
      EXISTS { (p)-[acted:ACTED_IN]->(m) } OR
      EXISTS { (p)-[directed:DIRECTED]->(m) } OR
      EXISTS { (p)-[:ACTED_IN]->(:Movie)<-[:PRODUCED_BY]-(:Studio)-[sibling_actor:PRODUCED_BY]->(m) } OR
      EXISTS { (p)-[:DIRECTED]->(:Movie)-[:OF_TYPE]->(:Genre)<-[sibling_director:OF_TYPE]-(m) }
        AS hasRelationship
    

    This will be planned with the SelectOrSemiApply operator which only evaluates an EXISTS subquery if the previous one returned false.