From f1059d95f00e0418092100ea184217ec34836837 Mon Sep 17 00:00:00 2001 From: katarinasupe Date: Wed, 21 Oct 2026 13:49:05 +0200 Subject: [PATCH] docs: allow WHEN branches in EXISTS, COUNT and COLLECT bodies --- pages/querying/clauses/call.mdx | 3 ++ pages/querying/subquery-expressions.mdx | 67 ++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/pages/querying/clauses/call.mdx b/pages/querying/clauses/call.mdx index 77af702d2..2059677a7 100644 --- a/pages/querying/clauses/call.mdx +++ b/pages/querying/clauses/call.mdx @@ -363,6 +363,9 @@ A conditional subquery needs a scope clause: `CALL { WHEN ... }` is an error, an start with `USING`. The whole conditional subquery can run [`IN TRANSACTIONS`](/querying/read-and-modify-data#call-subqueries-in-transactions). +The same branches can form the body of `EXISTS`, `COUNT` and `COLLECT`. Refer to +[subquery expressions](/querying/subquery-expressions#conditional-bodies-with-when). + ## 2. Invalid uses of CALL subquery ### 2.1. Returning variables with the same name as those in the outer scope diff --git a/pages/querying/subquery-expressions.mdx b/pages/querying/subquery-expressions.mdx index 06a15eb41..a1c5d1c9e 100644 --- a/pages/querying/subquery-expressions.mdx +++ b/pages/querying/subquery-expressions.mdx @@ -628,8 +628,9 @@ Output: To test whether anything matched, leave the aggregation out and let the `MATCH` decide, as in the [EXISTS section](#exists). -`EXISTS` and `COUNT` do not require a `RETURN` at all; `COLLECT` does, because it -needs a column to gather. +`EXISTS` and `COUNT` do not require a `RETURN` at all, except in a +[`WHEN` branch](#conditional-bodies-with-when); `COLLECT` does, because it needs a +column to gather. ### UNION @@ -704,6 +705,68 @@ Output: +---------------------------+ ``` +### Conditional bodies with `WHEN` + +A body may be a list of `WHEN` branches, as in a +[conditional `CALL` subquery](/querying/clauses/call#18-conditional-subqueries-with-when). +For each row of the enclosing query, the expression reduces the rows of the +first branch whose predicate is `true`. Here actors collect their movies and +everyone else collects the people they know: + +```cypher +MATCH (p:Person) +RETURN p.name AS name, + COLLECT { + WHEN EXISTS { (p)-[:ACTED_IN]->() } THEN + MATCH (p)-[:ACTED_IN]->(m) RETURN m.title AS item ORDER BY item + ELSE + MATCH (p)-[:KNOWS]->(f) RETURN f.name AS item ORDER BY item + } AS items +ORDER BY name; +``` + +Output: + +```nocopy ++---------+----------------------------------------------+ +| name | items | ++---------+----------------------------------------------+ +| "Alice" | ["Johnny Mnemonic", "Jumanji", "The Matrix"] | +| "Bob" | ["Jumanji"] | +| "Carol" | [] | +| "Dave" | [] | ++---------+----------------------------------------------+ +``` + +When no branch matches and there is no `ELSE`, the expression gives its empty +value, as for a body that matched nothing: + +```cypher +MATCH (p:Person) +RETURN p.name AS name, + COUNT { WHEN p.nickname IS NOT NULL THEN MATCH (p)-[:KNOWS]->(f) RETURN f } AS friends, + EXISTS { WHEN p.nickname IS NOT NULL THEN MATCH (p)-[:KNOWS]->(f) RETURN f } AS hasFriends +ORDER BY name; +``` + +Output: + +```nocopy ++---------+---------+------------+ +| name | friends | hasFriends | ++---------+---------+------------+ +| "Alice" | 2 | true | +| "Bob" | 1 | true | +| "Carol" | 0 | false | +| "Dave" | 0 | false | ++---------+---------+------------+ +``` + +Every branch must end with `RETURN`, also in `EXISTS` and `COUNT`. A branch +follows the same rules as a plain body: the +[supported clauses](#supported-clauses), and one column for `COLLECT`. A branch +may contain `UNION` or another `WHEN` in braces. + ## Nesting A body may itself contain subquery expressions. Here `EXISTS` filters on a nested