WHERE
adds constraints to the patterns in a MATCH
or OPTIONAL MATCH
clause or filters the results of a WITH
clause.
WHERE
is not a clause in it’s own right — rather, it’s part of MATCH
, OPTIONAL MATCH
, START
and WITH
.
In the case of WITH
and START
, WHERE
simply filters the results.
For MATCH
and OPTIONAL MATCH
on the other hand, WHERE
adds constraints to the patterns described.
It should not be seen as a filter after the matching is finished.
Important In the case of multiple |
Basic usage
Boolean operations
You can use the expected boolean operators AND
and OR
, and also the boolean function NOT
. See Section 9.8, “Working with NULL” for more information on how this works with NULL
.
Query
MATCH (n) WHERE n.name = 'Peter' XOR (n.age < 30 AND n.name = "Tobias") OR NOT (n.name = "Tobias" OR n.name="Peter") RETURN n
Result
n |
---|
3 rows |
|
|
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n) where n.name = 'Peter' xor (n.age < 30 and n.name = "Tobias") or not (n.name = "Tobias" or n.name="Peter") return n
Filter on node label
To filter nodes by label, write a label predicate after the WHERE
keyword using WHERE n:foo
.
Query
MATCH (n) WHERE n:Swedish RETURN n
The "Andres
" node will be returned.
Result
n |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n) where n:Swedish return n
Filter on node property
To filter on a node property, write your clause after the WHERE
keyword.
Query
MATCH (n) WHERE n.age < 30 RETURN n
"Tobias
" is returned because he is younger than 30.
Result
n |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n) where n.age < 30 return n
Filter on relationship property
To filter on a relationship property, write your clause after the WHERE
keyword.
Query
MATCH (n)-[k:KNOWS]->(f) WHERE k.since < 2000 RETURN f
"Peter
" is returned because Andres knows him since before 2000.
Result
f |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n)-[k:KNOWS]->(f) where k.since < 2000 return f
Filter on dynamic node property
To filter on a property using a dynamically computed name, use square bracket syntax.
Parameters
{ "prop" : "AGE" }
Query
MATCH (n) WHERE n[toLower({ prop })]< 30 RETURN n
"Tobias
" is returned because he is younger than 30.
Result
n |
---|
1 row |
|
Property exists
Use the EXISTS()
function to only include nodes or relationships in which a property exists.
Query
MATCH (n) WHERE exists(n.belt) RETURN n
"Andres
" will be returned because he is the only one with a belt
property.
Important The |
Result
n |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n) where exists(n.belt) return n
String matching
The start and end of strings can be matched using STARTS WITH
and ENDS WITH
.
To match regardless of location in a string, use CONTAINS
.
The matching is case-sensitive.
Match the start of a string
The STARTS WITH
operator is used to perform case-sensitive matching on the start of strings.
Query
MATCH (n) WHERE n.name STARTS WITH 'Pet' RETURN n
"Peter
" will be returned because his name starts with Pet
.
Result
n |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n) where n.name STARTS WITH 'Pet' return n
Match the end of a string
The ENDS WITH
operator is used to perform case-sensitive matching on the end of strings.
Query
MATCH (n) WHERE n.name ENDS WITH 'ter' RETURN n
"Peter
" will be returned because his name ends with ter
.
Result
n |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n) where n.name ENDS WITH 'ter' return n
Match anywhere in a string
The CONTAINS
operator is used to perform case-sensitive matching regardless of location in strings.
Query
MATCH (n) WHERE n.name CONTAINS 'ete' RETURN n
"Peter
" will be returned because his name contains ete
.
Result
n |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n) where n.name CONTAINS 'ete' return n
String matching negation
Use the NOT
keyword to exclude all matches on given string from your result:
Query
MATCH (n) WHERE NOT n.name ENDS WITH 's' RETURN n
"Peter
" will be returned because his name does not end with s
.
Result
n |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n) where NOT n.name ENDS WITH 's' return n
Regular expressions
Cypher supports filtering using regular expressions.
The regular expression syntax is inherited from the Java regular expressions.
This includes support for flags that change how strings are matched, including case-insensitive (?i)
, multiline (?m)
and dotall (?s)
.
Flags are given at the start of the regular expression, for example MATCH (n) WHERE n.name =~ '(?i)Lon.*' RETURN n
will return nodes with name London
or with name LonDoN
.
Regular expressions
You can match on regular expressions by using =~ "regexp"
, like this:
Query
MATCH (n) WHERE n.name =~ 'Tob.*' RETURN n
"Tobias
" is returned because his name starts with Tob
.
Result
n |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n) where n.name =~ 'Tob.*' return n
Escaping in regular expressions
If you need a forward slash inside of your regular expression, escape it. Remember that back slash needs to be escaped in string literals.
Query
MATCH (n) WHERE n.address =~ 'Sweden\\/Malmo' RETURN n
"Tobias
" is returned because his address is in Sweden/Malmo
.
Result
n |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n) where n.address =~ 'Sweden\\/Malmo' return n
Case insensitive regular expressions
By pre-pending a regular expression with (?i)
, the whole expression becomes case insensitive.
Query
MATCH (n) WHERE n.name =~ '(?i)ANDR.*' RETURN n
"Andres
" is returned because his name starts with ANDR
regardless of case.
Result
n |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n) where n.name =~ '(?i)ANDR.*' return n
Using path patterns in WHERE
Filter on patterns
Patterns are expressions in Cypher, expressions that return a list of paths. List
expressions are also predicates — an empty list represents false
, and a non-empty represents true
.
So, patterns are not only expressions, they are also predicates. The only limitation to your pattern is that you must be
able to express it in a single path. You can not use commas between multiple paths like you do in MATCH
. You can achieve
the same effect by combining multiple patterns with AND
.
Note that you can not introduce new variables here. Although it might look very similar to the MATCH
patterns, the
WHERE
clause is all about eliminating matched subgraphs. MATCH (a)-[*]->(b)
is very different from WHERE (a)-[*]->(b)
; the
first will produce a subgraph for every path it can find between a
and b
, and the latter will eliminate any matched
subgraphs where a
and b
do not have a directed relationship chain between them.
Query
MATCH (tobias { name: 'Tobias' }),(others) WHERE others.name IN ['Andres', 'Peter'] AND (tobias)<--(others) RETURN others
Nodes that have an outgoing relationship to the "Tobias
" node are returned.
Result
others |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (tobias {name: 'Tobias'}), (others) where others.name IN ['Andres', 'Peter'] and (tobias)<--(others) return others
Filter on patterns using NOT
The NOT
function can be used to exclude a pattern.
Query
MATCH (persons),(peter { name: 'Peter' }) WHERE NOT (persons)-->(peter) RETURN persons
Nodes that do not have an outgoing relationship to the "Peter
" node are returned.
Result
persons |
---|
2 rows |
|
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; MATCH (persons), (peter {name: 'Peter'}) where not (persons)-->(peter) return persons
Filter on patterns with properties
You can also add properties to your patterns:
Query
MATCH (n) WHERE (n)-[:KNOWS]-({ name:'Tobias' }) RETURN n
Finds all nodes that have a KNOWS
relationship to a node with the name "Tobias
".
Result
n |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n) where (n)-[:KNOWS]-({name:'Tobias'}) return n
Filtering on relationship type
You can put the exact relationship type in the MATCH
pattern, but sometimes you want to be able to do more advanced filtering on the type. You can use the special property TYPE
to compare the type with something else. In this example, the query does a regular expression comparison with the name of the relationship type.
Query
MATCH (n)-[r]->() WHERE n.name='Andres' AND type(r)=~ 'K.*' RETURN r
This returns relationships that has a type whose name starts with K
.
Result
r |
---|
2 rows |
|
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n)-[r]->() where n.name='Andres' and type(r) =~ 'K.*' return r
Lists
IN operator
To check if an element exists in a list, you can use the IN
operator.
Query
MATCH (a) WHERE a.name IN ["Peter", "Tobias"] RETURN a
This query shows how to check if a property exists in a literal list.
Result
a |
---|
2 rows |
|
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (a) where a.name IN ["Peter", "Tobias"] return a
Missing properties and values
Default to false if property is missing
As missing properties evaluate to NULL
, the comparision in the example will evaluate to FALSE
for nodes without the belt
property.
Query
MATCH (n) WHERE n.belt = 'white' RETURN n
Only nodes with white belts are returned.
Result
n |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n) where n.belt = 'white' return n
Default to true if property is missing
If you want to compare a property on a graph element, but only if it exists, you can compare the property against both the value you are looking for and NULL
, like:
Query
MATCH (n) WHERE n.belt = 'white' OR n.belt IS NULL RETURN n ORDER BY n.name
This returns all nodes, even those without the belt property.
Result
n |
---|
3 rows |
|
|
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (n) where n.belt = 'white' or n.belt IS NULL return n order by n.name
Filter on NULL
Sometimes you might want to test if a value or a variable is NULL
. This is done just like SQL does it, with IS NULL
. Also like SQL, the negative is IS NOT NULL
, although NOT(IS NULL x)
also works.
Query
MATCH (person) WHERE person.name = 'Peter' AND person.belt IS NULL RETURN person
Nodes that have name Peter
but no belt property are returned.
Result
person |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (person) where person.name = 'Peter' AND person.belt is null return person
Using ranges
Simple range
To check for an element being inside a specific range, use the inequality operators <
, <=
, >=
, >
.
Query
MATCH (a) WHERE a.name >= 'Peter' RETURN a
Nodes having a name property lexicographically greater than or equal to Peter are returned.
Result
a |
---|
2 rows |
|
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (a) where a.name >= 'Peter' return a
Composite range
Several inequalities can be used to construct a range.
Query
MATCH (a) WHERE a.name > 'Andres' AND a.name < 'Tobias' RETURN a
Nodes having a name property lexicographically between Andres and Tobias are returned.
Result
a |
---|
1 row |
|
Try this query live create (_0:`Swedish` {`age`:36, `belt`:"white", `name`:"Andres"}) create (_1 {`address`:"Sweden/Malmo", `age`:25, `name`:"Tobias"}) create (_2 {`age`:34, `email`:"peter_n@example.com", `name`:"Peter"}) create (_0)-[:`KNOWS` {`since`:1999}]->(_2) create (_0)-[:`KNOWS` {`since`:2012}]->(_1) ; match (a) where a.name > 'Andres' AND a.name < 'Tobias' return a