Skip to content

Expressions

GQL supports a rich expression language for conditional logic, type conversion, subqueries and list operations.

CASE Expressions

Searched CASE

Evaluate conditions in order and return the first matching result:

MATCH (p:Person)
RETURN p.name,
    CASE
        WHEN p.age < 18 THEN 'minor'
        WHEN p.age < 65 THEN 'adult'
        ELSE 'senior'
    END AS category

Simple CASE

Compare an expression against specific values:

MATCH (p:Person)
RETURN p.name,
    CASE p.status
        WHEN 'A' THEN 'Active'
        WHEN 'I' THEN 'Inactive'
        WHEN 'P' THEN 'Pending'
        ELSE 'Unknown'
    END AS status_label

NULLIF

Returns null if both arguments are equal, otherwise returns the first argument. Useful for avoiding division by zero:

MATCH (d:Department)
RETURN d.name,
    d.budget / NULLIF(d.headcount, 0) AS per_capita

COALESCE

Returns the first non-null value from the argument list:

-- Display name fallback chain
MATCH (p:Person)
RETURN COALESCE(p.nickname, p.firstName, p.email) AS display_name

CAST

Convert a value to a different type with CAST(expr AS type):

-- Numeric conversions
RETURN CAST('42' AS INT)        -- 42
RETURN CAST(42 AS FLOAT)        -- 42.0
RETURN CAST(42 AS STRING)       -- '42'
RETURN CAST('true' AS BOOLEAN)  -- true

-- Temporal conversions
RETURN CAST('2024-01-15' AS DATE)
RETURN CAST('14:30:00' AS TIME)
RETURN CAST('2024-01-15T14:30:00Z' AS DATETIME)
RETURN CAST('P1Y2M' AS DURATION)

-- Zoned temporal conversions
RETURN CAST('2024-01-15T14:30:00+05:30' AS ZONED DATETIME)
RETURN CAST('14:30:00+05:30' AS ZONED TIME)

-- Wrap a scalar in a list
RETURN CAST(42 AS LIST)         -- [42]

Supported target types: INT/INTEGER, FLOAT, STRING, BOOLEAN, DATE, TIME, DATETIME, DURATION, ZONED DATETIME, ZONED TIME, LIST.

Dynamic Parameters

Use $param placeholders in queries and provide values from the host language:

-- Parameterized query
MATCH (p:Person)
WHERE p.name = $name AND p.age > $min_age
RETURN p
# Python
result = db.execute(
    "MATCH (p:Person) WHERE p.name = $name RETURN p",
    {"name": "Alix"}
)
// Node.js
const result = await db.execute(
    "MATCH (p:Person) WHERE p.name = $name RETURN p",
    { name: "Alix" }
);

LET ... IN ... END

Bind local variables within an expression. The variables are scoped to the expression only.

MATCH (p:Person)
RETURN p.name,
    LET base = p.salary,
        bonus = base * 0.1
    IN base + bonus
    END AS total_compensation

List Comprehensions

Transform and filter lists in a single expression:

-- Filter: keep only even numbers
RETURN [x IN [1, 2, 3, 4, 5] WHERE x % 2 = 0] AS evens
-- [2, 4]

-- Transform: double each value
RETURN [x IN [1, 2, 3] | x * 2] AS doubled
-- [2, 4, 6]

-- Filter and transform
RETURN [x IN [1, 2, 3, 4, 5] WHERE x > 2 | x * 10] AS result
-- [30, 40, 50]

-- With graph data
MATCH (p:Person)-[:KNOWS]->(friend)
RETURN p.name,
    [f IN collect(friend) WHERE f.age > 30 | f.name] AS older_friends

List Predicates

Test whether list elements satisfy a condition:

-- all(): every element must match
RETURN all(x IN [2, 4, 6] WHERE x % 2 = 0)  -- true

-- any(): at least one element must match
RETURN any(x IN [1, 2, 3] WHERE x > 2)  -- true

-- none(): no element must match
RETURN none(x IN [1, 2, 3] WHERE x > 5)  -- true

-- single(): exactly one element must match
RETURN single(x IN [1, 2, 3] WHERE x = 2)  -- true

-- Practical example: check path properties
MATCH path = (a:Person)-[:KNOWS*]->(b:Person)
WHERE a.name = 'Alix'
    AND all(n IN nodes(path) WHERE n.active = true)
RETURN path

reduce()

Fold a list into a single value using an accumulator:

-- Sum a list
RETURN reduce(acc = 0, x IN [1, 2, 3, 4, 5] | acc + x) AS total
-- 15

-- Build a string
RETURN reduce(s = '', name IN ['Alix', 'Gus', 'Harm'] | s + name + ', ') AS names

-- Compute from graph data
MATCH (p:Person)
WITH collect(p.salary) AS salaries
RETURN reduce(total = 0, s IN salaries | total + s) AS payroll

Subquery Expressions

EXISTS

Check whether a pattern or subquery has any results:

-- Pattern exists
MATCH (p:Person)
WHERE EXISTS { MATCH (p)-[:MANAGES]->(:Team) }
RETURN p.name AS manager

-- Subquery with filtering
MATCH (p:Person)
WHERE EXISTS {
    MATCH (p)-[:ORDERED]->(o:Order)
    WHERE o.total > 1000
}
RETURN p.name AS high_value_customer

COUNT

Count the number of results from a subquery:

MATCH (p:Person)
WHERE COUNT { MATCH (p)-[:KNOWS]->() } > 5
RETURN p.name AS popular_person

VALUE

Return a single scalar value from a subquery:

MATCH (p:Person)
RETURN p.name,
    VALUE {
        MATCH (p)-[:KNOWS]->(friend)
        RETURN count(friend)
    } AS friend_count

Index Access

Access list elements by index (0-based):

RETURN [10, 20, 30, 40][0]   -- 10
RETURN [10, 20, 30, 40][2]   -- 30

-- With graph data
MATCH (p:Person)
RETURN p.tags[0] AS first_tag

Map Literals

Create and access map (dictionary) values:

-- Map literal
RETURN {name: 'Alix', age: 30, active: true} AS person

-- Access map properties
WITH {name: 'Alix', age: 30} AS person
RETURN person.name, person.age

-- Maps in mutations
MATCH (p:Person {name: 'Alix'})
SET p += {city: 'NYC', role: 'engineer'}

SESSION_USER

Returns the current session user name. In embedded databases, this returns 'default'.

RETURN SESSION_USER