Skip to content
Open
Show file tree
Hide file tree
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
294 changes: 146 additions & 148 deletions content/develop/data-types/json/path.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,18 @@ Beginning with Redis 8.10, the JSON data type supports a richer JSONPath syntax,
- [`in` and `nin`](#membership-in-and-nin) operators: membership test on an array and nodelist
- [Operators on numbers](#arithmetic-operators): binary `-`, `+`, `*`, `/`, `%`, and unary `-` and `+`
- Operator on object: [`~`](#get-keys-operator-)
- `length()` function on array, object, and string
- Functions on number: `abs()`, `ceiling()`, `floor()`
- Functions on string: `match()`, `search()`
- Strings concatenation with `concat()`
- Functions on array: `first()`, `last()`, `index()`, `append()`
- Aggregation functions on array: `min()`, `max()`, `avg()`, `sum()`, `stddev()`
- Function on object: `keys()`
- Function on nodelist: `count()`
- Function on nodelist with exactly one node: `value()`
- [`length()`](#length) function on array, object, and string
- Functions on number: [`abs()`, `ceiling()`, `floor()`](#abs-ceiling-and-floor)
- Functions on string: [`match()`, `search()`](#match-and-search)
- Strings concatenation with [`concat()`](#concat)
- Functions on array: [`first()`, `last()`, `index()`](#first-last-and-index), [`append()`](#append)
- Aggregation functions on array: [`min()`, `max()`, `avg()`, `sum()`, `stddev()`](#min-max-avg-sum-and-stddev)
- Function on object: [`keys()`](#keys)
- Function on nodelist: [`count()`](#count)
- Function on nodelist with exactly one node: [`value()`](#value)
- Relations functions on array and nodelist: [`subsetof()`, `anyof()`, `noneof()`](#set-relations-subsetof-anyof-and-noneof)

These operators can be used within a filter expression (`?()`).
These operators can be used within a filter expression (`?()`). Functions can appear inside filter expressions and, when they return a value, as top-level [projection expressions](#projection-expressions). A function can be written in prefix form, `length($.arr)`, or in postfix (method) form, `$.arr.length()`. A path segment immediately followed by `(` is a method call, so `$.arr.length()` is a function call while `$.arr.length` is a reference to a field named `length`.

{{< warning >}}
Beginning with Redis 8.10, two changes to path parsing may affect existing queries:
Expand All @@ -101,144 +101,6 @@ The following rules apply to the operators and functions described below:
- **Arithmetic operators must be surrounded by spaces.** `@.a + 1` is addition, but `@.a+1` is a reference to a field literally named `a+1`, because the field-name character set includes characters such as `+`, `-`, `/`, `%`, `$`, `^`, `:`, and `_`.
- **Functions are arity-checked.** Calling a function with the wrong number of arguments produces *Nothing* rather than silently using a subset of the arguments.

## Functions

Functions can appear inside filter expressions and, when they return a value, as top-level [projection expressions](#projection-expressions). A function can be written in prefix form, `length($.arr)`, or in postfix (method) form, `$.arr.length()`. A path segment immediately followed by `(` is a method call, so `$.arr.length()` is a function call while `$.arr.length` is a reference to a field named `length`.

### `length()`

Returns the number of characters in a string, elements in an array, or members in an object. Any other type produces *Nothing*.

```
> JSON.SET doc $ '{"a":[[1,2,3],[1],"abcd","x"]}'
OK
> JSON.GET doc '$.a[?length(@) > 2]'
"[[1,2,3],\"abcd\"]"
```

### `count()`

Returns the number of nodes selected by a query. An absent path counts as `0`; a single node counts as `1`.

```
> JSON.SET doc $ '[{"a":1,"b":2,"c":3},{"a":1}]'
OK
> JSON.GET doc '$[?count(@.*) == 3]'
"[{\"a\":1,\"b\":2,\"c\":3}]"
```

### `value()`

Returns the value of a query that selects exactly one node. A query that selects zero or more than one node produces *Nothing*.

```
> JSON.SET doc $ '[{"a":1},{"a":2}]'
OK
> JSON.GET doc '$[?value(@.a) == 1]'
"[{\"a\":1}]"
```

### `keys()`

Returns the member names of an object as a list of strings, like the `~` operator. Unlike `~`, `keys()` is composable and can be chained with other functions.

```
> JSON.SET doc $ '{"obj":{"x":1,"y":2}}'
OK
> JSON.GET doc '$.obj.keys()'
"[\"x\",\"y\"]"
> JSON.GET doc '$.obj.keys().count()'
"[2]"
```

### `match()` and `search()`

Both test a string against a regular expression pattern ([RFC 9485](https://datatracker.ietf.org/doc/rfc9485/) I-Regexp). `match()` requires the whole string to match (anchored), while `search()` matches any substring (the same behavior as the `=~` operator). An invalid pattern produces no match.

```
> JSON.SET doc $ '{"a":["abc","xabc","a","b"]}'
OK
> JSON.GET doc '$.a[?match(@, "a.*")]'
"[\"abc\",\"a\"]"
> JSON.SET doc $ '{"a":["abc","xyz","b"]}'
OK
> JSON.GET doc '$.a[?search(@, "b")]'
"[\"abc\",\"b\"]"
```

### `concat()`

Concatenates its string arguments into a single string. It requires at least one argument, and any non-string argument produces *Nothing*.

```
> JSON.SET doc $ '{"a":[{"x":"a","y":"b"},{"x":"a","y":"c"}]}'
OK
> JSON.GET doc '$.a[?concat(@.x, @.y) == "ab"]'
"[{\"x\":\"a\",\"y\":\"b\"}]"
```

### `abs()`, `ceiling()`, and `floor()`

Operate on a number: `abs()` returns the absolute value, `ceiling()` rounds up to the nearest integer, and `floor()` rounds down. An integer argument stays an integer and a floating-point argument stays a float. A result that overflows the signed 64-bit integer range produces *Nothing*.

```
> JSON.SET doc $ '{"a":[2.1,3.9,1.0]}'
OK
> JSON.GET doc '$.a[?ceiling(@) == 3]'
"[2.1]"
> JSON.SET doc $ '{"a":[2.1,2.9,3.5]}'
OK
> JSON.GET doc '$.a[?floor(@) == 2]'
"[2.1,2.9]"
> JSON.SET doc $ '{"a":[{"n":-5},{"n":5},{"n":-3}]}'
OK
> JSON.GET doc '$.a[?abs(@.n) == 5]'
"[{\"n\":-5},{\"n\":5}]"
```

### `first()`, `last()`, and `index()`

`first(array)` and `last(array)` return the first and last element of an array. `index(array, n)` returns the element at index `n`; a negative `n` counts from the end, a fractional `n` is truncated toward zero, and an out-of-range index produces *Nothing*.

```
> JSON.SET doc $ '{"a":[{"n":[1,2]},{"n":[9,8]}]}'
OK
> JSON.GET doc '$.a[?first(@.n) == 1]'
"[{\"n\":[1,2]}]"
> JSON.GET doc '$.a[?last(@.n) == 8]'
"[{\"n\":[9,8]}]"
> JSON.GET doc '$.a[?index(@.n, -1) == 2]'
"[{\"n\":[1,2]}]"
```

### `min()`, `max()`, `avg()`, `sum()`, and `stddev()`

These aggregation functions operate on an array of numbers. `stddev()` returns the *population* standard deviation (dividing by N). The functions are strict: an array that contains any non-numeric element, or an empty array, produces *Nothing* — elements are never silently skipped.

```
> JSON.SET doc $ '{"a":[{"n":[3,1,2]},{"n":[5,6]}]}'
OK
> JSON.GET doc '$.a[?sum(@.n) == 6]'
"[{\"n\":[3,1,2]}]"
> JSON.GET doc '$.a[?avg(@.n) == 2]'
"[{\"n\":[3,1,2]}]"
```

### `append()`

`append(value, ...)` returns the matched array with the given value or values added after its elements. It is a read-only query-time projection and does not modify the stored document — to mutate an array in place, use the [`JSON.ARRAPPEND`]({{< relref "commands/json.arrappend/" >}}) command instead. A multiple-value argument is added as a single element (it is not spread), and a *Nothing* argument makes the whole result *Nothing*.

```
> JSON.SET doc $ '{"arr":[1,2,3]}'
OK
> JSON.GET doc '$.arr.append(9)'
"[1,2,3,9]"
> JSON.SET doc $ '{"books":[{"t":"a","price":30},{"t":"b","price":5}]}'
OK
> JSON.GET doc '$.books[?(@.price >= 10)].append({"t":"X"})'
"[{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]"
```

## JSONPath examples

The following examples use this JSON document, which stores details about items in a store's inventory:
Expand Down Expand Up @@ -516,6 +378,142 @@ OK
"[]"
{{< /clients-example >}}

Functions can appear inside filter expressions and, when they return a value, as top-level [projection expressions](#projection-expressions). Beginning with Redis 8.10, JSONPath supports the following functions.

#### `length()`

Returns the number of characters in a string, elements in an array, or members in an object. Any other type produces *Nothing*.

{{< clients-example set="json_path_ops" step="func_length" description="Length function: Use length() to get the number of characters in a string, elements in an array, or members in an object" difficulty="advanced" >}}
> JSON.SET doc $ '{"a":[[1,2,3],[1],"abcd","x"]}'
OK
> JSON.GET doc '$.a[?length(@) > 2]'
"[[1,2,3],\"abcd\"]"
{{< /clients-example >}}

#### `count()`

Returns the number of nodes selected by a query. An absent path counts as `0`; a single node counts as `1`.

{{< clients-example set="json_path_ops" step="func_count" description="Count function: Use count() to get the number of nodes selected by a query" difficulty="advanced" >}}
> JSON.SET doc $ '[{"a":1,"b":2,"c":3},{"a":1}]'
OK
> JSON.GET doc '$[?count(@.*) == 3]'
"[{\"a\":1,\"b\":2,\"c\":3}]"
{{< /clients-example >}}

#### `value()`

Returns the value of a query that selects exactly one node. A query that selects zero or more than one node produces *Nothing*.

{{< clients-example set="json_path_ops" step="func_value" description="Value function: Use value() to get the value of a query that selects exactly one node" difficulty="advanced" >}}
> JSON.SET doc $ '[{"a":1},{"a":2}]'
OK
> JSON.GET doc '$[?value(@.a) == 1]'
"[{\"a\":1}]"
{{< /clients-example >}}

#### `keys()`

Returns the member names of an object as a list of strings, like the `~` operator. Unlike `~`, `keys()` is composable and can be chained with other functions.

{{< clients-example set="json_path_ops" step="func_keys" description="Keys function: Use keys() to get an object's member names as a composable, chainable list of strings" difficulty="advanced" >}}
> JSON.SET doc $ '{"obj":{"x":1,"y":2}}'
OK
> JSON.GET doc '$.obj.keys()'
"[\"x\",\"y\"]"
> JSON.GET doc '$.obj.keys().count()'
"[2]"
{{< /clients-example >}}

#### `match()` and `search()`

Both test a string against a regular expression pattern ([RFC 9485](https://datatracker.ietf.org/doc/rfc9485/) I-Regexp). `match()` requires the whole string to match (anchored), while `search()` matches any substring (the same behavior as the `=~` operator). An invalid pattern produces no match.

{{< clients-example set="json_path_ops" step="func_match_search" description="Regex functions: Use match() for an anchored (whole-string) regular expression match and search() for a partial match" difficulty="advanced" >}}
> JSON.SET doc $ '{"a":["abc","xabc","a","b"]}'
OK
> JSON.GET doc '$.a[?match(@, "a.*")]'
"[\"abc\",\"a\"]"
> JSON.SET doc $ '{"a":["abc","xyz","b"]}'
OK
> JSON.GET doc '$.a[?search(@, "b")]'
"[\"abc\",\"b\"]"
{{< /clients-example >}}

#### `concat()`

Concatenates its string arguments into a single string. It requires at least one argument, and any non-string argument produces *Nothing*.

{{< clients-example set="json_path_ops" step="func_concat" description="Concat function: Use concat() to join string arguments into a single string for comparison in a filter" difficulty="advanced" >}}
> JSON.SET doc $ '{"a":[{"x":"a","y":"b"},{"x":"a","y":"c"}]}'
OK
> JSON.GET doc '$.a[?concat(@.x, @.y) == "ab"]'
"[{\"x\":\"a\",\"y\":\"b\"}]"
{{< /clients-example >}}

#### `abs()`, `ceiling()`, and `floor()`

Operate on a number: `abs()` returns the absolute value, `ceiling()` rounds up to the nearest integer, and `floor()` rounds down. An integer argument stays an integer and a floating-point argument stays a float. A result that overflows the signed 64-bit integer range produces *Nothing*.

{{< clients-example set="json_path_ops" step="func_math" description="Numeric functions: Use abs(), ceiling(), and floor() to transform a number before comparing it in a filter" difficulty="advanced" >}}
> JSON.SET doc $ '{"a":[2.1,3.9,1.0]}'
OK
> JSON.GET doc '$.a[?ceiling(@) == 3]'
"[2.1]"
> JSON.SET doc $ '{"a":[2.1,2.9,3.5]}'
OK
> JSON.GET doc '$.a[?floor(@) == 2]'
"[2.1,2.9]"
> JSON.SET doc $ '{"a":[{"n":-5},{"n":5},{"n":-3}]}'
OK
> JSON.GET doc '$.a[?abs(@.n) == 5]'
"[{\"n\":-5},{\"n\":5}]"
{{< /clients-example >}}

#### `first()`, `last()`, and `index()`

`first(array)` and `last(array)` return the first and last element of an array. `index(array, n)` returns the element at index `n`; a negative `n` counts from the end, a fractional `n` is truncated toward zero, and an out-of-range index produces *Nothing*.

{{< clients-example set="json_path_ops" step="func_array_access" description="Array access functions: Use first(), last(), and index() to pick a single element out of an array before comparing it" difficulty="advanced" >}}
> JSON.SET doc $ '{"a":[{"n":[1,2]},{"n":[9,8]}]}'
OK
> JSON.GET doc '$.a[?first(@.n) == 1]'
"[{\"n\":[1,2]}]"
> JSON.GET doc '$.a[?last(@.n) == 8]'
"[{\"n\":[9,8]}]"
> JSON.GET doc '$.a[?index(@.n, -1) == 2]'
"[{\"n\":[1,2]}]"
{{< /clients-example >}}

#### `min()`, `max()`, `avg()`, `sum()`, and `stddev()`

These aggregation functions operate on an array of numbers. `stddev()` returns the *population* standard deviation (dividing by N). The functions are strict: an array that contains any non-numeric element, or an empty array, produces *Nothing* — elements are never silently skipped.

{{< clients-example set="json_path_ops" step="func_aggregate" description="Aggregation functions: Use sum(), avg(), min(), max(), or stddev() to reduce an array of numbers to a single value in a filter" difficulty="advanced" >}}
> JSON.SET doc $ '{"a":[{"n":[3,1,2]},{"n":[5,6]}]}'
OK
> JSON.GET doc '$.a[?sum(@.n) == 6]'
"[{\"n\":[3,1,2]}]"
> JSON.GET doc '$.a[?avg(@.n) == 2]'
"[{\"n\":[3,1,2]}]"
{{< /clients-example >}}

#### `append()`

`append(value, ...)` returns the matched array with the given value or values added after its elements. It is a read-only query-time projection and does not modify the stored document — to mutate an array in place, use the [`JSON.ARRAPPEND`]({{< relref "commands/json.arrappend/" >}}) command instead. A multiple-value argument is added as a single element (it is not spread), and a *Nothing* argument makes the whole result *Nothing*.

{{< clients-example set="json_path_ops" step="func_append" description="Append function: Use append() as a read-only, query-time projection that adds a value after an array's elements without modifying the stored document" difficulty="advanced" >}}
> JSON.SET doc $ '{"arr":[1,2,3]}'
OK
> JSON.GET doc '$.arr.append(9)'
"[1,2,3,9]"
> JSON.SET doc $ '{"books":[{"t":"a","price":30},{"t":"b","price":5}]}'
OK
> JSON.GET doc '$.books[?(@.price >= 10)].append({"t":"X"})'
"[{\"t\":\"a\",\"price\":30},{\"t\":\"X\"}]"
{{< /clients-example >}}

### Update examples

You can also use JSONPath queries when you want to update specific sections of a JSON document.
Expand Down
Loading
Loading