FILTERCypher 25 onlyIntroduced in Neo4j 2025.06
FILTER is used to add filters to queries, similar to Cypher®'s WHERE.
Unlike WHERE, FILTER is not a subclause, which means it can be used independently of the MATCH, OPTIONAL MATCH, and WITH clauses, but not within them.
Example graph
The following graph is used for the examples below:
To recreate the graph, run the following query in an empty Neo4j database:
CREATE (andy:Swedish:Person {name: 'Andy', age: 36}),
(timothy:Person {name: 'Timothy', age: 38}),
(peter:Person {name: 'Peter', age: 35}),
(lisa:Person {name: 'Lisa', age: 48}),
(john:Person {name: 'John', age: 40}),
(susan:Person {name: 'Susan', age: 32}),
(andy)-[:KNOWS {since: 2012}]->(timothy),
(andy)-[:KNOWS {since: 1999}]->(peter),
(peter)-[:KNOWS {since: 2005}]->(lisa),
(lisa)-[:KNOWS {since: 2010}]->(john),
(john)-[:KNOWS {since: 2021}]->(susan)
Basic filtering
MATCH (n)
FILTER n:Swedish
RETURN n.name AS name
| name |
|---|
|
|
MATCH (n:Person)
FILTER n.age < 35
RETURN n.name AS name, n.age AS age
| name | age |
|---|---|
|
|
|
|
MATCH (p:Person)-[r:KNOWS]->(n:Person)
FILTER r.since > 2010
RETURN p.name AS person,
r.since AS knowsSince,
n.name AS otherPerson
| person | knowsSince | otherPerson |
|---|---|---|
|
|
|
|
|
|
|
||
Filter on dynamic labels and types
To filter on a label or relationship type using a dynamically computed value, use $(<expr>) where <expr> is any Cypher expression:
{
"label": "Swedish"
}
MATCH (n)
FILTER n:$($label)
RETURN labels(n) AS labels
| labels |
|---|
|
Rows: 1 |
Filter on dynamic properties
To filter on a property using a dynamically computed name, use square brackets []:
{
"propname": "age"
}
MATCH (n:Person)
FILTER n[$propname] > 40
RETURN n.name AS name, n.age AS age
| name | age |
|---|---|
|
|
Rows: 1 |
|
Differences between FILTER and WHERE
FILTER and WHERE are both used to apply filter to queries.
However, there are a number of important differences between them that arise from the fact that FILTER is a clause and WHERE is a subclause:
-
FILTERacts on entities after they have been matched, whereasWHEREconstrains what rows get matched before the match is performed. -
FILTERcannot be used withinMATCH,OPTIONAL MATCH, orWITHclauses but only alongside them. This means that, unlikeWHERE,FILTERcannot be used to add filters inside patterns.
FILTER and WHEREThis OPTIONAL MATCH example highlights the differences between the WHERE subclause and the FILTER clause.
WHERE constraining an OPTIONAL MATCH patternUNWIND [32,37,40] AS ageValue
OPTIONAL MATCH (p:Person)
WHERE p.age = ageValue
RETURN p.name AS name, p.age AS age
| name | age |
|---|---|
|
|
|
|
|
|
Rows: 3 |
|
Because WHERE is a subclause belonging to OPTIONAL MATCH, it only filters the matches, but does not affect the number of returned rows.
In this case, OPTIONAL MATCH always keeps all rows from UNWIND, and WHERE does not remove any rows returning NULL.
The same is not true if WHERE is exchanged for FILTER:
FILTER adding post-filtering to OPTIONAL MATCHUNWIND [32,37,40] AS ageValue
OPTIONAL MATCH (p:Person)
FILTER p.age = ageValue
RETURN p.name AS name, p.age AS age
| name | age |
|---|---|
|
|
|
|
Rows: 2 |
|
Unlike WHERE, FILTER is not part of the OPTIONAL MATCH and so removes entire rows from the result set based on the condition provided within the expression.
That is, when OPTIONAL MATCH fails to find a match and p is NULL, FILTER p.age = ageValue cannot be evaluated, causing the entire row to be removed.
FILTER cannot be used within patternsBecause WHERE is a subclause qualifying a described pattern, it can be used inside patterns.
WHERE inside a node patternWITH 35 AS minAge
MATCH (a:Person WHERE a.name = 'Andy')-[:KNOWS]->(b:Person WHERE b.age > minAge)
RETURN b.name AS name
| name |
|---|
|
Rows: 1 |
The same is not true for FILTER which, as a clause, cannot be placed with a pattern.
FILTER inside a patternWITH 35 AS minAge
MATCH (a:Person FILTER a.name = 'Andy')-[:KNOWS]->(b:Person FILTER b.age > minAge)
RETURN b.name AS name
For more information about how to use WHERE in fixed-length and variable-length pattern matching, see WHERE → Filter patterns.
FILTER as a substitute for WITH * WHERE
Unlike WHERE, which relies on MATCH, OPTIONAL MATCH, or WITH to define its scope, FILTER can filter queries independently of these clauses.
This can make some queries more concise.
For example, the following two queries are equivalent:
WITH * WHEREUNWIND [1, 2, 3, 4, 5, 6] AS x
WITH x
WHERE x > 2
RETURN x
FILTERUNWIND [1, 2, 3, 4, 5, 6] AS x
FILTER x > 2
RETURN x
As such, FILTER can be seen as a substitute for the WITH * WHERE <predicate> constructs in Cypher.
FILTER instead of WITH * WHERE in LOAD CSVThe following two LOAD CSV commands are equivalent:
Id,Name,Location,Email,BusinessType
1,Neo4j,San Mateo,contact@neo4j.com,P
2,AAA,,info@aaa.com,
3,BBB,Chicago, info@ ,G
,CCC,Michigan,info@ccc.com,G
LOAD CSV using WITH * WHERELOAD CSV WITH HEADERS FROM 'file:///companies.csv' AS row
WITH row
WHERE row.Id IS NOT NULL
MERGE (c:Company {id: row.Id})
LOAD CSV using FILTERLOAD CSV WITH HEADERS FROM 'file:///companies.csv' AS row
FILTER row.Id IS NOT NULL
MERGE (c:Company {id: row.Id})
However, while FILTER can act as a substitute for WITH * WHERE <predicate> constructs, it does not include the ability of WITH to manipulate the variables in scope for subsequent clauses.
Nor can FILTER alias or create new variables.
In other words, FILTER only has the function of WITH * WHERE <predicate> and not WITH <selectedVariable> AS <newVariableName> WHERE <predicate>.