EXISTS subqueries
An EXISTS subquery can be used to find out if a specified pattern exists at least once in the graph.
It serves the same purpose as a path pattern but it is more powerful because it allows you to use MATCH and WHERE clauses internally.
Example graph
The following graph is used for the examples below:
To recreate the graph, run the following query against an empty Neo4j database:
CREATE
(andy:Swedish:Person {name: 'Andy', age: 36}),
(timothy:Person {name: 'Timothy', nickname: 'Tim', age: 25}),
(peter:Person {name: 'Peter', nickname: 'Pete', age: 35}),
(andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}),
(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}),
(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}),
(fido)-[:HAS_TOY]->(:Toy{name:'Banana'})
Simple EXISTS subquery
Variables introduced by the outside scope can be used in the EXISTS subquery without importing them.
In this regard, EXISTS subqueries are different from CALL subqueries, which do require importing.
The following example shows this:
MATCH (person:Person)
WHERE EXISTS {
(person)-[:HAS_DOG]->(:Dog)
}
RETURN person.name AS name
| name |
|---|
|
|
Rows: 2 |
EXISTS subquery with WHERE clause
A WHERE clause can be used in conjunction to the MATCH.
Variables introduced by the MATCH clause and the outside scope can be used in this scope.
MATCH (person:Person)
WHERE EXISTS {
MATCH (person)-[:HAS_DOG]->(dog:Dog)
WHERE person.name = dog.name
}
RETURN person.name AS name
| name |
|---|
|
Rows: 1 |
Conditional EXISTS subqueriesCypher 25 onlyIntroduced in Neo4j 2025.06
WHEN can be used inside EXISTS subqueries to execute branches conditionally when a predicate evaluates to true.
Note that the names and the number of columns returned by the different WHEN branches must be identical.
For more information, see Conditional queries → Conditional subqueries.
The example below filters people based on their age and pet ownership.
If someone is over 35, it checks for dog ownership, and if 35 or younger, it checks for cat ownership.
Note that Peter, being 35 and a dog owner, is excluded from the result.
EXISTS subqueryMATCH (n:Person)
WHERE EXISTS {
WHEN n.age > 35 THEN {
MATCH (n)-[:HAS_DOG]->(:Dog)
RETURN n AS petOwner
}
ELSE {
MATCH (n)-[:HAS_CAT]->(:Cat)
RETURN n AS petOwner
}
}
RETURN n.name AS name,
n.age AS age
| name | age |
|---|---|
|
|
|
|
Rows: 2 |
|
Nesting EXISTS subqueries
EXISTS subqueries can be nested like the following example shows.
The nesting also affects the scopes.
That means that it is possible to access all variables from inside the subquery which are either from the outside scope or defined in the very same subquery.
MATCH (person:Person)
WHERE EXISTS {
MATCH (person)-[:HAS_DOG]->(dog:Dog)
WHERE EXISTS {
MATCH (dog)-[:HAS_TOY]->(toy:Toy)
WHERE toy.name = 'Banana'
}
}
RETURN person.name AS name
| name |
|---|
|
Rows: 1 |
EXISTS subquery outside of a WHERE clause
EXISTS subquery expressions can appear anywhere that an expression is valid.
Here the result is a boolean that shows whether the subquery can find the given pattern.
MATCH (person:Person)
RETURN person.name AS name, EXISTS {
MATCH (person)-[:HAS_DOG]->(:Dog)
} AS hasDog
| name | hasDog |
|---|---|
|
|
|
|
|
|
Rows: 3 |
|
EXISTS subquery with a UNION
Exists can be used with a UNION clause, and the RETURN clauses are not required.
It is worth noting that if one branch has a RETURN clause, then all branches require one.
The below example demonstrates that if one of the UNION branches was to return at least one row, the entire EXISTS expression will evaluate to true.
MATCH (person:Person)
RETURN
person.name AS name,
EXISTS {
MATCH (person)-[:HAS_DOG]->(:Dog)
UNION
MATCH (person)-[:HAS_CAT]->(:Cat)
} AS hasPet
| name | hasPet |
|---|---|
|
|
|
|
|
|
Rows: 3 |
|
EXISTS subquery with WITH
Variables from the outside scope are visible for the entire subquery, even when using a WITH clause.
To avoid confusion, shadowing of these variables is not allowed.
An outside scope variable is shadowed when a newly introduced variable within the inner scope is defined with the same variable.
In the example below, the outer variable name is shadowed and will therefore throw an error.
WITH 'Peter' as name
MATCH (person:Person {name: name})
WHERE EXISTS {
WITH "Ozzy" AS name
MATCH (person)-[:HAS_DOG]->(d:Dog)
WHERE d.name = name
}
RETURN person.name AS name
The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 90))
New variables can be introduced into the subquery, as long as they use a different identifier.
In the example below, a WITH clause introduces a new variable.
Note that the outer scope variable person referenced in the main query is still available after the WITH clause.
MATCH (person:Person)
WHERE EXISTS {
WITH "Ozzy" AS dogName
MATCH (person)-[:HAS_DOG]->(d:Dog)
WHERE d.name = dogName
}
RETURN person.name AS name
| name |
|---|
|
Rows: 1 |
EXISTS subquery with RETURN
EXISTS subqueries do not require a RETURN clause at the end of the subquery. If one is present, it does not
need to be aliased, which is different compared to CALL subqueries.
Any variables returned in an EXISTS subquery will not be available after the subquery.
MATCH (person:Person)
WHERE EXISTS {
MATCH (person)-[:HAS_DOG]->(:Dog)
RETURN person.name
}
RETURN person.name AS name
| name |
|---|
|
|
Rows: 2 |
Rules
The following is true for EXISTS subqueries:
-
Any non-writing query is allowed.
-
If the
EXISTSsubquery evaluates to at least one row, the whole expression will becometrue. This also means that the system only needs to evaluate if there is at least one row and can skip the rest of the work. -
EXISTSsubqueries differ from regular queries in that the finalRETURNclause may be omitted, as any variable defined within the subquery will not be available outside of the expression, even if a finalRETURNclause is used. -
The
MATCHkeyword can be omitted in subqueries in cases where theEXISTSconsists of only a pattern and an optionalWHEREclause. -
An
EXISTSsubquery can appear anywhere in a query that an expression is valid. -
Any variable that is defined in the outside scope can be referenced inside the subquery’s own scope.
-
Variables introduced inside the subquery are not part of the outside scope and therefore cannot be accessed on the outside.