Operators and expressions
On this page
- Literals
- Arithmetic operators
- Comparison operators
- Boolean operators & three-valued logic
- NULL predicates
- IN (list membership)
- String predicates
- List & map operations
- Pattern existence (EXISTS { … })
- Function calls in expressions
- XOR
- CASE
- List & pattern comprehensions
- reduce
- Map projection
- Inline pattern predicates
- Operator precedence (high → low)
- List predicates and count subqueries
Literals
RETURN 42, -7, 3.14, true, false, null, 'single', "double";
RETURN [1, 2, 3], ['a', true, 3], []; // list literals
RETURN {name: 'Ada', age: 36, tags: ['x', 'y']}; // map literal
Property and variable access:
MATCH (p:Person) RETURN p.name, p.address.city; // dotted property access
Arithmetic operators
| Operator | Meaning | Notes |
|---|---|---|
+ | addition / string concatenation / list concat | 'a' + 'b' → 'ab' |
- | subtraction; unary negation | -x |
* | multiplication | |
/ | division | integer/integer is integer division; division by zero is an error |
% | modulo / remainder | modulo by zero is an error |
RETURN 2 + 3 * 4 AS r; // 14 (precedence respected)
RETURN (2 + 3) * 4 AS r; // 20
RETURN 'Hello, ' + 'world'; // 'Hello, world'
MATCH (p:Person) RETURN p.age * 12 AS months, -p.age AS neg;
Integer arithmetic is overflow-checked — an overflowing operation is a clean error, not a wraparound.
Comparison operators
| Operator | Meaning |
|---|---|
= | equal |
<> | not equal |
<, >, <=, >= | ordering comparisons |
Semantics (Cypher value comparison):
1 = 1.0istrue(numeric equality across int/float).- Any comparison with
nullyieldsnull(nottrue/false). NaNis never equal to anything, including itself (NaN = NaN→false) — andNaNcannot be stored as a property anyway.- Ordering (
<,>,<=,>=) between incomparable cross-type operands yieldsnull(e.g. a string vs a number). Numbers compare numerically.
RETURN 1 = 1.0; // true
RETURN 1 < null; // null
RETURN 'a' < 'b'; // true
Boolean operators & three-valued logic
AND, OR, NOT operate on three values: true, false, null (Kleene
logic). XOR exists too — see Operators and expressions.
a | b | a AND b | a OR b |
|---|---|---|---|
| true | true | true | true |
| true | false | false | true |
| true | null | null | true |
| false | null | false | null |
| null | null | null | null |
NOT null is null. A WHERE keeps only rows whose predicate is true
(both false and null are filtered out).
MATCH (p:Person)
WHERE p.active = true AND NOT p.archived = true
RETURN p.name;
Both operands are evaluated eagerly — there is no short-circuit. false AND f(x) still evaluates f(x), so a function that can error (say,
apoc.convert.fromJsonMap on a malformed blob) aborts the query even when the
other operand already decides the result. CASE branches are lazy
(Operators and expressions); use one as the guard:
// Guard absent data and text that does not start with an object prefix:
WITH {data: '{"channel":"web"}'} AS m
WHERE any(d IN [CASE WHEN m.data STARTS WITH '{'
THEN apoc.convert.fromJsonMap(m.data) ELSE NULL END]
WHERE d.channel = 'web')
RETURN m.data;
A leading { does not validate JSON. Malformed object text still raises a parse
error; use this guard only when object-prefixed values are valid JSON.
NULL predicates
MATCH (p:Person) WHERE p.nick IS NULL RETURN p.name;
MATCH (p:Person) WHERE p.nick IS NOT NULL RETURN p.name;
IN (list membership)
MATCH (p:Person) WHERE p.name IN ['Ada', 'Bob'] RETURN p;
RETURN 3 IN [1, 2, 3] AS yes; // true
IN uses the same value-equality as =, including its null behaviour.
String predicates
| Predicate | Meaning |
|---|---|
STARTS WITH | prefix test |
ENDS WITH | suffix test |
CONTAINS | substring test |
MATCH (p:Person)
WHERE toLower(p.name) STARTS WITH 'a'
OR p.email ENDS WITH '@example.com'
OR p.bio CONTAINS 'graph'
RETURN p.name;
There is no regex operator: =~ is a parse error (unexpected character '~') — The engine has no third-party dependencies and ships no regex engine.
Case-insensitive matching is toLower(x) CONTAINS $term; for "does this JSON
property match" parse the blob with apoc.convert.fromJsonMap and compare the
value (guarded against non-JSON data — see Operators and expressions).
List & map operations
Indexing (e[i]) and slicing (e[lo..hi]) of lists:
RETURN [10, 20, 30][0] AS first; // 10
RETURN [10, 20, 30][-1] AS last; // 30 (negative index from the end)
RETURN [10, 20, 30, 40][1..3] AS mid; // [20, 30] (half-open: lo inclusive, hi exclusive)
RETURN substring('graph', 0, 3) AS pre; // 'gra'; string slicing is unsupported
Map field access uses dotted or bracket notation:
RETURN {a: 1, b: 2}.a AS x; // 1
WITH {a: 1, b: 2} AS m RETURN m.b; // 2
Building structures from data:
MATCH (p:Person)
RETURN [p.name, p.age] AS pair, {id: p.email, name: p.name} AS rec;
Pattern existence (EXISTS { … })
A pattern can be used as a boolean predicate:
MATCH (p:Person)
WHERE EXISTS { (p)-[:KNOWS]->(:Person {name: 'Ada'}) }
RETURN p.name;
Function calls in expressions
Scalar and namespaced functions are ordinary expressions (see Scalar functions):
RETURN toUpper('ada'), size([1, 2, 3]), coalesce(null, null, 'fallback');
RETURN point({x: 0, y: 0}).x AS px; // call then field access
RETURN gds.similarity.cosine([1, 0], [1, 1]); // namespaced (dotted) function
XOR
Exclusive-or, between OR (looser) and AND (tighter) in precedence. Three-valued:
NULL if either side is NULL, else true when exactly one side is true.
RETURN true XOR false; // true
RETURN true XOR true; // false
RETURN null XOR true; // null
CASE
Both forms are supported. The generic form tests boolean predicates; the
simple form compares an operand for equality. The first match wins; with no
match the result is the ELSE value, or NULL.
// generic
MATCH (p:Person)
RETURN CASE WHEN p.age >= 18 THEN 'adult' ELSE 'minor' END AS bracket;
// simple
RETURN CASE size(['a','b']) WHEN 0 THEN 'empty' WHEN 2 THEN 'pair' ELSE 'other' END;
List & pattern comprehensions
A list comprehension iterates a list, optionally filtering and mapping:
RETURN [x IN [1,2,3,4] WHERE x % 2 = 0 | x * 10] AS evens; // [20, 40]
RETURN [x IN range(1,3) | x * x] AS squares; // [1, 4, 9]
A pattern comprehension matches a pattern in the current row's context and projects an expression per match:
MATCH (a:Person {name: 'Ada'})
RETURN [(a)-[:KNOWS]->(f) | f.name] AS friends;
reduce
Fold a list, threading an accumulator:
RETURN reduce(total = 0, x IN [1,2,3,4] | total + x) AS sum; // 10
RETURN reduce(s = '', x IN ['a','b','c'] | s + x) AS joined; // 'abc'
Map projection
Build a map from a node/relationship/map and a list of selectors: .prop copies a
property, .* copies all of them, key: expr adds a computed entry, and a bare
variable copies that variable.
MATCH (p:Person)
RETURN p {.name, .age, older: p.age + 1} AS view;
MATCH (p:Person) RETURN p {.*} AS all_props;
Inline pattern predicates
A WHERE may appear inside a node pattern; it is evaluated after the pattern is bound and can reference its variables:
MATCH (p:Person WHERE p.age > 18) RETURN p.name;
MATCH (a:Person)-[:KNOWS]->(b:Person WHERE b.age > a.age) RETURN b.name;
Operator precedence (high → low)
- Property access
., indexing/slicing[...], function callf(...), map projection{ ... } - Unary
-,NOT *,/,%+,-- Comparisons
= <> < > <= >=,IN,STARTS WITH/ENDS WITH/CONTAINS,IS NULL/IS NOT NULL ANDXOROR
Use parentheses to be explicit when in doubt: (a OR b) AND c. CASE and the
comprehension/reduce forms are primary expressions (they bind tightest).
List predicates and count subqueries
any, all, none and single bind a list element and evaluate a predicate.
Empty lists return false, true, true and false respectively. A null list yields
null. COUNT { pattern } counts matches without materialising an intermediate
result list; EXISTS { pattern } can stop at the first match.
RETURN any(x IN [1,2,3] WHERE x > 2), all(x IN [1,2,3] WHERE x > 0);
MATCH (p:Person) RETURN p.name, COUNT { (p)-[:KNOWS]->() } AS friends;
Related articles
Indexes and constraints · Procedure directory · Transactions and concurrency