Conditional Cypher Execution

Sometimes queries require conditional execution logic that can’t be adequately expressed in Cypher. The conditional execution procedures simulate an if / else structure, where a supplied boolean condition determines which cypher query is executed.

Procedure and Function Overview

The available procedures and functions are described below:

type qualified name signature description

procedure

apoc.when

apoc.when(condition :: BOOLEAN?, ifQuery :: STRING?, elseQuery = :: STRING?, params = {} :: MAP?) :: (value :: MAP?)

apoc.when(condition, ifQuery, elseQuery:'', params:{}) yield value - based on the conditional, executes read-only ifQuery or elseQuery with the given parameters

procedure

apoc.do.when

apoc.do.when(condition :: BOOLEAN?, ifQuery :: STRING?, elseQuery = :: STRING?, params = {} :: MAP?) :: (value :: MAP?)

apoc.do.when(condition, ifQuery, elseQuery:'', params:{}) yield value - based on the conditional, executes writing ifQuery or elseQuery with the given parameters

procedure

apoc.case

apoc.case(conditionals :: LIST? OF ANY?, elseQuery = :: STRING?, params = {} :: MAP?) :: (value :: MAP?)

apoc.case([condition, query, condition, query, …​], elseQuery:'', params:{}) yield value - given a list of conditional / read-only query pairs, executes the query associated with the first conditional evaluating to true (or the else query if none are true) with the given parameters

procedure

apoc.do.case

apoc.do.case(conditionals :: LIST? OF ANY?, elseQuery = :: STRING?, params = {} :: MAP?) :: (value :: MAP?)

apoc.do.case([condition, query, condition, query, …​], elseQuery:'', params:{}) yield value - given a list of conditional / writing query pairs, executes the query associated with the first conditional evaluating to true (or the else query if none are true) with the given parameters

WHEN Procedures

For if / else conditional logic, when procedures allow an ifQuery and elseQuery to be specified. If the conditional is true, the ifQuery will be run, and if not the elseQuery will be run.

signature

apoc.when(condition :: BOOLEAN?, ifQuery :: STRING?, elseQuery = :: STRING?, params = {} :: MAP?) :: (value :: MAP?)

apoc.do.when(condition :: BOOLEAN?, ifQuery :: STRING?, elseQuery = :: STRING?, params = {} :: MAP?) :: (value :: MAP?)

Read only
CALL apoc.when(
  condition: Boolean,
  ifQuery: String,
  elseQuery: String,
  params: Map)
YIELD value
Write
CALL apoc.do.when(
  condition: Boolean,
  ifQuery: String,
  elseQuery: String,
  params: Map)
YIELD value

For example, if we wanted to match to neighbor nodes one and two traversals away from a start node, and return the smaller set (either those one hop away, or those that are two hops away), we might use:

MATCH (start:Node)-[:REL]->(a)-[:REL]->(b)
WITH collect(distinct a) as aNodes, collect(distinct b) as bNodes

CALL apoc.when(
  size(aNodes) <= size(bNodes),
  'RETURN aNodes as resultNodes',
  'RETURN bNodes as resultNodes',
  {aNodes:aNodes, bNodes:bNodes})
YIELD value

RETURN value.resultNodes as resultNodes

Or, if we wanted to conditionally set or create graph elements if we deem some account to be suspicious, but still want to continue other query operations in either case, we could use apoc.do.when:

MATCH (acc:Account)
OPTIONAL MATCH (acc)-[r:ACCESSED_BY]->(suspect:User)
WHERE suspect.id in {suspiciousUsersIdList}

CALL apoc.do.when(
  r IS NOT NULL,
  'SET acc:Suspicious',
  '',
  {acc:acc})
YIELD value

// ignore value and continue
WITH acc
...

CASE Procedures

For more complex conditional logic, case procedures allow for a variable-length list of condition / query pairs, where the query following the first conditional evaluating to true is executed. An elseQuery block is executed if none of the conditionals are true.

signature

apoc.case(conditionals :: LIST? OF ANY?, elseQuery = :: STRING?, params = {} :: MAP?) :: (value :: MAP?)

apoc.do.case(conditionals :: LIST? OF ANY?, elseQuery = :: STRING?, params = {} :: MAP?) :: (value :: MAP?)

Read only
CALL apoc.case(
  conditionals: List of alternating Boolean/String,
  elseQuery: String,
  params: Map)
YIELD value
Write
CALL apoc.do.case(
  conditionals: List of alternating Boolean/String,
  elseQuery: String,
  params: Map)
YIELD value

If we wanted to MATCH to selection nodes in a column, we could use entirely different MATCHES depending on query parameters, or based on data already in the graph:

MATCH (me:User {id:$myId})
CALL apoc.case([
  $selection = 'friends', "RETURN [(me)-[:FRIENDS]-(friend) | friend] as selection",
  $selection = 'coworkers', "RETURN [(me)-[:WORKS_AT*2]-(coworker) | coworker] as selection",
  $selection = 'all', "RETURN apoc.coll.union([(me)-[:FRIENDS]-(friend) | friend], [(me)-[:WORKS_AT*2]-(coworker) | coworker]) as selection"],
  'RETURN [] as selection',
  {me:me}
)
YIELD value
RETURN value.selection as selection;

Or if we want to create different relationship types between two nodes based on the value

MATCH (me:User {id:$myId})
MATCH (friend:User {id:$friendId})
CALL apoc.do.case([
  $selection = 'friends', "MERGE (me)-[rel:FRIENDS]->(friend) RETURN rel",
  $selection = 'coworkers', "MERGE (me)-[rel:CO_WORKER]->(friend) RETURN rel"],
  'MERGE (me)-[rel:CONNECTED]->(friend) RETURN rel',
  {me:me, friend:friend}
)
YIELD value
RETURN value.rel as rel;