Expressions

Expressions in general

Most expressions in Cypher evaluate to null if any of their inner expressions are null. Notable exceptions are the operators IS NULL and IS NOT NULL.

An expression in Cypher can be:

  • A decimal (integer or float) literal: 13, -40000, 3.14

  • A decimal (integer or float) literal in scientific notation: 6.022E23.

  • A hexadecimal integer literal (starting with 0x): 0x13af, 0xFC3A9, -0x66eff.

  • An octal integer literal (starting with 0o): 0o1372, 0o2127, -0o5671.

  • A float literal: Inf, Infinity, NaN

  • A string literal: 'Hello', "World".

  • A boolean literal: true, false.

  • A variable: n, x, rel, myFancyVariable, `A name with weird stuff in it[]!`.

  • A property: n.prop, x.prop, rel.thisProperty, myFancyVariable.`(weird property name)`.

  • A dynamic property: n["prop"], rel[n.city + n.zip], map[coll[0]].

  • A parameter: $param, $0.

  • A list of expressions: ['a', 'b'], [1, 2, 3], ['a', 2, n.property, $param], [].

  • A function call: length(p), nodes(p).

  • An aggregate function: avg(x.prop), count(*).

  • A path-pattern: (a)-[r]->(b), (a)-[r]-(b), (a)--(b), (a)-->()<--(b).

  • An operator application: 1 + 2, 3 < 4.

  • A predicate expression is an expression that returns true or false: a.prop = 'Hello', length(p) > 10, a.name IS NOT NULL.

  • A special case of predicates are label and relationship type expressions: (n:A|B), ()-[r:R1|R2]→().

  • A subquery expression, for example: EXISTS { MATCH (n)-[r]→(p) WHERE p.name = 'Sven' }.

  • A regular expression: a.name =~ 'Tim.*'.

  • A case-sensitive string matching expression: a.surname STARTS WITH 'Sven', a.surname ENDS WITH 'son' or a.surname CONTAINS 'son'.

  • A CASE expression.

Note on string literals

String literals can contain the following escape sequences:

Escape sequence Character

\t

Tab

\b

Backspace

\n

Newline

\r

Carriage return

\f

Form feed

\'

Single quote

\"

Double quote

\\

Backslash

\uxxxx

Unicode UTF-16 code point (4 hex digits must follow the \u)

\Uxxxxxxxx

Unicode UTF-32 code point (8 hex digits must follow the \U)

Note on number literals

Any number literal may contain an underscore _ between digits. There may be an underscore between the 0x or 0o and the digits for hexadecimal and octal literals.

CASE expressions

Generic conditional expressions may be expressed using the CASE construct. Two variants of CASE exist within Cypher: the simple form, which allows an expression to be compared against multiple values, and the generic form, which allows multiple conditional statements to be expressed.

CASE can only be used as part of RETURN or WITH if you want to use the result in the succeeding clause or statement.

The following graph is used for the examples below:

Diagram

Simple CASE form: comparing an expression against multiple values

The expression is calculated, and compared in order with the WHEN clauses until a match is found. If no match is found, the expression in the ELSE clause is returned. However, if there is no ELSE case and no match is found, null will be returned.

Syntax:

CASE test
  WHEN value THEN result
  [WHEN ...]
  [ELSE default]
END

Arguments:

Name Description

test

A valid expression.

value

An expression whose result will be compared to test.

result

This is the expression returned as output if value matches test.

default

If no match is found, default is returned.

Query
MATCH (n)
RETURN
CASE n.eyes
  WHEN 'blue'  THEN 1
  WHEN 'brown' THEN 2
  ELSE 3
END AS result
Table 1. Result
result

2

1

3

2

1

Rows: 5

Generic CASE form: allowing for multiple conditionals to be expressed

The predicates are evaluated in order until a true value is found, and the result value is used. If no match is found, the expression in the ELSE clause is returned. However, if there is no ELSE case and no match is found, null will be returned.

Syntax:

CASE
  WHEN predicate THEN result
  [WHEN ...]
  [ELSE default]
END

Arguments:

Name Description

predicate

A predicate that is tested to find a valid alternative.

result

This is the expression returned as output if predicate evaluates to true.

default

If no match is found, default is returned.

Query
MATCH (n)
RETURN
CASE
  WHEN n.eyes = 'blue' THEN 1
  WHEN n.age < 40      THEN 2
  ELSE 3
END AS result
Table 2. Result
result

2

1

3

3

1

Rows: 5

Distinguishing between when to use the simple and generic CASE forms

Owing to the close similarity between the syntax of the two forms, sometimes it may not be clear at the outset as to which form to use. We illustrate this scenario by means of the following query, in which there is an expectation that age_10_years_ago is -1 if n.age is null:

Query
MATCH (n)
RETURN n.name,
CASE n.age
  WHEN n.age IS NULL THEN -1
  ELSE n.age - 10
END AS age_10_years_ago

However, as this query is written using the simple CASE form, instead of age_10_years_ago being -1 for the node named Daniel, it is null. This is because a comparison is made between n.age and n.age IS NULL. As n.age IS NULL is a boolean value, and n.age is an integer value, the WHEN n.age IS NULL THEN -1 branch is never taken. This results in the ELSE n.age - 10 branch being taken instead, returning null.

Table 3. Result
n.name age_10_years_ago

"Alice"

28

"Bob"

15

"Charlie"

43

"Daniel"

<null>

"Eskil"

31

Rows: 5

The corrected query, behaving as expected, is given by the following generic CASE form:

Query
MATCH (n)
RETURN n.name,
CASE
  WHEN n.age IS NULL THEN -1
  ELSE n.age - 10
END AS age_10_years_ago

We now see that the age_10_years_ago correctly returns -1 for the node named Daniel.

Table 4. Result
n.name age_10_years_ago

"Alice"

28

"Bob"

15

"Charlie"

43

"Daniel"

-1

"Eskil"

31

Rows: 5

Using the result of CASE in the succeeding clause or statement

You can use the result of CASE to set properties on a node or relationship. For example, instead of specifying the node directly, you can set a property for a node selected by an expression:

Query
MATCH (n)
WITH n,
CASE n.eyes
  WHEN 'blue'  THEN 1
  WHEN 'brown' THEN 2
  ELSE 3
END AS colourCode
SET n.colourCode = colourCode

For more information about using the SET clause, see SET.

Table 5. Result

(empty result)

Rows: 0
Properties set: 5

Using CASE with null values

When using the simple CASE form, it is useful to remember that in Cypher null = null yields null. For example, you might expect age_10_years_ago to be -1 for the node named Daniel:

Query
MATCH (n)
RETURN n.name,
CASE n.age
  WHEN null THEN -1
  ELSE n.age - 10
END AS age_10_years_ago

However, as null = null does not yield true, the WHEN null THEN -1 branch is never taken, resulting in the ELSE n.age - 10 branch being taken instead, returning null.

Table 6. Result
n.name age_10_years_ago

"Alice"

28

"Bob"

15

"Charlie"

43

"Daniel"

<null>

"Eskil"

31

Rows: 5

Subquery expressions

Cypher has expressions that evaluate a subquery and aggregate the result in different fashions.

Subquery expressions can appear anywhere that an expression is valid. A subquery has a scope, as indicated by the opening and closing braces, { and }. 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 can’t be accessed on the outside.

The following graph is used for the examples below:

Diagram

EXISTS subqueries

An EXISTS subquery can be used to find out if a specified pattern exists at least once in the data. It serves the same purpose as a path pattern but is more powerful because it allows you to use MATCH and WHERE clauses internally. Moreover, it can appear in any expression position, unlike path patterns. If the subquery evaluates to at least one row, the whole expression will become true. 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.

Syntax:

EXISTS {
  MATCH [Pattern]
  WHERE [Expression]
}

It is worth noting that the MATCH keyword can be omitted in such subqueries and that the WHERE clause is optional.

Simple EXISTS subquery

Variables introduced by the outside scope can be used in the EXISTS subquery without importing them, unlike the case for CALL subqueries, as they require importing. The following example shows this:

Query
MATCH (person:Person)
WHERE EXISTS {
  MATCH (person)-[:HAS_DOG]->(:Dog)
}
RETURN person.name AS name
Table 7. Result
name

"Andy"

"Peter"

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.

Query
MATCH (person:Person)
WHERE EXISTS {
  MATCH (person)-[:HAS_DOG]->(dog:Dog)
  WHERE person.name = dog.name
}
RETURN person.name AS name
Table 8. Result
name

"Andy"

Rows: 1

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.

Query
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
Table 9. Result
name

"Peter"

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.

Query
MATCH (person:Person)
RETURN person.name AS name, EXISTS {
  MATCH (person)-[:HAS_DOG]->(:Dog)
} AS hasDog
Table 10. Result
name hasDog

"Andy"

true

"Timothy"

false

"Peter"

true

Rows: 3

COUNT subqueries

A COUNT subquery expression can be used to count the number of results of the subquery.

Syntax:

COUNT {
  MATCH [Pattern]
  WHERE [Expression]
}

It is worth noting that the MATCH keyword can be omitted in such subqueries and that the WHERE clause is optional.

Simple COUNT subquery

Variables introduced by the outside scope can be used in the COUNT subquery without importing them, unlike the case for CALL subqueries, as they require importing. The following query exemplifies this and outputs the owners of more than one dog:

Query
MATCH (person:Person)
WHERE COUNT { (person)-[:HAS_DOG]->(:Dog) } > 1
RETURN person.name AS name
Table 11. Result
name

"Peter"

Rows: 1

COUNT subquery with WHERE clause

A WHERE clause can be used inside the COUNT pattern. Variables introduced by the MATCH clause and the outside scope can be used in this scope.

Query
MATCH (person:Person)
WHERE COUNT {
  (person)-[:HAS_DOG]->(dog:Dog)
  WHERE person.name = dog.name
} = 1
RETURN person.name AS name
Table 12. Result
name

"Andy"

Rows: 1

Using COUNT subqueries inside other clauses

COUNT can be used in any position in a query, with the exception of administration commands, where it is restricted. See a few examples below:

Using COUNT in RETURN
Query
MATCH (person:Person)
RETURN person.name, COUNT { (person)-[:HAS_DOG]->(:Dog) } as howManyDogs
Table 13. Result
person.name howManyDogs

"Andy"

1

"Timothy"

0

"Peter"

2

Rows: 3

Using COUNT in SET
Query
MATCH (person:Person) WHERE person.name ="Andy"
SET person.howManyDogs = COUNT { (person)-[:HAS_DOG]->(:Dog) }
RETURN person.howManyDogs as howManyDogs
Table 14. Result
howManyDogs

1

Rows: 1
Properties set: 1

Using COUNT in CASE
Query
MATCH (person:Person)
RETURN
   CASE
     WHEN COUNT { (person)-[:HAS_DOG]->(:Dog) } > 1 THEN "Doglover " + person.name
     ELSE person.name
   END AS result
Table 15. Result
result

"Andy"

"Timothy"

"Doglover Peter"

Rows: 3

Using COUNT as a grouping key

The following query groups all persons by how many dogs they own, and then calculates the average age for each group.

Query
MATCH (person:Person)
RETURN COUNT { (person)-[:HAS_DOG]->(:Dog) } AS numDogs,
       avg(person.age) AS averageAge
 ORDER BY numDogs
Table 16. Result
numDogs averageAge

0

25.0

1

36.0

2

35.0

Rows: 3

Label expressions

In earlier versions of Neo4j, label expressions for nodes had a single colon operator that represented the AND operator. With the release of version 5.0, a new label expression with an extended set of logical operators is being introduced, in addition to the single colon operator. It is important to note that you cannot mix these different types of label expression syntax. For more information, see Restrictions on using the different types of label expression syntax.

Label expressions evaluate to true or false when applied to the label set of a node, or the type of a relationship. Assuming no other filters are applied, then a label expression evaluating to true means the node or relationship is matched.

The following graph is used for the examples below:

Diagram

Match without label expression

A match without a label expression returns all nodes in the graph, non withstanding if the node is empty

Query
MATCH (n) RETURN n.name AS name
Table 17. Result
name

"Alice"

"Bob"

"Charlie"

"Daniel"

"Eskil"

"Frank"

"George"

"Henry"

Rows: 8

Match on single node label

A match on a single node label returns the nodes that contains the specified label

Query
MATCH (n:A) RETURN n.name AS name
Table 18. Result
name

"Alice"

"Daniel"

"Eskil"

"George"

Rows: 4

Match with an AND expression for the node labels

A match with an AND expression for the node label returns the nodes that contains both the specified labels

Query
MATCH (n:A&B) RETURN n.name AS name
Table 19. Result
name

"Daniel"

"George"

Rows: 2

Match with an OR expression for the node labels

A match with an OR expression for the node label returns the nodes that contains either of the specified labels

Query
MATCH (n:A|B) RETURN n.name AS name
Table 20. Result
name

"Alice"

"Bob"

"Daniel"

"Eskil"

"Frank"

"George"

Rows: 6

Match with a NOT expression for the node labels

A match with a NOT expression for the node label returns the nodes that does not contain the specified label

Query
MATCH (n:!A) RETURN n.name AS name
Table 21. Result
name

"Bob"

"Charlie"

"Frank"

"Henry"

Rows: 4

Match with a Wildcard expression for the node labels

A match with a Wildcard expression for the node label returns the nodes that contains any label

Query
MATCH (n:%) RETURN n.name AS name
Table 22. Result
name

"Alice"

"Bob"

"Charlie"

"Daniel"

"Eskil"

"Frank"

"George"

Rows: 7

Match with a nesting of label expressions for the node labels

A match with a nesting of label expressions for the node label returns the nodes for which the full expression is true

Query
MATCH (n:(!A&!B)|C) RETURN n.name AS name
Table 23. Result
name

"Charlie"

"Eskil"

"Frank"

"George"

"Henry"

Rows: 5

Match with label expressions in the predicate

A label expression can also be used as a predicate in the WHERE clause

Query
MATCH (n) WHERE n:A|B RETURN n.name AS name
Table 24. Result
name

"Alice"

"Bob"

"Daniel"

"Eskil"

"Frank"

"George"

Rows: 6

Match with label expressions in the return

A label expression can also be used in the WITH or RETURN statement

Query
MATCH (n) RETURN n:A&B
Table 25. Result
n:A&B

false

false

false

true

false

false

true

false

Rows: 8

List of label expressions examples

The following table displays whether the label expression matches the node:

Node

Label expression

()

(:A)

(:B)

(:C)

(:A:B)

(:A:C)

(:B:C)

(:A:B:C)

()

(:A)

(:A&B)

(:A|B)

(:!A)

(:!!A)

(:A&!A)

(:A|!A)

(:%)

(:!%)

(:%|!%)

(:%&!%)

(:A&%)

(:A|%)

(:(A&B)&!(B&C))

(:!(A&%)&%)

Restrictions on using the different types of label expression syntax

Neo4j version 5.0 introduced an ampersand operator, which is equivalent to the colon conjunction operator. Mixing the colon conjunction operator with any of the new label expression operators in the same clause will raise a syntax error.

For example, each of the following clauses will raise syntax errors:

  • MATCH (n:A|B:C)

  • MATCH (n:A:B)-[]-(m:(A&B)|C)

  • MATCH (n:A:B)--(m), (n)-→(o:(A&B)|C)

  • RETURN n:A&B, n:A:B

  • MATCH (n:A:B)-[]-(m) WHERE m:(A&B)|C

In earlier versions of Neo4j (version 4.4 and earlier), relationship type expressions only had the pipe operator. As the pipe operator will continue to act as an OR operator, it can continue to be used alongside the new operators.

To make it easier to use the new syntax when extending existing queries, using the different syntax types in separate clauses will be supported.

For example, the following query will not raise a syntax error:

MATCH (m:A:B:C)-[]->()
MATCH (n:(A&B)|C)-[]->(m)
RETURN m,n

Queries that exclusively use syntax from earlier versions of Neo4j (version 4.4 and earlier) will continue to be supported.

For example, the following will not raise a syntax error:

MATCH (m:A:B:C)-[:S|T]->()
RETURN
  CASE
    WHEN m:D:E THEN m.p
    ELSE null
  END AS result

Relationship Type Expressions

Relationship Type Expressions evaluate to true or false when applied to the type of a relationship. Assuming no other filters are applied, then a relationship type expression evaluating to true means the relationship is matched.

The following graph is used for the examples below:

Diagram

Match without relationship type expression

A match without a relationship type expression returns all relationships in the graph

Query
MATCH ()-[r]->() RETURN r.name as name
Table 26. Result
name

"Teaches"

"Studies"

"Parents"

Rows: 3

Match on single relationship type

A match on a single relationship type returns the relationships that contains the specified type

Query
MATCH ()-[r:R1]->() RETURN r.name AS name
Table 27. Result
name

"Teaches"

Rows: 1

Match with an OR expression for the relationship types

A match with an OR expression for the relationship type returns the relationships that contains either of the specified types

Query
MATCH ()-[r:R1|R2]->() RETURN r.name AS name
Table 28. Result
name

"Teaches"

"Studies"

Rows: 2

Match with a NOT expression for the relationship types

A match with a NOT expression for the relationship type returns the relationships that does not contain the specified type

Query
MATCH ()-[r:!R1]->() RETURN r.name AS name
Table 29. Result
name

"Studies"

"Parents"

Rows: 2

Match with a nesting of relationship type expressions for the relationship types

A match with a nesting of relationship type expressions for the relationship type returns the nodes for which the full expression is true

Query
MATCH ()-[r:(!R1&!R2)|R3]->() RETURN r.name as name
Table 30. Result
name

"Parents"

Rows: 1

Match with relationship type expressions in the predicate

A relationship type expression can also be used as a predicate in the WHERE clause

Query
MATCH (n)-[r]->(m) WHERE r:R1|R2 RETURN r.name AS name
Table 31. Result
name

"Teaches"

"Studies"

Rows: 2

Match with relationship type expression in the return

A relationship type expression can also be used in the WITH or RETURN statement

Query
MATCH (n)-[r]->(m) RETURN r:R1|R2 AS result
Table 32. Result
result

true

true

false

Rows: 3

Match with relationship type expression and node label expression in a case

A relationship type expression and a label expression can also be used in a case statement

Query
MATCH (n)-[r]->(m)
 RETURN
 CASE
    WHEN n:A&B THEN 1
    WHEN r:!R1&!R2 THEN 2
    ELSE -1
    END AS result
Table 33. Result
result

1

-1

2

Rows: 3

List of relationship type expressions examples

The following table displays whether the relationship type expression matches the node:

Relationship Type

Relationship Type expression

[:A]

[:B]

[:C]

[]

[:A]

[:A&B]

[:A|B]

[:!A]

[:!!A]

[:A&!A]

[:A|!A]

[:%]

[:!%]

[:%|!%]

[:%&!%]

[:A&%]

[:A|%]

Variable length relationships may only have relationship type expressions consisting of |. That means that ()-[r:!R*]-() is not allowed, whereas ()-[r:Q|R*]-() is allowed.

Relationships must have exactly one type so for example, (a)-[r:R&Q]-(b) or (a)-[r:!%]-(b) will never return any results.