3.2.2. Expressions

3.2.2.1. Expressions in general

An expression in Cypher can be:

  • A decimal (integer or double) literal: 13, -40000, 3.14, 6.022E23.
  • A hexadecimal integer literal (starting with 0x): 0x13zf, 0xFC3A9, -0x66eff.
  • An octal integer literal (starting with 0): 01372, 02127, -05671.
  • A string literal: 'Hello', "World".
  • A boolean literal: true, false, 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)-->()<--(b).
  • An operator application: 1 + 2 and 3 < 4.
  • A predicate expression is an expression that returns true or false: a.prop = 'Hello', length(p) > 10, exists(a.name).
  • A regular expression: a.name =~ 'Tob.*'
  • A case-sensitive string matching expression: a.surname STARTS WITH 'Sven', a.surname ENDS WITH 'son' or a.surname CONTAINS 'son'
  • A CASE expression.

3.2.2.2. 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)

3.2.2.3. CASE expressions

Generic conditional expressions may be expressed using the well-known 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.

The following graph is used for the examples below:

Figure 3.2. Graph
alt
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 3.1. Result
result

5 rows

2

1

3

2

1

Try this query live.  CREATE (alice:A {name:'Alice', age: 38, eyes: 'brown'}), (bob:B {name: 'Bob', age: 25, eyes: 'blue'}), (charlie:C {name: 'Charlie', age: 53, eyes: 'green'}), (daniel:D {name: 'Daniel', eyes: 'brown'}), (eskil:E {name: 'Eskil', age: 41, eyes: 'blue', array: ['one', 'two', 'three']}), (alice)-[:KNOWS]->(bob), (alice)-[:KNOWS]->(charlie), (bob)-[:KNOWS]->(daniel), (charlie)-[:KNOWS]->(daniel), (bob)-[:MARRIED]->(eskil) MATCH (n) RETURN CASE n.eyes WHEN 'blue' THEN 1 WHEN 'brown' THEN 2 ELSE 3 END AS result

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 3.2. Result
result

5 rows

2

1

3

3

1

Try this query live.  CREATE (alice:A {name:'Alice', age: 38, eyes: 'brown'}), (bob:B {name: 'Bob', age: 25, eyes: 'blue'}), (charlie:C {name: 'Charlie', age: 53, eyes: 'green'}), (daniel:D {name: 'Daniel', eyes: 'brown'}), (eskil:E {name: 'Eskil', age: 41, eyes: 'blue', array: ['one', 'two', 'three']}), (alice)-[:KNOWS]->(bob), (alice)-[:KNOWS]->(charlie), (bob)-[:KNOWS]->(daniel), (charlie)-[:KNOWS]->(daniel), (bob)-[:MARRIED]->(eskil) MATCH (n) RETURN CASE WHEN n.eyes = 'blue' THEN 1 WHEN n.age < 40 THEN 2 ELSE 3 END AS result

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.3. Result
n.name age_10_years_ago

5 rows

"Alice"

28

"Bob"

15

"Charlie"

43

"Daniel"

<null>

"Eskil"

31

Try this query live.  CREATE (alice:A {name:'Alice', age: 38, eyes: 'brown'}), (bob:B {name: 'Bob', age: 25, eyes: 'blue'}), (charlie:C {name: 'Charlie', age: 53, eyes: 'green'}), (daniel:D {name: 'Daniel', eyes: 'brown'}), (eskil:E {name: 'Eskil', age: 41, eyes: 'blue', array: ['one', 'two', 'three']}), (alice)-[:KNOWS]->(bob), (alice)-[:KNOWS]->(charlie), (bob)-[:KNOWS]->(daniel), (charlie)-[:KNOWS]->(daniel), (bob)-[:MARRIED]->(eskil) 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

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 3.4. Result
n.name age_10_years_ago

5 rows

"Alice"

28

"Bob"

15

"Charlie"

43

"Daniel"

-1

"Eskil"

31

Try this query live.  CREATE (alice:A {name:'Alice', age: 38, eyes: 'brown'}), (bob:B {name: 'Bob', age: 25, eyes: 'blue'}), (charlie:C {name: 'Charlie', age: 53, eyes: 'green'}), (daniel:D {name: 'Daniel', eyes: 'brown'}), (eskil:E {name: 'Eskil', age: 41, eyes: 'blue', array: ['one', 'two', 'three']}), (alice)-[:KNOWS]->(bob), (alice)-[:KNOWS]->(charlie), (bob)-[:KNOWS]->(daniel), (charlie)-[:KNOWS]->(daniel), (bob)-[:MARRIED]->(eskil) MATCH (n) RETURN n.name, CASE WHEN n.age IS NULL THEN -1 ELSE n.age - 10 END AS age_10_years_ago