Refactor & Control Flow Procedures
These are procedures, not functions: they are invoked with CALL … YIELD … and they write to the database. All three are available under their bare name and under the apoc. prefix.
do.when()
Run one of two Cypher sub-queries depending on a condition, and yield each row the sub-query produces.
Syntax: CALL do.when(condition, ifQuery, elseQuery, params) YIELD value
Parameters:
| Parameter | Type | Description |
|---|---|---|
condition |
Boolean |
Selects the branch to run. Must be a boolean; a string, number or list is rejected rather than coerced. |
ifQuery |
String |
Cypher run when the condition is true |
elseQuery |
String |
Cypher run when the condition is false. Pass |
params |
Map |
Named parameters bound into the sub-query. Pass |
All four arguments are required.
Returns: value - One row per row the sub-query produces, each holding that row as a map
The sub-query is routed to a read or a write execution automatically, so both MATCH … RETURN and CREATE/SET/DELETE branches work without the caller having to say which. ifQuery is type-checked on every call, including calls where the condition is false and the branch never runs.
APOC Compatible: apoc.do.when
CALL do.when(
size($names) > 0,
'RETURN $names[0] AS first',
'RETURN null AS first',
{names: $names}
) YIELD value
RETURN value.first AS first
A write branch:
MATCH (p:Person {name: 'Alice'})
CALL do.when(
p.score > 100,
'MATCH (n:Person) WHERE elementId(n) = $id SET n.tier = "gold" RETURN n.tier AS tier',
null,
{id: elementId(p)}
) YIELD value
RETURN value.tier AS tier
ifQuery and elseQuery run with the caller’s privileges, exactly as any other command string the engine executes. Never build either argument by concatenating untrusted input — pass values through params instead.
|
refactor.cloneNodesWithRelationships()
Clone each given node together with every relationship touching it, leaving the originals untouched.
Syntax: CALL refactor.cloneNodesWithRelationships(nodes, config) YIELD input, output, error
Parameters:
| Parameter | Type | Description |
|---|---|---|
nodes |
List |
The nodes to clone. A node listed twice is cloned once. |
config |
Map |
Configuration. Pass |
Configuration keys:
| Key | Type | Description |
|---|---|---|
skipProperties |
List |
Property names to leave off the clones |
Returns: One row per input node:
-
input- The original node -
output- The clone, or null if cloning it failed -
error- The failure message, or null on success
Each clone is a new vertex of the same type carrying the same properties. A relationship whose other endpoint is also being cloned reconnects the two clones; a relationship to a node outside the list reconnects the clone to that same original node.
Cloning is best-effort per item: a node that fails to clone gets its own row with output null and a message in error, and an edge that fails to clone is skipped. This covers failures raised at save() time: a UNIQUE index violation is checked at commit, so it still fails the whole transaction.
APOC Compatible: apoc.refactor.cloneNodesWithRelationships
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
CALL refactor.cloneNodesWithRelationships([a, b], {}) YIELD input, output, error
RETURN input, output, error
// Excluding properties from the clones:
MATCH (p:Person)
WITH collect(p) AS people
CALL refactor.cloneNodesWithRelationships(people, {skipProperties: ['ssn', 'email']}) YIELD output
RETURN count(output) AS cloned
refactor.mergeNodes()
Merge a list of nodes into the first one, rewiring their relationships and deleting the absorbed nodes.
Syntax: CALL refactor.mergeNodes(nodes, config) YIELD node
Parameters:
| Parameter | Type | Description |
|---|---|---|
nodes |
List |
The nodes to merge. The first is the survivor; the rest are absorbed. A node listed twice counts once, and at least two distinct nodes are required. |
config |
Map |
Configuration. Pass |
Configuration keys:
| Key | Type | Description |
|---|---|---|
properties |
String |
How to resolve a property present on both the survivor and an absorbed node: |
Returns: node - The surviving node
A property present only on an absorbed node is always copied onto the survivor, whichever policy is in effect. Every incoming and outgoing relationship of an absorbed node is rewired onto the survivor; a relationship that connected two nodes both being merged becomes a self-relationship on the survivor.
APOC Compatible: apoc.refactor.mergeNodes
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Alice'})
WHERE elementId(a) <> elementId(b)
CALL refactor.mergeNodes([a, b], {properties: 'combine'}) YIELD node
RETURN node
// Keeping the survivor's own values on conflict:
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Alice'})
WHERE elementId(a) <> elementId(b)
CALL refactor.mergeNodes([a, b], {properties: 'discard'}) YIELD node
RETURN node