Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 48 additions & 2 deletions pages/querying/expressions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,58 @@ MATCH (n)
RETURN CASE WHEN n.height < 30 THEN "short" WHEN n.height > 300 THEN "tall" END;
```

Most expressions that take `null` as input will produce `null`. This includes boolean expressions that are used as
predicates. In this case, anything that is not true is interpreted as being false. This also concludes that logically `null!=null`.
A `WHEN` predicate that evaluates to `null` counts as false, so its branch isn't
taken. See [comparisons with null](#comparisons-with-null) for which
comparisons evaluate to `null`.

The [`exists()`](/querying/functions#pattern-functions) function and the `EXISTS { … }`, `COUNT { … }` and
`COLLECT { … }` [subquery expressions](/querying/subquery-expressions) can all be used inside `CASE`.

## Comparisons with null

`null` stands for a value that isn't known, so most expressions that take
`null` as input return `null`. A comparison with `null` returns `null` too:
`null = null` and `1 <> null` are both `null`. In a `WHERE` clause or a `CASE`
predicate, `null` counts as false, so the row is filtered out or the branch
isn't taken. To test for a missing value, use `IS NULL` or `IS NOT NULL`.

The same rule applies inside lists and maps. Two lists are equal when they
have the same length and equal elements in the same positions; two maps are
equal when they have the same keys and equal values under each key. If one
pair of elements differs, or the lengths or keys differ, the comparison
returns `false`. Otherwise, if a pair of elements compares to `null`, the
whole comparison returns `null`:

| Comparison | Result |
|---|---|
| `[1] = [1]` | `true` |
| `[null] = [null]` | `null` |
| `[null] = [1]` | `null` |
| `[null] <> [null]` | `null` |
| `{k: null} = {k: null}` | `null` |
| `[null, 1] = [null, 2]` | `false`: the second elements differ |
| `[null] = [null, null]` | `false`: the lengths differ |
| `{a: null} = {b: null}` | `false`: the keys differ |

`IN` compares each element of the list with `=`, so it follows the same rule:
`1 IN [1, null]` is `true`, while `3 IN [1, null]` and `[null] IN [[null]]`
are `null`. As a result, `WHERE NOT (n.code IN ['A', 'B'])` keeps no row where
`n.code` is `null`.

A filter that compares a property with a list or map holding `null` matches
nothing, with or without an index on the property:
`MATCH (n:Order) WHERE n.tags = ['urgent', null] RETURN n` returns no nodes,
because the comparison is never `true`. The same holds for relationship
properties and for joins such as `WHERE a.tags = b.tags`.

`DISTINCT` and grouping keys treat two `null` values, and two lists or maps
that hold `null` in the same places, as the same value:
`UNWIND [{k: null}, {k: null}] AS x RETURN DISTINCT x` returns one row.

Comparing lists and maps that hold `null` changed in Memgraph 3.14. Before,
`[null] = [null]` returned `true` and a map holding `null` was never equal to
another map.

## Pattern existence (exists(pattern))

`exists(pattern)` is a short form for a simple pattern existence check. It is accepted in the same
Expand Down
Loading