From de79914b239fd640dab1a88e1c9a05545f8990e9 Mon Sep 17 00:00:00 2001 From: Ryan Kuo Date: Thu, 1 May 2025 17:44:37 -0400 Subject: [PATCH 1/8] draft JSONPath documentation --- src/current/v25.2/jsonpath.md | 567 ++++++++++++++++++++++++++++++++++ 1 file changed, 567 insertions(+) create mode 100644 src/current/v25.2/jsonpath.md diff --git a/src/current/v25.2/jsonpath.md b/src/current/v25.2/jsonpath.md new file mode 100644 index 00000000000..144a9eb5bba --- /dev/null +++ b/src/current/v25.2/jsonpath.md @@ -0,0 +1,567 @@ +--- +title: JSONPath Queries +summary: Use JSONPath expressions, functions, operators, and methods to navigate, extract, and transform JSONB data. +toc: true +docs_area: reference.sql +--- + +{% include_cached new-in.html version="v25.2" %} JSONPath expressions and functions are used to query and filter [`JSONB`]({% link {{ page.version.version }}/jsonb.md %}) data. A [JSONPath expression](#jsonpath-expression) is a string that identifies one or more elements in a JSON document, and is used as a [JSONPath function](#jsonpath-functions) argument. + +## JSONPath expression + +A JSONPath expression has the following generic structure: + +~~~ +[mode] $[.key] [? (filter_exp)].[key].[method] +~~~ + +- `mode` is an optional mode (`lax` or `strict`) that determines how structural mismatches are tolerated. If not specified, the mode is `lax`. Refer to [Structural error handling](#structural-error-handling). +- `$` is the root of the JSONPath, and must be the first element in a JSONPath expression. For an example, refer to [Access entire document](#access-entire-document). +- `.key` is an optional key accessor that refers to a named field in a `JSONB` object. To access array elements, use `.key[index]` or `.key[*]`. Each successive key accessor navigates one level deeper into the structure. `.*` matches all fields in the current object. +- `? (filter_exp)` is an optional filter expression that evaluates the path according to a [predicate check expression](#check-expression), only returning the results that match. For syntax details, refer to [Filter expressions](#filter-expressions). +- `method` is an optional [JSONPath method](#jsonpath-methods) that can be used to access or transform the current path value. + +The path is evaluated left to right, and each stage refines or filters the result. + +## JSONPath functions + +Use JSONPath functions to extract or evaluate target `JSONB` data according to a specified [path](#jsonpath-expression). For full details on JSONPath functions, refer to [Functions and Operators]({% link {{ page.version.version }}/functions-and-operators.md %}# jsonpath-functions). + +| Function | Description | If no match | +|-------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| +| `jsonb_path_exists(jsonb, jsonpath)` | Returns true if any match is found. | `false` | +| `jsonb_path_match(jsonb, jsonpath)` | Returns true if the path expression evaluates to true. Only useful with [predicate check expressions](#predicate-check-expressions), as it expects a single Boolean value. | `false` | +| `jsonb_path_query(jsonb, jsonpath)` | Returns all matches as a set of `JSONB` values. | `NULL` | +| `jsonb_path_query_array(jsonb, jsonpath)` | Returns all matches as a single `JSONB` array. | `[]` | +| `jsonb_path_query_first(jsonb, jsonpath)` | Returns the first match only. | `NULL` | + +Each function accepts two required and two optional arguments as follows: + +- `target` (required): A [`JSONB`]({% link {{ page.version.version }}/jsonb.md %}) value to access. +- `path` (required): A [JSONPath expression](#jsonpath-expression). +- `vars`: An optional value referenced as a variable in the path. +- `silent`: An optional Boolean that specifies whether to throw errors during execution. If not specified, this is `false`. + +## JSONPath operators + +Use the following operators in [predicate check expressions](#check-expressions) to compare values, evaluate conditions, and combine logical clauses. + +| Operator | Description | Example usage | +|---------------|----------------------------------------|------------------------------------| +| `==` | Equality | `@.team == "Lakers"` | +| `!=` | Inequality | `@.name != "Luka"` | +| `>` | Greater than | `@.stats.ppg > 25` | +| `<` | Less than | `@.stats.ppg < 30` | +| `>=` | Greater than or equal to | `@.stats.rpg >= 10` | +| `<=` | Less than or equal to | `@.stats.apg <= 4` | +| `starts with` | String prefix match | `@.name starts with "A"` | +| `like_regex` | Regex string match | `@.name like_regex "^L.*"` | +| `is unknown` | True if expression evaluates to `null` | `(@.age > 25) is unknown` | +| `!` | Logical NOT | `!(@.team == "Mavericks")` | +| `&&` | Logical AND | `@.ppg > 20 && @.team == "Lakers"` | +| `||` | Logical OR | `@.ppg > 20 || @.team == "Lakers"` | + +## JSONPath methods + +Use the following methods to access or transform data in the path. For examples, refer to [Access using methods](#access-using-methods). + +| Method | Description | Example usage | +|-------------|-------------------------------------------------------------------------|------------------------------------| +| `size()` | Returns the size of an array, or `1` for a scalar. | `$.players.size()` | +| `type()` | Returns the type of the current value as a string. | `$.players[0].stats.type()` | +| `abs()` | Returns the absolute value of a number. | `$.players[0].stats.bpg.abs()` | +| `floor()` | Returns the nearest integer less than or equal to the current value. | `$.players[1].stats.ppg.floor()` | +| `ceiling()` | Returns the nearest integer greater than or equal to the current value. | `$.players[1].stats.ppg.ceiling()` | + +## Example setup + +To follow the examples on this page, create and populate the following table: + +{% include_cached copy-clipboard.html %} +~~~ sql +CREATE TABLE stats (data JSONB); + +INSERT INTO stats VALUES ( + '{ + "season": "2023-24", + "players": [ + { + "name": "Anthony Davis", + "team": "Lakers", + "stats": { + "ppg": 24.7, + "apg": 3.5, + "rpg": 12.6 + } + }, + { + "name": "Jayson Tatum", + "team": "Celtics", + "stats": { + "ppg": 26.9, + "apg": 4.9, + "rpg": 8.1 + } + }, + { + "name": "Luka Doncic", + "team": "Mavericks", + "stats": { + "ppg": 33.9, + "apg": 9.8, + "rpg": 9.2 + } + } + ] + }' +); +~~~ + +The examples use JSONPath functions, operators, and methods to navigate the preceding `JSONB` structure. + +## Access `JSONB` content + +Access `JSONB` content by passing a [JSONPath expression](#jsonpath-expression) to a [JSONPath function](#jsonpath-functions). + +### Access entire document + +To return the entire `JSONB` value, query the root accessor (`$`): + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$') FROM stats; +~~~ + +~~~ + jsonb_path_query +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {"players": [{"name": "Anthony Davis", "stats": {"apg": 3.5, "ppg": 24.7, "rpg": 12.6}, "team": "Lakers"}, {"name": "Jayson Tatum", "stats": {"apg": 4.9, "ppg": 26.9, "rpg": 8.1}, "team": "Celtics"}, {"name": "Luka Doncic", "stats": {"apg": 9.8, "ppg": 33.9, "rpg": 9.2}, "team": "Mavericks"}], "season": "2023-24"} + ~~~ + +### Access `JSONB` fields + +To return a specific field in the `JSONB` value, query its corresponding key accessor. + +The following query returns the `season` value from the root object: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.season') FROM stats; +~~~ + +~~~ + jsonb_path_query +-------------------- + "2023-24" +~~~ + +To access nested keys, append key accessors to their parent keys. The following query returns the `stats` value for each element in the `players` array, using the [array accessor](#access-array-elements): + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[*].stats') FROM stats; +~~~ + +~~~ + jsonb_path_query +------------------------------------------ + {"apg": 3.5, "ppg": 24.7, "rpg": 12.6} + {"apg": 4.9, "ppg": 26.9, "rpg": 8.1} + {"apg": 9.8, "ppg": 33.9, "rpg": 9.2} +~~~ + +Descending one more level, the following query returns the `ppg` value within the `stats` array for each player: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[*].stats.ppg') FROM stats; +~~~ + +~~~ + jsonb_path_query +-------------------- + 24.7 + 26.9 + 33.9 +~~~ + +### Access `JSONB` array elements + +Access `JSONB` array elements through their index value (`JSONB` arrays are 0-indexed), the `*` (any element) keyword, or the `last` (last element) keyword. + +The following query returns the `name` value of the first element in the `players` array (i.e., the first player's name): + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[0].name') FROM stats; +~~~ + +~~~ + jsonb_path_query +-------------------- + "Anthony Davis" +~~~ + +The following query returns the last player's name, using the `last` keyword: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[last].name') FROM stats; +~~~ + +~~~ + jsonb_path_query +-------------------- + "Luka Doncic" +~~~ + +The following query returns the array elements in the range 0-1: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[0 to 1]') FROM stats; +~~~ + +~~~ + jsonb_path_query +------------------------------------------------------------------------------------------------ + {"name": "Anthony Davis", "stats": {"apg": 3.5, "ppg": 24.7, "rpg": 12.6}, "team": "Lakers"} + {"name": "Jayson Tatum", "stats": {"apg": 4.9, "ppg": 26.9, "rpg": 8.1}, "team": "Celtics"} +~~~ + +Use a comma-separated list to return the union of mulitple array accessors: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[1 to 2, 0]') FROM stats; +~~~ + +~~~ + jsonb_path_query +------------------------------------------------------------------------------------------------ + {"name": "Jayson Tatum", "stats": {"apg": 4.9, "ppg": 26.9, "rpg": 8.1}, "team": "Celtics"} + {"name": "Luka Doncic", "stats": {"apg": 9.8, "ppg": 33.9, "rpg": 9.2}, "team": "Mavericks"} + {"name": "Anthony Davis", "stats": {"apg": 3.5, "ppg": 24.7, "rpg": 12.6}, "team": "Lakers"} +~~~ + +### Access using methods + +You can use [JSONPath methods](#jsonpath-methods) to access or transform data in the path. + +The following query returns the type of each value in the `players` array, using the `type()` method: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[*].type()') FROM stats; +~~~ + +~~~ + jsonb_path_query +-------------------- + "object" + "object" + "object" +~~~ + +The following query returns the number of objects in the `players` array, using the `size()` method: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players.size()') FROM stats; +~~~ + +~~~ + jsonb_path_query +-------------------- + 3 +~~~ + +The following query rounds down the `ppg` statistic for each player, using the `floor()` method: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[*].stats.ppg.floor()') FROM stats; +~~~ + +Returns the floor of the PPG value. + +~~~ + jsonb_path_query +-------------------- + 24 + 26 + 33 +~~~ + +## Check expressions + +A JSONPath expression can be a *predicate check expression* that returns a Boolean value. Use one or more [JSONPath operators](#jsonpath-operators) to specify conditions such as equality, logical expressions, and existence. + +Each of the following check expressions evaluates to true: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[2].name == "Luka Doncic"') FROM stats; +~~~ + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[2].name starts with "L"') FROM stats; +~~~ + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[2].stats.ppg > 30') FROM stats; +~~~ + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '(($.players[2].team != "Lakers") && ($.players[1].stats.ppg > 25))') FROM stats; +~~~ + +~~~ + jsonb_path_match +-------------------- + t +~~~ + +{{site.data.alerts.callout_info}} +The preceding check expressions can be used with the `jsonb_path_match` [function](#jsonpath-functions) for an identical result. +{{site.data.alerts.end}} + +## Filter expressions + +A *filter expression* uses a [predicate check expression](#check-expressions) to return `JSONB` fields that match a condition. + +To write a filter expression, insert `?` into a JSONPath expression, followed by a check expression that uses `@` to reference each item selected by the preceding path step. The filter expression only returns items that evaluate to true. The path can optionally continue after the filter. + +For example, the following JSONPath expression selects all elements in an `items` array (`$.items[*]`), filters all items whose `price` value is greater than 100 (`(@.price > 100)`), and returns the `name` value for each filtered item (`.name`): + +~~~ +$.items[*] ? (@.price > 100).name; +~~~ + +### Filter with comparison operators + +The following query returns all players who averaged more than 25 points per game: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[*] ? (@.stats.ppg > 25)') FROM stats; +~~~ + +~~~ + jsonb_path_query +------------------------------------------------------------------------------------------------ + {"name": "Jayson Tatum", "stats": {"apg": 4.9, "ppg": 26.9, "rpg": 8.1}, "team": "Celtics"} + {"name": "Luka Doncic", "stats": {"apg": 9.8, "ppg": 33.9, "rpg": 9.2}, "team": "Mavericks"} +~~~ + +The following modification to the query returns only the player names: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[*] ? (@.stats.ppg > 25).name') FROM stats; +~~~ + +~~~ + jsonb_path_query +-------------------- + "Jayson Tatum" + "Luka Doncic" +~~~ + +You can sequence multiple filters in a query. The following query adds a filter on the `rpg` statistic to the preceding filter: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[*] ? (@.stats.ppg > 25) ? (@.stats.rpg >= 9).name') FROM stats; +~~~ + +~~~ + jsonb_path_query +-------------------- + "Luka Doncic" +~~~ + +To have a filter return a Boolean value, use the `jsonb_path_exists` [function](#jsonpath-functions). The following query evaluates whether any player averaged more than 25 points per game: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_exists(data, '$.players[*] ? (@.stats.ppg > 25)') FROM stats; +~~~ + +~~~ + jsonb_path_exists +--------------------- + t +~~~ + +### Filter with string matching + +The following two queries use the `starts with` and `like_regex` operators to return the same result: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[*] ? (@.team starts with "L")') FROM stats; +~~~ + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[*] ? (@.team like_regex "^L.*")') FROM stats; +~~~ + +~~~ + jsonb_path_query +------------------------------------------------------------------------------------------------ + {"name": "Anthony Davis", "stats": {"apg": 3.5, "ppg": 24.7, "rpg": 12.6}, "team": "Lakers"} +~~~ + +## Variables in JSONPath expressions + +Define a variable in a JSONPath expression by prefixing it with `$`. Then specify a value as an argument in the [JSONPath function](#jsonpath-function). + +The following query filters players whose `ppg` is greater than the value of the `min` variable: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, '$.players[*] ? (@.stats.ppg > $min)', '{"min": 25}') FROM stats; +~~~ + +~~~ + jsonb_path_query +------------------------------------------------------------------------------------------------ + {"name": "Jayson Tatum", "stats": {"apg": 4.9, "ppg": 26.9, "rpg": 8.1}, "team": "Celtics"} + {"name": "Luka Doncic", "stats": {"apg": 9.8, "ppg": 33.9, "rpg": 9.2}, "team": "Mavericks"} +~~~ + +## Arithmetic operations + +JSONPath expressions can include arithmetic: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query('{}', '1+1'); +~~~ + +~~~ + jsonb_path_query +-------------------- + 2 +~~~ + +## Control function output + +### Return first match + +Use the `jsonb_path_query_first` [function](#jsonpath-functions) to return the first query result. + +The following query returns the first `name` in the `players` array: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query_first(data, '$.players[*].name') FROM stats; +~~~ + +~~~ + jsonb_path_query_first +------------------------ + "Anthony Davis" +~~~ + +### Return all matches as array + +Use the `jsonb_path_query_array` [function](#jsonpath-functions) to return all query results as a `JSONB` array. + +The following query returns all `team` values in a single array. + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query_array(data, '$.players[*].team') FROM stats; +~~~ + +~~~ + jsonb_path_query_array +-------------------------------------- + ["Lakers", "Celtics", "Mavericks"] +~~~ + +### Structural error handling + +By default, JSONPath expressions are evaluated in `lax` mode, which tolerates structural mismatches between the path and the `JSONB` data: + +- When the path contains keys that are missing in the `JSONB` structure, `NULL` or `false` values are returned, [depending on the function](#jsonpath-functions), rather than an error. +- Values are automatically wrapped into arrays or unwrapped from arrays. For example, the following query works without an array accessor because it unwraps the `players` array: + + {% include_cached copy-clipboard.html %} + ~~~ sql + SELECT jsonb_path_query(data, '$.players.stats') FROM stats; + ~~~ + + ~~~ + jsonb_path_query + ------------------------------------------ + {"apg": 3.5, "ppg": 24.7, "rpg": 12.6} + {"apg": 4.9, "ppg": 26.9, "rpg": 8.1} + {"apg": 9.8, "ppg": 33.9, "rpg": 9.2} + ~~~ + +To enforce strict access rules, specify `strict` at the start of the path expression. The following query throws an error because it requires an explicit [array accessor](#access-jsonb-array-elements) for `players` (e.g., `players[*]`): + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, 'strict $.players.stats') FROM stats; +~~~ + +~~~ +ERROR: jsonpath member accessor can only be applied to an object +SQLSTATE: 2203A +~~~ + +However, setting `silent` to `true` in a [JSONPath function](#jsonpath-function) suppresses any errors: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query(data, 'strict $.players.stats', '{}', true) FROM stats; +~~~ + +~~~ + jsonb_path_query +-------------------- +~~~ + +### Order of evaluation with parentheses + +A JSONPath expression is normally evaluated left to right, while respecting operator precedence. + +For example, multiplication takes precedence over addition: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query('{}', '1 + 2 * 3'); +~~~ + +~~~ + jsonb_path_query +-------------------- + 7 +~~~ + +Use parentheses to override the default order: + +{% include_cached copy-clipboard.html %} +~~~ sql +SELECT jsonb_path_query('{}', '(1 + 2) * 3'); +~~~ + +~~~ + jsonb_path_query +-------------------- + 9 +~~~ + +## See also + +- [JSONB]({% link {{ page.version.version }}/jsonb.md %}) +- [Data Types]({% link {{ page.version.version }}/data-types.md %}) +- [Functions and Operators]({% link {{ page.version.version }}/functions-and-operators.md %}) \ No newline at end of file From 7e49ae5c7192abda63001638453230ad1be47957 Mon Sep 17 00:00:00 2001 From: Ryan Kuo Date: Thu, 1 May 2025 18:04:07 -0400 Subject: [PATCH 2/8] fix links --- src/current/v25.2/jsonpath.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/current/v25.2/jsonpath.md b/src/current/v25.2/jsonpath.md index 144a9eb5bba..6a148d1006f 100644 --- a/src/current/v25.2/jsonpath.md +++ b/src/current/v25.2/jsonpath.md @@ -18,7 +18,7 @@ A JSONPath expression has the following generic structure: - `mode` is an optional mode (`lax` or `strict`) that determines how structural mismatches are tolerated. If not specified, the mode is `lax`. Refer to [Structural error handling](#structural-error-handling). - `$` is the root of the JSONPath, and must be the first element in a JSONPath expression. For an example, refer to [Access entire document](#access-entire-document). - `.key` is an optional key accessor that refers to a named field in a `JSONB` object. To access array elements, use `.key[index]` or `.key[*]`. Each successive key accessor navigates one level deeper into the structure. `.*` matches all fields in the current object. -- `? (filter_exp)` is an optional filter expression that evaluates the path according to a [predicate check expression](#check-expression), only returning the results that match. For syntax details, refer to [Filter expressions](#filter-expressions). +- `? (filter_exp)` is an optional filter expression that evaluates the path according to a [predicate check expression](#check-expressions), only returning the results that match. For syntax details, refer to [Filter expressions](#filter-expressions). - `method` is an optional [JSONPath method](#jsonpath-methods) that can be used to access or transform the current path value. The path is evaluated left to right, and each stage refines or filters the result. @@ -27,13 +27,13 @@ The path is evaluated left to right, and each stage refines or filters the resul Use JSONPath functions to extract or evaluate target `JSONB` data according to a specified [path](#jsonpath-expression). For full details on JSONPath functions, refer to [Functions and Operators]({% link {{ page.version.version }}/functions-and-operators.md %}# jsonpath-functions). -| Function | Description | If no match | -|-------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| -| `jsonb_path_exists(jsonb, jsonpath)` | Returns true if any match is found. | `false` | -| `jsonb_path_match(jsonb, jsonpath)` | Returns true if the path expression evaluates to true. Only useful with [predicate check expressions](#predicate-check-expressions), as it expects a single Boolean value. | `false` | -| `jsonb_path_query(jsonb, jsonpath)` | Returns all matches as a set of `JSONB` values. | `NULL` | -| `jsonb_path_query_array(jsonb, jsonpath)` | Returns all matches as a single `JSONB` array. | `[]` | -| `jsonb_path_query_first(jsonb, jsonpath)` | Returns the first match only. | `NULL` | +| Function | Description | If no match | +|-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| +| `jsonb_path_exists(jsonb, jsonpath)` | Returns true if any match is found. | `false` | +| `jsonb_path_match(jsonb, jsonpath)` | Returns true if the path expression evaluates to true. Only useful with [predicate check expressions](#check-expressions), as it expects a single Boolean value. | `false` | +| `jsonb_path_query(jsonb, jsonpath)` | Returns all matches as a set of `JSONB` values. | `NULL` | +| `jsonb_path_query_array(jsonb, jsonpath)` | Returns all matches as a single `JSONB` array. | `[]` | +| `jsonb_path_query_first(jsonb, jsonpath)` | Returns the first match only. | `NULL` | Each function accepts two required and two optional arguments as follows: @@ -155,7 +155,7 @@ SELECT jsonb_path_query(data, '$.season') FROM stats; "2023-24" ~~~ -To access nested keys, append key accessors to their parent keys. The following query returns the `stats` value for each element in the `players` array, using the [array accessor](#access-array-elements): +To access nested keys, append key accessors to their parent keys. The following query returns the `stats` value for each element in the `players` array, using the [array accessor](#access-jsonb-array-elements): {% include_cached copy-clipboard.html %} ~~~ sql @@ -419,7 +419,7 @@ SELECT jsonb_path_query(data, '$.players[*] ? (@.team like_regex "^L.*")') FROM ## Variables in JSONPath expressions -Define a variable in a JSONPath expression by prefixing it with `$`. Then specify a value as an argument in the [JSONPath function](#jsonpath-function). +Define a variable in a JSONPath expression by prefixing it with `$`. Then specify a value as an argument in the [JSONPath function](#jsonpath-functions). The following query filters players whose `ppg` is greater than the value of the `min` variable: @@ -518,7 +518,7 @@ ERROR: jsonpath member accessor can only be applied to an object SQLSTATE: 2203A ~~~ -However, setting `silent` to `true` in a [JSONPath function](#jsonpath-function) suppresses any errors: +However, setting `silent` to `true` in a [JSONPath function](#jsonpath-functions) suppresses any errors: {% include_cached copy-clipboard.html %} ~~~ sql From 3aefe622b7f9e1411aa1e023e29b1961dcc0fe96 Mon Sep 17 00:00:00 2001 From: Ryan Kuo Date: Thu, 1 May 2025 18:12:19 -0400 Subject: [PATCH 3/8] mention string casting; add to nav --- src/current/_includes/v25.2/sidebar-data/sql.json | 6 ++++++ src/current/v25.2/string.md | 1 + 2 files changed, 7 insertions(+) diff --git a/src/current/_includes/v25.2/sidebar-data/sql.json b/src/current/_includes/v25.2/sidebar-data/sql.json index 148d7a6a3a7..96b249cd993 100644 --- a/src/current/_includes/v25.2/sidebar-data/sql.json +++ b/src/current/_includes/v25.2/sidebar-data/sql.json @@ -885,6 +885,12 @@ "/${VERSION}/common-table-expressions.html" ] }, + { + "title": "JSONPath Queries", + "urls": [ + "/${VERSION}/jsonpath.html" + ] + }, { "title": "Name Resolution", "urls": [ diff --git a/src/current/v25.2/string.md b/src/current/v25.2/string.md index d2e6350922f..7fea14d9eca 100644 --- a/src/current/v25.2/string.md +++ b/src/current/v25.2/string.md @@ -141,6 +141,7 @@ Type | Details `INET` | Requires supported [`INET`]({% link {{ page.version.version }}/inet.md %}) string format, e.g, `'192.168.0.1'`. `INT` | Requires supported [`INT`]({% link {{ page.version.version }}/int.md %}) string format, e.g., `'10'`. `INTERVAL` | Requires supported [`INTERVAL`]({% link {{ page.version.version }}/interval.md %}) string format, e.g., `'1h2m3s4ms5us6ns'`. +`JSONPATH` | Requires a valid [`JSONPath`]({% link {{ page.version.version }}/jsonpath.md %}) expression string, e.g., `'$'` or `'$.players[*] ? (@.stats.ppg > 30)'`. `TIME` | Requires supported [`TIME`]({% link {{ page.version.version }}/time.md %}) string format, e.g., `'01:22:12'` (microsecond precision). `TIMESTAMP` | Requires supported [`TIMESTAMP`]({% link {{ page.version.version }}/timestamp.md %}) string format, e.g., `'2016-01-25 10:10:10.555555'`. `TSQUERY` | Requires supported [`TSQUERY`]({% link {{ page.version.version }}/tsquery.md %}) string format, e.g., `'Requires & supported & TSQUERY & string & format'`.
Note that casting a string to a `TSQUERY` will not normalize the tokens into lexemes. To do so, [use `to_tsquery()`, `plainto_tsquery()`, or `phraseto_tsquery()`](#convert-string-to-tsquery). From ff8f30ff061a4364025b5d9c41844550bd860697 Mon Sep 17 00:00:00 2001 From: Ryan Kuo Date: Thu, 8 May 2025 14:24:05 -0400 Subject: [PATCH 4/8] address reviewer comments --- src/current/v25.2/jsonpath.md | 74 +++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/src/current/v25.2/jsonpath.md b/src/current/v25.2/jsonpath.md index 6a148d1006f..9d4b623b29f 100644 --- a/src/current/v25.2/jsonpath.md +++ b/src/current/v25.2/jsonpath.md @@ -9,40 +9,35 @@ docs_area: reference.sql ## JSONPath expression -A JSONPath expression has the following generic structure: +A JSONPath expression consists of an optional [mode](#structural-error-handling) (`lax` or `strict`), followed by a scalar expression (such as `1 + 2`), a predicate expression (such as `1 != 2` or `exists($)`), or a path-based expression rooted at `$`. A path-based expression is composed of one or more [accessor](#accessor-operators) operators that are optionally interleaved with one or more [filter expressions](#filter-expressions) introduced by `?`. Expressions can optionally include scalar expressions, [predicate operators](#predicate-operators) for conditional logic, and a [method](#methods) applied to the current value. The path is evaluated left to right, and each stage refines or filters the result. -~~~ -[mode] $[.key] [? (filter_exp)].[key].[method] -~~~ +### Variables -- `mode` is an optional mode (`lax` or `strict`) that determines how structural mismatches are tolerated. If not specified, the mode is `lax`. Refer to [Structural error handling](#structural-error-handling). -- `$` is the root of the JSONPath, and must be the first element in a JSONPath expression. For an example, refer to [Access entire document](#access-entire-document). -- `.key` is an optional key accessor that refers to a named field in a `JSONB` object. To access array elements, use `.key[index]` or `.key[*]`. Each successive key accessor navigates one level deeper into the structure. `.*` matches all fields in the current object. -- `? (filter_exp)` is an optional filter expression that evaluates the path according to a [predicate check expression](#check-expressions), only returning the results that match. For syntax details, refer to [Filter expressions](#filter-expressions). -- `method` is an optional [JSONPath method](#jsonpath-methods) that can be used to access or transform the current path value. +Use the following variables in path and [filter expressions](#filter-expressions) to reference parts of the JSON document or external values. -The path is evaluated left to right, and each stage refines or filters the result. +| Variable | Description | Example usage | +|----------|-----------------------------------------------------------------------------|----------------------------------------| +| `$` | Root of the JSON document. | `$.players[0]` | +| `$var` | A named variable defined in the `var` argument. | `@.price > $min` (with `{"min": 100}`) | +| `@` | Current item being evaluated in a [filter expression](#filter-expressions). | `@.team == "Lakers"` | -## JSONPath functions - -Use JSONPath functions to extract or evaluate target `JSONB` data according to a specified [path](#jsonpath-expression). For full details on JSONPath functions, refer to [Functions and Operators]({% link {{ page.version.version }}/functions-and-operators.md %}# jsonpath-functions). +### Operators -| Function | Description | If no match | -|-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| -| `jsonb_path_exists(jsonb, jsonpath)` | Returns true if any match is found. | `false` | -| `jsonb_path_match(jsonb, jsonpath)` | Returns true if the path expression evaluates to true. Only useful with [predicate check expressions](#check-expressions), as it expects a single Boolean value. | `false` | -| `jsonb_path_query(jsonb, jsonpath)` | Returns all matches as a set of `JSONB` values. | `NULL` | -| `jsonb_path_query_array(jsonb, jsonpath)` | Returns all matches as a single `JSONB` array. | `[]` | -| `jsonb_path_query_first(jsonb, jsonpath)` | Returns the first match only. | `NULL` | +#### Accessor operators -Each function accepts two required and two optional arguments as follows: +Use the following operators in path expressions to navigate JSON objects and arrays. -- `target` (required): A [`JSONB`]({% link {{ page.version.version }}/jsonb.md %}) value to access. -- `path` (required): A [JSONPath expression](#jsonpath-expression). -- `vars`: An optional value referenced as a variable in the path. -- `silent`: An optional Boolean that specifies whether to throw errors during execution. If not specified, this is `false`. +| Operator | Description | Example usage | +|----------------|----------------------------------------------------------|------------------------------------------------| +| `.key` | Access a named field `key` in a JSON object. | `$.players` | +| `.*` | Access all fields in the current object. | `$.stats.*` | +| `.key[a]` | Access a specific `a` element in an array field. | `$.players[0]` | +| `.key[a to b]` | Access an index range from `a` to `b` in an array field. | `$.players[0 to 3]` | +| `.key[last]` | Access the last index element in an array field. | `$.players[last]` | +| `.key[*]` | Access all elements in an array field. | `$.players[*]` | +| `.$var` | Access the field named by a variable `var`. | `$.players[0].$field` (with `$field = "name"`) | -## JSONPath operators +#### Predicate operators Use the following operators in [predicate check expressions](#check-expressions) to compare values, evaluate conditions, and combine logical clauses. @@ -61,9 +56,9 @@ Use the following operators in [predicate check expressions](#check-expressions) | `&&` | Logical AND | `@.ppg > 20 && @.team == "Lakers"` | | `||` | Logical OR | `@.ppg > 20 || @.team == "Lakers"` | -## JSONPath methods +### Methods -Use the following methods to access or transform data in the path. For examples, refer to [Access using methods](#access-using-methods). +Append the following methods to a path (after `.`) to access or transform the value. For examples, refer to [Access using methods](#access-using-methods). | Method | Description | Example usage | |-------------|-------------------------------------------------------------------------|------------------------------------| @@ -73,6 +68,25 @@ Use the following methods to access or transform data in the path. For examples, | `floor()` | Returns the nearest integer less than or equal to the current value. | `$.players[1].stats.ppg.floor()` | | `ceiling()` | Returns the nearest integer greater than or equal to the current value. | `$.players[1].stats.ppg.ceiling()` | +## JSONPath functions + +Use JSONPath functions to extract or evaluate target `JSONB` data according to a specified [path](#jsonpath-expression). For full details on JSONPath functions, refer to [Functions and Operators]({% link {{ page.version.version }}/functions-and-operators.md %}# jsonpath-functions). + +| Function | Description | If no match | +|-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| +| `jsonb_path_exists(jsonb, jsonpath)` | Returns true if any match is found. | `false` | +| `jsonb_path_match(jsonb, jsonpath)` | Returns true if the path expression evaluates to true. Only useful with [predicate check expressions](#check-expressions), as it expects a single Boolean value. | `false` | +| `jsonb_path_query(jsonb, jsonpath)` | Returns all matches as a set of `JSONB` values. | `NULL` | +| `jsonb_path_query_array(jsonb, jsonpath)` | Returns all matches as a single `JSONB` array. | `[]` | +| `jsonb_path_query_first(jsonb, jsonpath)` | Returns the first match only. | `NULL` | + +Each function accepts two required and two optional arguments as follows: + +- `target` (required): A [`JSONB`]({% link {{ page.version.version }}/jsonb.md %}) value to access. +- `path` (required): A [JSONPath expression](#jsonpath-expression). +- `vars`: An optional value referenced as a variable in the path. +- `silent`: An optional Boolean that specifies whether to throw errors during execution. If not specified, this is `false`. + ## Example setup To follow the examples on this page, create and populate the following table: @@ -246,7 +260,7 @@ SELECT jsonb_path_query(data, '$.players[1 to 2, 0]') FROM stats; ### Access using methods -You can use [JSONPath methods](#jsonpath-methods) to access or transform data in the path. +You can use [JSONPath methods](#methods) to access or transform data in the path. The following query returns the type of each value in the `players` array, using the `type()` method: @@ -295,7 +309,7 @@ Returns the floor of the PPG value. ## Check expressions -A JSONPath expression can be a *predicate check expression* that returns a Boolean value. Use one or more [JSONPath operators](#jsonpath-operators) to specify conditions such as equality, logical expressions, and existence. +A JSONPath expression can be a *predicate check expression* that returns a Boolean value. Use one or more [predicate operators](#predicate-operators) to specify conditions such as equality, logical expressions, and existence. Each of the following check expressions evaluates to true: From daf6351da22fb93c15f099a94e55a6b0177c7ed4 Mon Sep 17 00:00:00 2001 From: Ryan Kuo Date: Thu, 8 May 2025 22:14:00 -0400 Subject: [PATCH 5/8] fix link --- src/current/v25.2/jsonpath.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/current/v25.2/jsonpath.md b/src/current/v25.2/jsonpath.md index 9d4b623b29f..63ab1324631 100644 --- a/src/current/v25.2/jsonpath.md +++ b/src/current/v25.2/jsonpath.md @@ -70,7 +70,7 @@ Append the following methods to a path (after `.`) to access or transform the va ## JSONPath functions -Use JSONPath functions to extract or evaluate target `JSONB` data according to a specified [path](#jsonpath-expression). For full details on JSONPath functions, refer to [Functions and Operators]({% link {{ page.version.version }}/functions-and-operators.md %}# jsonpath-functions). +Use JSONPath functions to extract or evaluate target `JSONB` data according to a specified [path](#jsonpath-expression). For full details on JSONPath functions, refer to [Functions and Operators]({% link {{ page.version.version }}/functions-and-operators.md %}#jsonpath-functions). | Function | Description | If no match | |-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| From ea042c1cb3cbcb64d32e96a7a1c50e14131d7270 Mon Sep 17 00:00:00 2001 From: Ryan Kuo Date: Fri, 9 May 2025 01:38:38 -0400 Subject: [PATCH 6/8] fix table --- src/current/v25.2/jsonpath.md | 85 +++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 14 deletions(-) diff --git a/src/current/v25.2/jsonpath.md b/src/current/v25.2/jsonpath.md index 63ab1324631..a6a1eb7e80f 100644 --- a/src/current/v25.2/jsonpath.md +++ b/src/current/v25.2/jsonpath.md @@ -41,20 +41,77 @@ Use the following operators in path expressions to navigate JSON objects and arr Use the following operators in [predicate check expressions](#check-expressions) to compare values, evaluate conditions, and combine logical clauses. -| Operator | Description | Example usage | -|---------------|----------------------------------------|------------------------------------| -| `==` | Equality | `@.team == "Lakers"` | -| `!=` | Inequality | `@.name != "Luka"` | -| `>` | Greater than | `@.stats.ppg > 25` | -| `<` | Less than | `@.stats.ppg < 30` | -| `>=` | Greater than or equal to | `@.stats.rpg >= 10` | -| `<=` | Less than or equal to | `@.stats.apg <= 4` | -| `starts with` | String prefix match | `@.name starts with "A"` | -| `like_regex` | Regex string match | `@.name like_regex "^L.*"` | -| `is unknown` | True if expression evaluates to `null` | `(@.age > 25) is unknown` | -| `!` | Logical NOT | `!(@.team == "Mavericks")` | -| `&&` | Logical AND | `@.ppg > 20 && @.team == "Lakers"` | -| `||` | Logical OR | `@.ppg > 20 || @.team == "Lakers"` | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperatorDescriptionExample usage
==Equality@.team == "Lakers"
!=Inequality@.name != "Luka"
>Greater than@.stats.ppg > 25
<Less than@.stats.ppg < 30
>=Greater than or equal to@.stats.rpg >= 10
<=Less than or equal to@.stats.apg <= 4
starts withString prefix match@.name starts with "A"
like_regexRegex string match@.name like_regex "^L.*"
is unknownTrue if expression evaluates to null(@.age > 25) is unknown
!Logical NOT!(@.team == "Mavericks")
&&Logical AND@.ppg > 20 && @.team == "Lakers"
||Logical OR@.ppg > 20 || @.team == "Lakers"
### Methods From b49090aee4394172b35f51d79174b25917e3c156 Mon Sep 17 00:00:00 2001 From: Ryan Kuo Date: Fri, 9 May 2025 12:24:38 -0400 Subject: [PATCH 7/8] JSONPath known limitations --- .../v25.2/known-limitations/jsonpath-limitations.md | 2 ++ src/current/v25.2/jsonpath.md | 4 ++++ src/current/v25.2/known-limitations.md | 8 ++++---- 3 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 src/current/_includes/v25.2/known-limitations/jsonpath-limitations.md diff --git a/src/current/_includes/v25.2/known-limitations/jsonpath-limitations.md b/src/current/_includes/v25.2/known-limitations/jsonpath-limitations.md new file mode 100644 index 00000000000..9b51bfb6e87 --- /dev/null +++ b/src/current/_includes/v25.2/known-limitations/jsonpath-limitations.md @@ -0,0 +1,2 @@ +- The following keywords are only accepted in lowercase: `strict`, `lax`, `exists`, `like_regex`, `flag`, `is unknown`, `to`, `last`. [#144255](https://github.com/cockroachdb/cockroach/issues/144255) +- Comparisons involving empty arrays (e.g., `SELECT jsonb_path_query('{"a": [1], "b": []}', '$.a == $.b');`) return `null`, rather than `false` as in PostgreSQL. [#145099](https://github.com/cockroachdb/cockroach/issues/145099) \ No newline at end of file diff --git a/src/current/v25.2/jsonpath.md b/src/current/v25.2/jsonpath.md index a6a1eb7e80f..f8193e9821d 100644 --- a/src/current/v25.2/jsonpath.md +++ b/src/current/v25.2/jsonpath.md @@ -631,6 +631,10 @@ SELECT jsonb_path_query('{}', '(1 + 2) * 3'); 9 ~~~ +## Known limitations + +{% include {{ page.version.version }}/known-limitations/jsonpath-limitations.md %} + ## See also - [JSONB]({% link {{ page.version.version }}/jsonb.md %}) diff --git a/src/current/v25.2/known-limitations.md b/src/current/v25.2/known-limitations.md index 4f76260d1aa..cef7ce7cb60 100644 --- a/src/current/v25.2/known-limitations.md +++ b/src/current/v25.2/known-limitations.md @@ -1,5 +1,5 @@ --- -title: Known Limitations in CockroachDB v25.1 +title: Known Limitations in CockroachDB v25.2 summary: Learn about newly identified limitations in CockroachDB as well as unresolved limitations identified in earlier releases. toc: true keywords: limitations, known limitations, unsupported features, PostgreSQL compatibility @@ -12,9 +12,9 @@ docs_area: releases This section describes newly identified limitations in CockroachDB {{ page.version.version }}. -{{site.data.alerts.callout_info}} -Limitations will be added as they are discovered. -{{site.data.alerts.end}} +### JSONPath + +{% include {{ page.version.version }}/known-limitations/jsonpath-limitations.md %} ## Limitations from {{ previous_version }} and earlier From 378e3684b42c98425dbc476a683d8df4b60c7f82 Mon Sep 17 00:00:00 2001 From: Ryan Kuo Date: Fri, 9 May 2025 17:20:51 -0400 Subject: [PATCH 8/8] address reviewer feedback --- src/current/v25.2/jsonpath.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/current/v25.2/jsonpath.md b/src/current/v25.2/jsonpath.md index f8193e9821d..c223d452a45 100644 --- a/src/current/v25.2/jsonpath.md +++ b/src/current/v25.2/jsonpath.md @@ -18,8 +18,8 @@ Use the following variables in path and [filter expressions](#filter-expressions | Variable | Description | Example usage | |----------|-----------------------------------------------------------------------------|----------------------------------------| | `$` | Root of the JSON document. | `$.players[0]` | -| `$var` | A named variable defined in the `var` argument. | `@.price > $min` (with `{"min": 100}`) | | `@` | Current item being evaluated in a [filter expression](#filter-expressions). | `@.team == "Lakers"` | +| `$var` | A named variable defined in the `var` argument. | `@.price > $min` (with `{"min": 100}`) | ### Operators @@ -121,7 +121,7 @@ Append the following methods to a path (after `.`) to access or transform the va |-------------|-------------------------------------------------------------------------|------------------------------------| | `size()` | Returns the size of an array, or `1` for a scalar. | `$.players.size()` | | `type()` | Returns the type of the current value as a string. | `$.players[0].stats.type()` | -| `abs()` | Returns the absolute value of a number. | `$.players[0].stats.bpg.abs()` | +| `abs()` | Returns the absolute value of a number. | `$.players[0].stats.ppg.abs()` | | `floor()` | Returns the nearest integer less than or equal to the current value. | `$.players[1].stats.ppg.floor()` | | `ceiling()` | Returns the nearest integer greater than or equal to the current value. | `$.players[1].stats.ppg.ceiling()` | @@ -141,8 +141,8 @@ Each function accepts two required and two optional arguments as follows: - `target` (required): A [`JSONB`]({% link {{ page.version.version }}/jsonb.md %}) value to access. - `path` (required): A [JSONPath expression](#jsonpath-expression). -- `vars`: An optional value referenced as a variable in the path. -- `silent`: An optional Boolean that specifies whether to throw errors during execution. If not specified, this is `false`. +- `vars` (optional): A [`JSONB`]({% link {{ page.version.version }}/jsonb.md %}) value referenced as a variable in the path. +- `silent` (optional): A Boolean value that specifies whether to throw errors during execution. If not specified, this is `false`. ## Example setup @@ -391,20 +391,20 @@ SELECT jsonb_path_query(data, '(($.players[2].team != "Lakers") && ($.players[1] ~~~ ~~~ - jsonb_path_match + jsonb_path_query -------------------- - t + true ~~~ {{site.data.alerts.callout_info}} -The preceding check expressions can be used with the `jsonb_path_match` [function](#jsonpath-functions) for an identical result. +The preceding check expressions can be used with the `jsonb_path_match` [function](#jsonpath-functions) for a similar result. {{site.data.alerts.end}} ## Filter expressions A *filter expression* uses a [predicate check expression](#check-expressions) to return `JSONB` fields that match a condition. -To write a filter expression, insert `?` into a JSONPath expression, followed by a check expression that uses `@` to reference each item selected by the preceding path step. The filter expression only returns items that evaluate to true. The path can optionally continue after the filter. +To write a filter expression, insert `?` into a JSONPath expression, followed by a check expression that uses `@` to reference each item selected by the preceding path step. The filter expression only returns items that evaluate to true. For example, the following JSONPath expression selects all elements in an `items` array (`$.items[*]`), filters all items whose `price` value is greater than 100 (`(@.price > 100)`), and returns the `name` value for each filtered item (`.name`): @@ -490,7 +490,7 @@ SELECT jsonb_path_query(data, '$.players[*] ? (@.team like_regex "^L.*")') FROM ## Variables in JSONPath expressions -Define a variable in a JSONPath expression by prefixing it with `$`. Then specify a value as an argument in the [JSONPath function](#jsonpath-functions). +Define a variable in a JSONPath expression by prefixing it with `$`. Then specify a value in the `vars` argument of the [JSONPath function](#jsonpath-functions). The following query filters players whose `ppg` is greater than the value of the `min` variable: