Docs
View as markdown

Composing clauses

UNION, CALL subqueries with scope clauses and IN TRANSACTIONS, EXISTS, COUNT and COLLECT subqueries, USE, CALL procedure YIELD, NEXT and WHEN in relate.

cypher

Composing clauses combine queries. UNION stacks the rows of two queries, a CALL { … } subquery runs a query once per input row, EXISTS, COUNT and COLLECT turn a subquery into a value, USE picks the namespace One of the three fixed graphs in every store: neo4j for user data, system for administration, and system_ontologies, listed but not queryable. Read more in the glossary a statement runs against, CALL <procedure> runs a built-in procedure, NEXT feeds one query's rows into the next, and WHEN … THEN … ELSE picks one query by condition. Every form on this page runs without a preview opt-in.

Synopsis

<query> UNION [DISTINCT | ALL] <query> [UNION [DISTINCT | ALL] <query> …]

[OPTIONAL] CALL [(<variable>, … | * )] { <query> }
  [IN [<n> CONCURRENT] TRANSACTIONS [OF <n> ROWS]
    [ON ERROR CONTINUE | BREAK | FAIL | RETRY [FOR <duration>] [THEN CONTINUE | BREAK | FAIL]]
    [REPORT STATUS AS <variable>]]

EXISTS { <pattern> [WHERE <predicate>] | <query> }
COUNT { <pattern> [WHERE <predicate>] | <query> }
COLLECT { <query that returns one column> }

USE <namespace> <query>

CALL <procedure>([<argument>, …]) [YIELD * | YIELD <column> [AS <alias>], … [WHERE <predicate>]]

<query> NEXT <query> [NEXT <query> …]

WHEN <predicate> THEN <query> [WHEN <predicate> THEN <query> …] [ELSE <query>]

UNION

FormResult
UNION, UNION DISTINCTThe rows of every branch, duplicates removed
UNION ALLThe rows of every branch, duplicates kept
RuleBehavior
Column namesEvery branch returns the same column names; the order of the columns may differ, and rows are matched by name
Mixing UNION and UNION ALL in one statementsyntax error: InvalidClauseComposition: mixing UNION and UNION ALL
Different column namessyntax error: DifferentColumnsInUnion: all UNION branches must have the same column names
ORDER BY, SKIP, LIMIT after the last branchApply to that branch only; an ORDER BY after a CALL () { … } that wraps the union sorts every row (example)
USEEach branch may start with USE, naming the same namespace

CALL subqueries

A CALL { … } subquery runs once for each incoming row. The scope clause in parentheses names the outer variables the body can read.

ScopeBody can readNotes
CALL (a, b) { … }a and b
CALL (*) { … }Every variable in scope
CALL () { … }No outer variableReading one fails with syntax error: UndefinedVariable: variable '<name>' not defined
CALL { WITH a … }The variables the leading WITH importsDeprecated. EXPLAIN reports notification 01N00, The `CALL { … }` subquery has no variable scope clause., with the remedy Add the scope clause the body imports: `CALL (n) { … }`, or `CALL () { … }` when it imports nothing.
BodyRows after the subquery
Ends in RETURNOne row per row the body returns, per input row; an input row for which the body returns nothing is dropped
Ends in RETURN, under OPTIONAL CALLOne row per row the body returns, per input row; an input row for which the body returns nothing is kept, with the returned variables null
No RETURN (a unit subquery, such as one that only writes)The input rows, unchanged
RuleBehavior
A returned name that an outer variable already hassyntax error: VariableAlreadyBound: Variable `<name>` already declared in outer scope
Aggregation in the bodyPer input row: CALL (p) { MATCH (p)-->(x) RETURN count(x) AS n } counts for each p
NestingA body can hold another CALL subquery

CALL IN TRANSACTIONS

IN TRANSACTIONS commits the subquery's work in batches of input rows, each batch its own transaction A unit of work that commits as a whole or not at all; each statement is its own transaction unless an explicit begin … commit groups several. Read more in the glossary. ON ERROR … and REPORT STATUS AS … may come in either order. Outside an explicit transaction, each batch commits on its own: a batch that committed stays committed when a later one fails. Inside an explicit transaction, the batches commit or roll back with that transaction.

OptionDefaultBehavior
OF <n> ROWS1,000 rowsInput rows per batch
IN [<n>] CONCURRENT TRANSACTIONSOffAccepted; the batches run one after another
ON ERROR FAILThe defaultThe statement fails at the first failing batch; earlier batches stay committed
ON ERROR CONTINUE—Skips the failing batch and runs the rest
ON ERROR BREAK—Stops at the failing batch; the statement succeeds with what committed
ON ERROR RETRY [FOR <duration>] [THEN …]Without THEN: FAILAccepted, but no batch is retried: the duration is ignored and the THEN fallback applies at once. Without THEN, the statement fails as under ON ERROR FAIL
REPORT STATUS AS <variable>OffBinds a map per input row: started, committed, transactionId and errorMessage. Needs ON ERROR CONTINUE, BREAK, or RETRY … THEN CONTINUE or THEN BREAK; otherwise syntax error: InvalidReportStatusUsage: REPORT STATUS requires ON ERROR CONTINUE, BREAK, or RETRY with THEN CONTINUE or THEN BREAK

A write before CALL { … } IN TRANSACTIONS in the same statement fails with syntax error: WriteBeforeCallInTransactions: a write cannot precede CALL IN TRANSACTIONS, and LOAD CSV counts as a write (writing clauses). Every input row runs the subquery once and counts against the fixed budget of 100,000 nested evaluation passes per statement (limitations).

Subquery expressions

FormValueBody
EXISTS { … }true when the body has at least one rowA pattern with an optional WHERE, or a query with MATCH and RETURN
COUNT { … }The number of rows the body returnsThe same as EXISTS
COLLECT { … }A list of the body's single columnA query ending in RETURN of exactly one column; otherwise syntax error: parse error: A COLLECT subquery must end with a single return column
RuleBehavior
Outer variablesThe body reads them without a scope clause
Writes in the bodysyntax error: InvalidClauseComposition: writes are not allowed in subquery expressions
UNION in the bodyAllowed, with the same column-name rule as a top-level UNION

USE

A store has three fixed namespaces: neo4j holds the graph, system answers administration commands, and system_ontologies is listed but not queryable. A statement without USE runs against the session namespace, neo4j unless --namespace says otherwise (namespaces).

FormResult
USE neo4j <query>Runs against the graph
USE system <query>Runs a procedure call or SHOW DATABASES; SHOW INDEXES and SHOW CONSTRAINTS fail with syntax error: InvalidClauseComposition: NotAllowedOnSystemDatabase: SHOW INDEXES and SHOW CONSTRAINTS are per-database catalogs and are not available on the system database, and a MATCH fails with semantic error: SemanticError: The following unsupported clauses were used: MATCH. followed by the clauses system supports
USE system_ontologies <query>semantic error: SemanticError: This database is not queryable. Use the db.ontology.* procedures on the user database 'neo4j' instead.
USE <other name>semantic error: DatabaseNotFound: Graph not found: <name>
USE in two UNION branches naming different namespacessemantic error: SemanticError: Multiple graphs in the same query not allowed here. This feature is only available on composite databases. Attempted to access graph <name>
USE graph.byName(…)semantic error: SemanticError: Dynamic graph lookup not allowed here. This feature is only available on composite databases. Attempted to access graph graph.byName(<argument>)
USE after another clausesyntax error: InvalidPlacementOfUseClause: USE clause must be either the first clause in a query, or preceded by an importing WITH clause
CALL { USE neo4j … }Runs; USE may open a subquery without a scope clause
CALL () { USE neo4j … }syntax error: UndefinedVariable: variable 'neo4j' not defined

CALL procedure

FormResult
CALL db.labels() as the whole statementEvery column the procedure yields
CALL db.labels() YIELD labelThe named columns
YIELD *Every column
YIELD label AS lThe column renamed
YIELD … WHERE <predicate>The rows that pass the predicate
Inside a larger queryRuns once per input row
A column the procedure does not yieldsyntax error: UnknownProcedureOutput: <procedure> does not return `<column>`
A name that is not a proceduresyntax error: ProcedureNotFound: <name>

SHOW PROCEDURES lists the procedures with their signatures; the procedures page describes them.

NEXT and WHEN

FormResultRule
<query> NEXT <query>The second query runs on the first one's returned rows, with its column names as variablesNEXT at the start fails: syntax error: parse error: InvalidNextStatement: NEXT must follow a statement, not begin one
WHEN <predicate> THEN <query> … [ELSE <query>]The query of the first branch whose predicate is true; no true branch and no ELSE gives no rowsStarts a statement, a subquery body or a query after NEXT. Every branch returns the same columns: syntax error: ColumnNameConflict: conditional branches must return the same columns

Examples

The cells run in page order on one in-memory store, starting from the shared example graph:

Cypher
CREATE (ada:Person {name: 'Ada', role: 'engineer'}),
       (grace:Person {name: 'Grace', role: 'engineer'}),
       (noor:Person {name: 'Noor', role: 'maintainer'}),
       (relate:Project {name: 'relate', language: 'Rust'}),
       (tck:Project {name: 'opencypher-tck', language: 'Gherkin'}),
       (ada)-[:WORKS_ON {since: 2024}]->(relate),
       (grace)-[:WORKS_ON {since: 2023}]->(relate),
       (grace)-[:WORKS_ON {since: 2022}]->(tck),
       (noor)-[:MAINTAINS]->(relate),
       (ada)-[:KNOWS]->(grace)
Recorded result
(no rows) · nodes created: 5, relationships created: 5, properties set: 13, labels added: 5

ORDER BY after a UNION sorts the last branch only:

Cypher
MATCH (r:Project) RETURN r.name AS name
UNION
MATCH (p:Person) RETURN p.name AS name ORDER BY name DESC
Recorded result
name
"relate"
"opencypher-tck"
"Noor"
"Grace"
"Ada"

5 rows

Wrapped in CALL () { … }, the sort covers every row:

Cypher
CALL () {
  MATCH (r:Project) RETURN r.name AS name
  UNION
  MATCH (p:Person) RETURN p.name AS name
}
RETURN name ORDER BY name
Recorded result
name
"Ada"
"Grace"
"Noor"
"opencypher-tck"
"relate"

5 rows

A scoped subquery counts per person; Noor, with no project, gets 0 because an aggregate always returns a row:

Cypher
MATCH (p:Person)
CALL (p) {
  MATCH (p)-[:WORKS_ON]->(r:Project)
  RETURN count(r) AS projects
}
RETURN p.name AS name, projects ORDER BY name
Recorded result
nameprojects
"Ada"1
"Grace"2
"Noor"0

3 rows

Without an aggregate, Noor's row is dropped, unless the call is OPTIONAL:

Cypher
MATCH (p:Person)
OPTIONAL CALL (p) {
  MATCH (p)-[:WORKS_ON]->(r:Project)
  RETURN r.name AS project
}
RETURN p.name AS name, project ORDER BY name, project
Recorded result
nameproject
"Ada""relate"
"Grace""opencypher-tck"
"Grace""relate"
"Noor"null

4 rows

Batches that report their status. This writes a Row node for each value except the one that divides by zero:

Cypher
UNWIND [1, 0, 2] AS i
CALL (i) { CREATE (:Row {inverse: 1 / i}) } IN TRANSACTIONS OF 1 ROWS
  ON ERROR CONTINUE
  REPORT STATUS AS s
RETURN i, s.committed AS committed, s.errorMessage AS error
Recorded result
icommittederror
1truenull
0false"/ by zero"
2truenull

3 rows · nodes created: 2, properties set: 2, labels added: 2

The three subquery expressions on one row per person:

Cypher
MATCH (p:Person)
RETURN p.name AS name,
       EXISTS { (p)-[:KNOWS]->() } AS knowsSomeone,
       COUNT { (p)-[:WORKS_ON]->() } AS projects,
       COLLECT { MATCH (p)-[:WORKS_ON]->(r) RETURN r.name ORDER BY r.name } AS projectNames
ORDER BY name
Recorded result
nameknowsSomeoneprojectsprojectNames
"Ada"true1["relate"]
"Grace"false2["opencypher-tck", "relate"]
"Noor"false0[]

3 rows

Cypher
RETURN COLLECT { MATCH (p:Person) RETURN p.name, p.role } AS pairs
Expected error
error: syntax error: parse error: A COLLECT subquery must end with a single return column
  RETURN COLLECT { MATCH (p:Person) RETURN p.name, p.role } AS pairs
                                                            ^

USE names the namespace; system refuses a MATCH:

Cypher
USE neo4j MATCH (p:Person) RETURN count(p) AS people
Recorded result
people
3

1 row

Cypher
USE system MATCH (n) RETURN count(n) AS n
Expected error
error: semantic error: SemanticError: The following unsupported clauses were used: MATCH. 
The system database supports a restricted set of Cypher clauses. The supported clause structure for procedure calls is: CALL, YIELD, RETURN. YIELD and RETURN clauses are optional. The order of the clauses is fixed and each can only occur once.

A procedure call, with its column renamed and filtered:

Cypher
CALL db.labels() YIELD label AS l WHERE l STARTS WITH 'Pro'
RETURN l
Recorded result
l
"Project"

1 row

NEXT passes the returned columns on, and WHEN picks a branch per row:

Cypher
MATCH (p:Person) RETURN p.name AS name, p.role AS role
NEXT
WHEN role = 'maintainer' THEN RETURN name, 'reviews' AS duty
ELSE RETURN name, 'builds' AS duty
Recorded result
nameduty
"Noor""reviews"
"Ada""builds"
"Grace""builds"

3 rows

See also