JSONPath
The JSONPath
type implements support for the SQL/JSON path language in PostgreSQL to efficiently query JSON data stored in the JSONB
format. JSONPath
is a binary representation of the parsed SQL/JSON path expression that specifies the items to be retrieved for further operations.
JSONPath
uses the following JavaScript convention syntax:
-
The dot (
.
) is used for member access. -
Square brackets (
[]
) are used for array access. -
SQL/JSON arrays are 0-relative, unlike regular SQL arrays that start from 1.
Enclose an SQL/JSON path expression in single quotes to include it in an SQL query. To use a single-quote character in a value, double it. For example: 'Dianne''s horse'
.
Some forms of path expressions require string literals. These embedded string literals must be surrounded by double quotes. These string literals can contain backslash escapes. To write a double quote, use \"
.
To add a backslash, type \\
. Other special backslash sequences that can be used in JSON strings: \b
, \f
, \n
, \r
, \t
, \v
for ASCII control characters; \xNN
, \uNNNN
, and \u{N…}
for a Unicode character identified by two hex digits, by four hex digits, and by from one to six hex digits respectively.
A path expression can consist of the following path elements:
-
Path variables:
-
$
is the current context of the element. The expression with$
specifies the area to process and is used in a filter. Example:$.items
. -
@
is the current context in the filter expression (the result of path evaluation in filter expressions). Example:$.items[*] ? (@.qty > 2)
. -
$varname
is a named variable. Its value can be set by the parameter vars. Example:'$[*] ? (@ > $x)',vars ⇒ '{"x": 2}'
.
-
-
A filter expression that starts with a question mark (
?
) and contains a condition in parentheses:? (condition)
It operates similar to the
WHERE
clause. -
JSONPath
operators and methods listed in the following section: SQL/JSON Path Operators And Methods. -
Parentheses that are used to specify filter expressions or define the order of path evaluation.
-
Accessor operators that are listed in the table below.
Accessor operator | Description | Example |
---|---|---|
.key ."$varname" |
A member accessor that returns an object member with the specified key. If the key name matches a named variable starting with |
$.items |
.* |
A wildcard member accessor that returns the values of all members located at the top level of the current object |
$.items.* |
.** |
A recursive wildcard member accessor that processes all levels of the JSON hierarchy of the current object and returns all the member values, regardless of their nesting level |
$.** |
.**{level} .**{start_level to end_level} |
This member accessor selects only the specified levels of the JSON hierarchy. A nesting level is an integer value. The |
$.**{2} $.**{1 to 3} |
[index] [start_index to end_index] |
An array element accessor that can be used in two forms: The specified index can be an integer or an expression returning a single numeric value, which is automatically cast to integer. The |
$.a[1] $.a[3 to 5] |
[*] |
A wildcard array element accessor that returns all array elements |
$.items[*] |
You can use JSONPath
syntax to create simpler queries than the SQL syntax allows.
JSONPath functions
The functions described below manipulate JSONB
data. All these functions have vars
and silent
optional arguments. vars
allows you to pass variables into a JSONPath expression. silent
determines whether error handling is suppressed. These arguments are described below.
Create the book_orders
and demand
tables for test examples:
CREATE TABLE book_orders (
id serial NOT NULL PRIMARY KEY,
content jsonb NOT NULL
);
INSERT INTO book_orders (content)
VALUES('{ "supplier": "Jacob Johnson", "items": {"book": "Hyperion","qty": 3, "price": 21}}'),
('{ "supplier": "Adam Brown", "items": {"book": "War and Peace","qty": 2, "price": 26}}'),
('{ "supplier": "Andrew Nelson", "items": {"book": "1984","qty": 4, "price": 31}}');
CREATE TABLE demand(name text, max_price int);
INSERT INTO demand VALUES ('Jane', 25), ('Alex', 27 ), ('Max', 35);
jsonb_path_exists
jsonb_path_exists ( <target> jsonb, <path> jsonpath [, vars jsonb [, silent boolean ]] ) → boolean
The function checks whether path
returns any item for the target
JSON value.
Examples:
SELECT jsonb_path_exists('{"a": 1}', '$.a');
-- returns true
SELECT jsonb_path_exists('{"a": 1}', '$.b');
-- returns false
SELECT jsonb_path_exists('{"a":[1,2,3,4,5]}', '$.a[*] ? (@ >= $min && @ <= $max)', '{"min":2, "max":4}')
-- returns true
The following query determines if the JSON data stored in the content
column of the book_orders
table contains strings that have the Hyperion
value:
SELECT jsonb_path_exists(content, '$.** ? (@ == "Hyperion")')
FROM book_orders ORDER by id;
$.**
iterates through the JSON value hierarchy of the content
field and returns all element values, regardless of their nesting level. @
refers to the current element value in the filter expression.
The result:
jsonb_path_exists ------------------- true false false
jsonb_path_match
jsonb_path_match ( <target> jsonb, <path> jsonpath [, vars jsonb [, silent boolean ]] ) → boolean
The function returns the result of the path
predicate check for the target
JSON value. The function takes into account only the first item of the result. If the result is not boolean
, jsonb_path_match
returns null
.
Examples:
SELECT jsonb_path_match('{"a": 1}', '$.a == 1');
-- returns true
SELECT jsonb_path_match('{"a": 1}', '$.a >= 2');
-- returns false
SELECT jsonb_path_match('{"a":[1,2,3,4,5]}', 'exists($.a[*] ? (@ >= $min && @ <= $max))', '{"min":2, "max":4}');
-- returns true
The following query also determines if the content
column of the book_orders
table contains strings that have the Hyperion
value:
SELECT jsonb_path_match(content, 'exists($.** ? (@ == "Hyperion"))')
FROM book_orders ORDER by id;
The result:
jsonb_path_match ------------------- true false false
jsonb_path_query
jsonb_path_query ( <target> jsonb, <path> jsonpath [, vars jsonb [, silent boolean ]] ) → setof jsonb
The function returns all JSON items that path
extracts from the target
JSON value.
Examples:
SELECT jsonb_path_query('{"a": [1,2,3,4,5]}', '$.a[*] ? (@ > 2)');
-- returns 3, 4, 5
SELECT jsonb_path_query('{"a": [1,2,3,4,5]}', '$.a[*] ? (@ > 5)');
-- returns 0 rows
The following query returns items
where the qty
field more than 2:
SELECT jsonb_path_query(content, '$.items[*] ? (@.qty > 2)')
FROM book_orders;
The result:
jsonb_path_query --------------------------------------------- {"qty": 3, "book": "Hyperion", "price": 21} {"qty": 4, "book": "1984", "price": 31}
jsonb_path_query_array
jsonb_path_query_array ( <target> jsonb, <path> jsonpath [, vars jsonb [, silent boolean ]] ) → jsonb
The function returns all JSON items that path
extracts from the target
JSON value as a JSON array.
Examples:
SELECT jsonb_path_query_array('{"a": [1,2,3,4,5]}', '$.a[*] ? (@ > 2)');
-- returns [3, 4, 5]
SELECT jsonb_path_query_array('{"a": [1,2,3,4,5]}', '$.a[*] ? (@ > 5)');
-- returns []
jsonb_path_query_first
jsonb_path_query_first ( <target> jsonb, <path> jsonpath [, vars jsonb [, silent boolean ]] ) → jsonb
The function returns the first JSON item that path
extracts from the target
JSON value. jsonb_path_query_first
returns null
if there are no results.
Examples:
SELECT jsonb_path_query_first('{"a": [1,2,3,4,5]}', '$.a[*] ? (@ > 2)');
-- returns 3
SELECT jsonb_path_query_first('{"a": [1,2,3,4,5]}', '$.a[*] ? (@ > 5)');
-- returns null
JSONPath functions with the _tz suffix
PostgreSQL provides the following functions with the _tz
suffix:
jsonb_path_exists_tz
, jsonb_path_match_tz
,jsonb_path_query_tz
, jsonb_path_query_array_tz
, and jsonb_path_query_first_tz
. These functions operate as their counterparts described above without the _tz
suffix, but support comparisons of date/time values that require timezone-aware conversions.
The example below requires interpretation of the date-only value 2022-08-02
as a timestamp with time zone. The result depends on the current TimeZone setting. Because of this dependency, PostgreSQL marks these functions as stable, which means that you cannot use these functions in indexes.
Examples:
SELECT jsonb_path_exists_tz('["2022-08-01 12:00:00 -05"]', '$[*] ? (@.datetime() < "2022-08-02".datetime())');
-- returns false
SELECT jsonb_path_exists_tz('["2022-08-01 12:00:00"]', '$[*] ? (@.datetime() < "2022-08-02".datetime())');
-- returns true
The vars argument
The vars
argument allows you to pass variables into a JSONPath
expression. It must be a JSON object, and its fields must contain named values to be substituted into the JSONPath
expression.
Execute the following query with the vars
argument to find books with a price lower than max_price
specified for Alex
in the demand
table:
SELECT jsonb_path_query(content, '$.items[*]? (@.price < $max_price)',
vars => jsonb_build_object('max_price', demand.max_price))
FROM book_orders, demand WHERE name = 'Alex';
The vars
argument passes the result of the query that determines max_price
specified for Alex
to the jsonb_path_query
function.
The result:
jsonb_path_query -------------------------------------------------- {"qty": 3, "book": "Hyperion", "price": 21} {"qty": 2, "book": "War and Peace", "price": 26}
The silent argument
silent
suppresses error handling. Execute the following query, in which the strict modifier changes the error handling mode:
SELECT jsonb_path_query('[]', 'strict $.a');
An error occurs:
ERROR: jsonpath member accessor can only be applied to an object
Set silent
to true
:
SELECT jsonb_path_query('[]', 'strict $.a', silent => true);
The error is not displayed. The result:
jsonb_path_query ------------------ (0 rows)
JSONPath operators
The match operator @@
The @@
operator returns the result of the JSONPath
predicate check for the specified JSON value. PostgreSQL takes into account only the first item of the result. If the result is not boolean
, @@
returns null
.
The following query determines if the content
column of the book_orders
table contains strings that have the Hyperion
value:
SELECT content @@ '$.** == "Hyperion"' AS hyperion FROM book_orders;
The result:
hyperion ---------- true null null
The existence check operator @?
The @?
operator determines whether a JSONPath
expression returns any item for the specified JSON value.
The following query uses @?
to select books with the price
value more than 25.
SELECT jsonb_path_query(content, '$.items.book')
FROM book_orders WHERE content @? '$.items[*] ? (@.price > 25)';
The result:
jsonb_path_query ------------------ "War and Peace" "1984"
The strict and lax modes
When you query JSON data, the JSONPath
expression may not match the actual JSON data structure. An attempt to access a non-existent member or element is a structural error. JSONPath
expressions have two modes of handling structural errors:
-
lax
(default) — the path engine implicitly adapts the queried data to the specified path. Any remaining structural errors are suppressed and converted to empty SQL/JSON sequences. -
strict
— if a structural error occurs, an error is raised.
Use the lax
and strict
modifiers to switch modes.
To see how these modes operate, search the b
key in a JSON value that does not contain this key. In the lax
mode, the result is false
:
SELECT jsonb '{"a":1}' @? 'lax $.b ? (@ > 1)';
The result:
?column? ---------- false
In the strict
mode, it returns null
:
SELECT jsonb '{"a":1}' @? 'strict $.b ? (@ > 1)';
The result:
?column? ---------- null
In the lax
mode, an array with a complex hierarchy [1,3,[5,7,9]] is processed as [1,3,5,7,9]:
SELECT jsonb '[1,3,[5,7,9]]' @? 'lax $[*] ? (@ == 9)';
The result is true
.
In the strict
mode, the query returns false
:
SELECT jsonb '[1,3,[5,7,9]]' @? 'strict $[*] ? (@ == 9)';
Replace @
with @[*]
to get the true
result in the strict
mode:
SELECT jsonb '[1,3,[5,7,9]]' @? 'strict $[*] ? (@[*] == 9)';
For more information on modes, see Strict and lax modes.