From 66e0ea75bb9d4b40c734067dd7c7e1fc804a88cb Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter@eisentraut.org> Date: Thu, 26 Sep 2019 09:42:19 +0200 Subject: [PATCH] doc: Reorder JSON functions documentation Put the description of the SQL/JSON path language after the description of the general JSON functions and operators, instead of before. Discussion: https://www.postgresql.org/message-id/16968.1569189812@sss.pgh.pa.us --- doc/src/sgml/func.sgml | 1649 ++++++++++++++++++++-------------------- 1 file changed, 823 insertions(+), 826 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 432dbad8684..390cd48c67b 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -11636,884 +11636,247 @@ table2-mapping </sect1> <sect1 id="functions-json"> - <title>JSON Functions, Operators, and Expressions</title> - - <para> - The functions, operators, and expressions described in this section - operate on JSON data: - </para> - - <itemizedlist> - <listitem> - <para> - SQL/JSON path expressions - (see <xref linkend="functions-sqljson-path"/>). - </para> - </listitem> - <listitem> - <para> - PostgreSQL-specific functions and operators for JSON - data types (see <xref linkend="functions-pgjson"/>). - </para> - </listitem> - </itemizedlist> - - <para> - To learn more about the SQL/JSON standard, see - <xref linkend="sqltr-19075-6"/>. For details on JSON types - supported in <productname>PostgreSQL</productname>, - see <xref linkend="datatype-json"/>. - </para> + <title>JSON Functions and Operators</title> - <sect2 id="functions-sqljson-path"> - <title>SQL/JSON Path Expressions</title> <indexterm zone="functions-json"> - <primary>SQL/JSON</primary> - <secondary>path expressions</secondary> + <primary>JSON</primary> + <secondary>functions and operators</secondary> </indexterm> <para> - SQL/JSON path expressions specify the items to be retrieved - from the JSON data, similar to XPath expressions used - for SQL access to XML. In <productname>PostgreSQL</productname>, - path expressions are implemented as the <type>jsonpath</type> - data type and can use any elements described in - <xref linkend="datatype-jsonpath"/>. - </para> - - <para>JSON query functions and operators - pass the provided path expression to the <firstterm>path engine</firstterm> - for evaluation. If the expression matches the queried JSON data, - the corresponding SQL/JSON item is returned. - Path expressions are written in the SQL/JSON path language - and can also include arithmetic expressions and functions. - Query functions treat the provided expression as a - text string, so it must be enclosed in single quotes. - </para> - - <para> - A path expression consists of a sequence of elements allowed - by the <type>jsonpath</type> data type. - The path expression is evaluated from left to right, but - you can use parentheses to change the order of operations. - If the evaluation is successful, a sequence of SQL/JSON items - (<firstterm>SQL/JSON sequence</firstterm>) is produced, - and the evaluation result is returned to the JSON query function - that completes the specified computation. - </para> - - <para> - To refer to the JSON data to be queried (the - <firstterm>context item</firstterm>), use the <literal>$</literal> sign - in the path expression. It can be followed by one or more - <link linkend="type-jsonpath-accessors">accessor operators</link>, - which go down the JSON structure level by level to retrieve the - content of context item. Each operator that follows deals with the - result of the previous evaluation step. - </para> - - <para> - For example, suppose you have some JSON data from a GPS tracker that you - would like to parse, such as: -<programlisting> -{ "track" : - { - "segments" : [ - { "location": [ 47.763, 13.4034 ], - "start time": "2018-10-14 10:05:14", - "HR": 73 - }, - { "location": [ 47.706, 13.2635 ], - "start time": "2018-10-14 10:39:21", - "HR": 135 - } ] - } -} -</programlisting> - </para> - - <para> - To retrieve the available track segments, you need to use the - <literal>.<replaceable>key</replaceable></literal> accessor - operator for all the preceding JSON objects: -<programlisting> -'$.track.segments' -</programlisting> - </para> + This section describes: - <para> - If the item to retrieve is an element of an array, you have - to unnest this array using the <literal>[*]</literal> operator. For example, - the following path will return location coordinates for all - the available track segments: -<programlisting> -'$.track.segments[*].location' -</programlisting> + <itemizedlist> + <listitem> + <para> + functions and operators for processing and creating JSON data + </para> + </listitem> + <listitem> + <para> + the SQL/JSON path language + </para> + </listitem> + </itemizedlist> </para> <para> - To return the coordinates of the first segment only, you can - specify the corresponding subscript in the <literal>[]</literal> - accessor operator. Note that the SQL/JSON arrays are 0-relative: -<programlisting> -'$.track.segments[0].location' -</programlisting> + To learn more about the SQL/JSON standard, see + <xref linkend="sqltr-19075-6"/>. For details on JSON types + supported in <productname>PostgreSQL</productname>, + see <xref linkend="datatype-json"/>. </para> - <para> - The result of each path evaluation step can be processed - by one or more <type>jsonpath</type> operators and methods - listed in <xref linkend="functions-sqljson-path-operators"/>. - Each method name must be preceded by a dot. For example, - you can get an array size: -<programlisting> -'$.track.segments.size()' -</programlisting> - For more examples of using <type>jsonpath</type> operators - and methods within path expressions, see - <xref linkend="functions-sqljson-path-operators"/>. - </para> + <sect2 id="functions-json-processing"> + <title>Processing and Creating JSON Data</title> <para> - When defining the path, you can also use one or more - <firstterm>filter expressions</firstterm> that work similar to the - <literal>WHERE</literal> clause in SQL. A filter expression begins with - a question mark and provides a condition in parentheses: - - <programlisting> -? (<replaceable>condition</replaceable>) - </programlisting> + <xref linkend="functions-json-op-table"/> shows the operators that + are available for use with JSON data types (see <xref + linkend="datatype-json"/>). </para> - <para> - Filter expressions must be specified right after the path evaluation step - to which they are applied. The result of this step is filtered to include - only those items that satisfy the provided condition. SQL/JSON defines - three-valued logic, so the condition can be <literal>true</literal>, <literal>false</literal>, - or <literal>unknown</literal>. The <literal>unknown</literal> value - plays the same role as SQL <literal>NULL</literal> and can be tested - for with the <literal>is unknown</literal> predicate. Further path - evaluation steps use only those items for which filter expressions - return <literal>true</literal>. - </para> + <table id="functions-json-op-table"> + <title><type>json</type> and <type>jsonb</type> Operators</title> + <tgroup cols="6"> + <thead> + <row> + <entry>Operator</entry> + <entry>Right Operand Type</entry> + <entry>Return type</entry> + <entry>Description</entry> + <entry>Example</entry> + <entry>Example Result</entry> + </row> + </thead> + <tbody> + <row> + <entry><literal>-></literal></entry> + <entry><type>int</type></entry> + <entry><type>json</type> or <type>jsonb</type></entry> + <entry>Get JSON array element (indexed from zero, negative + integers count from the end)</entry> + <entry><literal>'[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json->2</literal></entry> + <entry><literal>{"c":"baz"}</literal></entry> + </row> + <row> + <entry><literal>-></literal></entry> + <entry><type>text</type></entry> + <entry><type>json</type> or <type>jsonb</type></entry> + <entry>Get JSON object field by key</entry> + <entry><literal>'{"a": {"b":"foo"}}'::json->'a'</literal></entry> + <entry><literal>{"b":"foo"}</literal></entry> + </row> + <row> + <entry><literal>->></literal></entry> + <entry><type>int</type></entry> + <entry><type>text</type></entry> + <entry>Get JSON array element as <type>text</type></entry> + <entry><literal>'[1,2,3]'::json->>2</literal></entry> + <entry><literal>3</literal></entry> + </row> + <row> + <entry><literal>->></literal></entry> + <entry><type>text</type></entry> + <entry><type>text</type></entry> + <entry>Get JSON object field as <type>text</type></entry> + <entry><literal>'{"a":1,"b":2}'::json->>'b'</literal></entry> + <entry><literal>2</literal></entry> + </row> + <row> + <entry><literal>#></literal></entry> + <entry><type>text[]</type></entry> + <entry><type>json</type> or <type>jsonb</type></entry> + <entry>Get JSON object at the specified path</entry> + <entry><literal>'{"a": {"b":{"c": "foo"}}}'::json#>'{a,b}'</literal></entry> + <entry><literal>{"c": "foo"}</literal></entry> + </row> + <row> + <entry><literal>#>></literal></entry> + <entry><type>text[]</type></entry> + <entry><type>text</type></entry> + <entry>Get JSON object at the specified path as <type>text</type></entry> + <entry><literal>'{"a":[1,2,3],"b":[4,5,6]}'::json#>>'{a,2}'</literal></entry> + <entry><literal>3</literal></entry> + </row> + </tbody> + </tgroup> + </table> + <note> + <para> + There are parallel variants of these operators for both the + <type>json</type> and <type>jsonb</type> types. + The field/element/path extraction operators + return the same type as their left-hand input (either <type>json</type> + or <type>jsonb</type>), except for those specified as + returning <type>text</type>, which coerce the value to text. + The field/element/path extraction operators return NULL, rather than + failing, if the JSON input does not have the right structure to match + the request; for example if no such element exists. The + field/element/path extraction operators that accept integer JSON + array subscripts all support negative subscripting from the end of + arrays. + </para> + </note> <para> - Functions and operators that can be used in filter expressions are listed - in <xref linkend="functions-sqljson-filter-ex-table"/>. The path - evaluation result to be filtered is denoted by the <literal>@</literal> - variable. To refer to a JSON element stored at a lower nesting level, - add one or more accessor operators after <literal>@</literal>. + The standard comparison operators shown in <xref + linkend="functions-comparison-op-table"/> are available for + <type>jsonb</type>, but not for <type>json</type>. They follow the + ordering rules for B-tree operations outlined at <xref + linkend="json-indexing"/>. </para> - <para> - Suppose you would like to retrieve all heart rate values higher - than 130. You can achieve this using the following expression: -<programlisting> -'$.track.segments[*].HR ? (@ > 130)' -</programlisting> + Some further operators also exist only for <type>jsonb</type>, as shown + in <xref linkend="functions-jsonb-op-table"/>. + Many of these operators can be indexed by + <type>jsonb</type> operator classes. For a full description of + <type>jsonb</type> containment and existence semantics, see <xref + linkend="json-containment"/>. <xref linkend="json-indexing"/> + describes how these operators can be used to effectively index + <type>jsonb</type> data. </para> - - <para> - To get the start time of segments with such values instead, you have to - filter out irrelevant segments before returning the start time, so the - filter expression is applied to the previous step, and the path used - in the condition is different: -<programlisting> -'$.track.segments[*] ? (@.HR > 130)."start time"' -</programlisting> - </para> - - <para> - You can use several filter expressions on the same nesting level, if - required. For example, the following expression selects all segments - that contain locations with relevant coordinates and high heart rate values: -<programlisting> -'$.track.segments[*] ? (@.location[1] < 13.4) ? (@.HR > 130)."start time"' -</programlisting> - </para> - - <para> - Using filter expressions at different nesting levels is also allowed. - The following example first filters all segments by location, and then - returns high heart rate values for these segments, if available: -<programlisting> -'$.track.segments[*] ? (@.location[1] < 13.4).HR ? (@ > 130)' -</programlisting> - </para> - - <para> - You can also nest filter expressions within each other: -<programlisting> -'$.track ? (exists(@.segments[*] ? (@.HR > 130))).segments.size()' -</programlisting> - This expression returns the size of the track if it contains any - segments with high heart rate values, or an empty sequence otherwise. - </para> - - <para> - <productname>PostgreSQL</productname>'s implementation of SQL/JSON path - language has the following deviations from the SQL/JSON standard: - </para> - - <itemizedlist> - <listitem> - <para> - <literal>.datetime()</literal> item method is not implemented yet - mainly because immutable <type>jsonpath</type> functions and operators - cannot reference session timezone, which is used in some datetime - operations. Datetime support will be added to <type>jsonpath</type> - in future versions of <productname>PostgreSQL</productname>. - </para> - </listitem> - - <listitem> - <para> - A path expression can be a Boolean predicate, although the SQL/JSON - standard allows predicates only in filters. This is necessary for - implementation of the <literal>@@</literal> operator. For example, - the following <type>jsonpath</type> expression is valid in - <productname>PostgreSQL</productname>: -<programlisting> -'$.track.segments[*].HR < 70' -</programlisting> - </para> - </listitem> - - <listitem> - <para> - There are minor differences in the interpretation of regular - expression patterns used in <literal>like_regex</literal> filters, as - described in <xref linkend="jsonpath-regular-expressions"/>. - </para> - </listitem> - </itemizedlist> - - <sect3 id="strict-and-lax-modes"> - <title>Strict and Lax Modes</title> - <para> - When you query JSON data, the path expression may not match the - actual JSON data structure. An attempt to access a non-existent - member of an object or element of an array results in a - structural error. SQL/JSON path expressions have two modes - of handling structural errors: - </para> - - <itemizedlist> - <listitem> - <para> - 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. - </para> - </listitem> - <listitem> - <para> - strict — if a structural error occurs, an error is raised. - </para> - </listitem> - </itemizedlist> - - <para> - The lax mode facilitates matching of a JSON document structure and path - expression if the JSON data does not conform to the expected schema. - If an operand does not match the requirements of a particular operation, - it can be automatically wrapped as an SQL/JSON array or unwrapped by - converting its elements into an SQL/JSON sequence before performing - this operation. Besides, comparison operators automatically unwrap their - operands in the lax mode, so you can compare SQL/JSON arrays - out-of-the-box. An array of size 1 is considered equal to its sole element. - Automatic unwrapping is not performed only when: - <itemizedlist> - <listitem> - <para> - The path expression contains <literal>type()</literal> or - <literal>size()</literal> methods that return the type - and the number of elements in the array, respectively. - </para> - </listitem> - <listitem> - <para> - The queried JSON data contain nested arrays. In this case, only - the outermost array is unwrapped, while all the inner arrays - remain unchanged. Thus, implicit unwrapping can only go one - level down within each path evaluation step. - </para> - </listitem> - </itemizedlist> - </para> - - <para> - For example, when querying the GPS data listed above, you can - abstract from the fact that it stores an array of segments - when using the lax mode: -<programlisting> -'lax $.track.segments.location' -</programlisting> - </para> - - <para> - In the strict mode, the specified path must exactly match the structure of - the queried JSON document to return an SQL/JSON item, so using this - path expression will cause an error. To get the same result as in - the lax mode, you have to explicitly unwrap the - <literal>segments</literal> array: -<programlisting> -'strict $.track.segments[*].location' -</programlisting> - </para> - - </sect3> - - <sect3 id="jsonpath-regular-expressions"> - <title>Regular Expressions</title> - - <indexterm zone="jsonpath-regular-expressions"> - <primary><literal>LIKE_REGEX</literal></primary> - <secondary>in SQL/JSON</secondary> - </indexterm> - - <para> - SQL/JSON path expressions allow matching text to a regular expression - with the <literal>like_regex</literal> filter. For example, the - following SQL/JSON path query would case-insensitively match all - strings in an array that start with an English vowel: -<programlisting> -'$[*] ? (@ like_regex "^[aeiou]" flag "i")' -</programlisting> - </para> - - <para> - The optional <literal>flag</literal> string may include one or more of - the characters - <literal>i</literal> for case-insensitive match, - <literal>m</literal> to allow <literal>^</literal> - and <literal>$</literal> to match at newlines, - <literal>s</literal> to allow <literal>.</literal> to match a newline, - and <literal>q</literal> to quote the whole pattern (reducing the - behavior to a simple substring match). - </para> - - <para> - The SQL/JSON standard borrows its definition for regular expressions - from the <literal>LIKE_REGEX</literal> operator, which in turn uses the - XQuery standard. PostgreSQL does not currently support the - <literal>LIKE_REGEX</literal> operator. Therefore, - the <literal>like_regex</literal> filter is implemented using the - POSIX regular expression engine described in - <xref linkend="functions-posix-regexp"/>. This leads to various minor - discrepancies from standard SQL/JSON behavior, which are cataloged in - <xref linkend="posix-vs-xquery"/>. - Note, however, that the flag-letter incompatibilities described there - do not apply to SQL/JSON, as it translates the XQuery flag letters to - match what the POSIX engine expects. - </para> - - <para> - Keep in mind that the pattern argument of <literal>like_regex</literal> - is a JSON path string literal, written according to the rules given in - <xref linkend="datatype-jsonpath"/>. This means in particular that any - backslashes you want to use in the regular expression must be doubled. - For example, to match strings that contain only digits: -<programlisting> -'$ ? (@ like_regex "^\\d+$")' -</programlisting> - </para> - - </sect3> - - <sect3 id="functions-sqljson-path-operators"> - <title>SQL/JSON Path Operators and Methods</title> - - <para> - <xref linkend="functions-sqljson-op-table"/> shows the operators and - methods available in <type>jsonpath</type>. <xref - linkend="functions-sqljson-filter-ex-table"/> shows the available filter - expression elements. - </para> - - <table id="functions-sqljson-op-table"> - <title><type>jsonpath</type> Operators and Methods</title> - <tgroup cols="5"> + <table id="functions-jsonb-op-table"> + <title>Additional <type>jsonb</type> Operators</title> + <tgroup cols="4"> <thead> <row> - <entry>Operator/Method</entry> + <entry>Operator</entry> + <entry>Right Operand Type</entry> <entry>Description</entry> - <entry>Example JSON</entry> - <entry>Example Query</entry> - <entry>Result</entry> + <entry>Example</entry> </row> </thead> <tbody> <row> - <entry><literal>+</literal> (unary)</entry> - <entry>Plus operator that iterates over the SQL/JSON sequence</entry> - <entry><literal>{"x": [2.85, -14.7, -9.4]}</literal></entry> - <entry><literal>+ $.x.floor()</literal></entry> - <entry><literal>2, -15, -10</literal></entry> - </row> - <row> - <entry><literal>-</literal> (unary)</entry> - <entry>Minus operator that iterates over the SQL/JSON sequence</entry> - <entry><literal>{"x": [2.85, -14.7, -9.4]}</literal></entry> - <entry><literal>- $.x.floor()</literal></entry> - <entry><literal>-2, 15, 10</literal></entry> + <entry><literal>@></literal></entry> + <entry><type>jsonb</type></entry> + <entry>Does the left JSON value contain the right JSON + path/value entries at the top level?</entry> + <entry><literal>'{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonb</literal></entry> </row> <row> - <entry><literal>+</literal> (binary)</entry> - <entry>Addition</entry> - <entry><literal>[2]</literal></entry> - <entry><literal>2 + $[0]</literal></entry> - <entry><literal>4</literal></entry> + <entry><literal><@</literal></entry> + <entry><type>jsonb</type></entry> + <entry>Are the left JSON path/value entries contained at the top level within + the right JSON value?</entry> + <entry><literal>'{"b":2}'::jsonb <@ '{"a":1, "b":2}'::jsonb</literal></entry> </row> <row> - <entry><literal>-</literal> (binary)</entry> - <entry>Subtraction</entry> - <entry><literal>[2]</literal></entry> - <entry><literal>4 - $[0]</literal></entry> - <entry><literal>2</literal></entry> - </row> - <row> - <entry><literal>*</literal></entry> - <entry>Multiplication</entry> - <entry><literal>[4]</literal></entry> - <entry><literal>2 * $[0]</literal></entry> - <entry><literal>8</literal></entry> + <entry><literal>?</literal></entry> + <entry><type>text</type></entry> + <entry>Does the <emphasis>string</emphasis> exist as a top-level + key within the JSON value?</entry> + <entry><literal>'{"a":1, "b":2}'::jsonb ? 'b'</literal></entry> </row> <row> - <entry><literal>/</literal></entry> - <entry>Division</entry> - <entry><literal>[8]</literal></entry> - <entry><literal>$[0] / 2</literal></entry> - <entry><literal>4</literal></entry> + <entry><literal>?|</literal></entry> + <entry><type>text[]</type></entry> + <entry>Do any of these array <emphasis>strings</emphasis> + exist as top-level keys?</entry> + <entry><literal>'{"a":1, "b":2, "c":3}'::jsonb ?| array['b', 'c']</literal></entry> </row> <row> - <entry><literal>%</literal></entry> - <entry>Modulus</entry> - <entry><literal>[32]</literal></entry> - <entry><literal>$[0] % 10</literal></entry> - <entry><literal>2</literal></entry> + <entry><literal>?&</literal></entry> + <entry><type>text[]</type></entry> + <entry>Do all of these array <emphasis>strings</emphasis> exist + as top-level keys?</entry> + <entry><literal>'["a", "b"]'::jsonb ?& array['a', 'b']</literal></entry> </row> <row> - <entry><literal>type()</literal></entry> - <entry>Type of the SQL/JSON item</entry> - <entry><literal>[1, "2", {}]</literal></entry> - <entry><literal>$[*].type()</literal></entry> - <entry><literal>"number", "string", "object"</literal></entry> + <entry><literal>||</literal></entry> + <entry><type>jsonb</type></entry> + <entry>Concatenate two <type>jsonb</type> values into a new <type>jsonb</type> value</entry> + <entry><literal>'["a", "b"]'::jsonb || '["c", "d"]'::jsonb</literal></entry> </row> <row> - <entry><literal>size()</literal></entry> - <entry>Size of the SQL/JSON item</entry> - <entry><literal>{"m": [11, 15]}</literal></entry> - <entry><literal>$.m.size()</literal></entry> - <entry><literal>2</literal></entry> + <entry><literal>-</literal></entry> + <entry><type>text</type></entry> + <entry>Delete key/value pair or <emphasis>string</emphasis> + element from left operand. Key/value pairs are matched based + on their key value.</entry> + <entry><literal>'{"a": "b"}'::jsonb - 'a' </literal></entry> </row> <row> - <entry><literal>double()</literal></entry> - <entry>Approximate floating-point number converted from an SQL/JSON number or a string</entry> - <entry><literal>{"len": "1.9"}</literal></entry> - <entry><literal>$.len.double() * 2</literal></entry> - <entry><literal>3.8</literal></entry> + <entry><literal>-</literal></entry> + <entry><type>text[]</type></entry> + <entry>Delete multiple key/value pairs or <emphasis>string</emphasis> + elements from left operand. Key/value pairs are matched based + on their key value.</entry> + <entry><literal>'{"a": "b", "c": "d"}'::jsonb - '{a,c}'::text[] </literal></entry> </row> <row> - <entry><literal>ceiling()</literal></entry> - <entry>Nearest integer greater than or equal to the SQL/JSON number</entry> - <entry><literal>{"h": 1.3}</literal></entry> - <entry><literal>$.h.ceiling()</literal></entry> - <entry><literal>2</literal></entry> + <entry><literal>-</literal></entry> + <entry><type>integer</type></entry> + <entry>Delete the array element with specified index (Negative + integers count from the end). Throws an error if top level + container is not an array.</entry> + <entry><literal>'["a", "b"]'::jsonb - 1 </literal></entry> </row> <row> - <entry><literal>floor()</literal></entry> - <entry>Nearest integer less than or equal to the SQL/JSON number</entry> - <entry><literal>{"h": 1.3}</literal></entry> - <entry><literal>$.h.floor()</literal></entry> - <entry><literal>1</literal></entry> + <entry><literal>#-</literal></entry> + <entry><type>text[]</type></entry> + <entry>Delete the field or element with specified path (for + JSON arrays, negative integers count from the end)</entry> + <entry><literal>'["a", {"b":1}]'::jsonb #- '{1,b}'</literal></entry> </row> <row> - <entry><literal>abs()</literal></entry> - <entry>Absolute value of the SQL/JSON number</entry> - <entry><literal>{"z": -0.3}</literal></entry> - <entry><literal>$.z.abs()</literal></entry> - <entry><literal>0.3</literal></entry> + <entry><literal>@?</literal></entry> + <entry><type>jsonpath</type></entry> + <entry>Does JSON path return any item for the specified JSON value?</entry> + <entry><literal>'{"a":[1,2,3,4,5]}'::jsonb @? '$.a[*] ? (@ > 2)'</literal></entry> </row> <row> - <entry><literal>keyvalue()</literal></entry> - <entry> - Sequence of object's key-value pairs represented as array of items - containing three fields (<literal>"key"</literal>, - <literal>"value"</literal>, and <literal>"id"</literal>). - <literal>"id"</literal> is a unique identifier of the object - key-value pair belongs to. - </entry> - <entry><literal>{"x": "20", "y": 32}</literal></entry> - <entry><literal>$.keyvalue()</literal></entry> - <entry><literal>{"key": "x", "value": "20", "id": 0}, {"key": "y", "value": 32, "id": 0}</literal></entry> + <entry><literal>@@</literal></entry> + <entry><type>jsonpath</type></entry> + <entry>Returns the result of JSON path predicate check for the specified JSON value. + Only the first item of the result is taken into account. If the + result is not Boolean, then <literal>null</literal> is returned.</entry> + <entry><literal>'{"a":[1,2,3,4,5]}'::jsonb @@ '$.a[*] > 2'</literal></entry> </row> </tbody> </tgroup> - </table> - - <table id="functions-sqljson-filter-ex-table"> - <title><type>jsonpath</type> Filter Expression Elements</title> - <tgroup cols="5"> - <thead> - <row> - <entry>Value/Predicate</entry> - <entry>Description</entry> - <entry>Example JSON</entry> - <entry>Example Query</entry> - <entry>Result</entry> - </row> - </thead> - <tbody> - <row> - <entry><literal>==</literal></entry> - <entry>Equality operator</entry> - <entry><literal>[1, 2, 1, 3]</literal></entry> - <entry><literal>$[*] ? (@ == 1)</literal></entry> - <entry><literal>1, 1</literal></entry> - </row> - <row> - <entry><literal>!=</literal></entry> - <entry>Non-equality operator</entry> - <entry><literal>[1, 2, 1, 3]</literal></entry> - <entry><literal>$[*] ? (@ != 1)</literal></entry> - <entry><literal>2, 3</literal></entry> - </row> - <row> - <entry><literal><></literal></entry> - <entry>Non-equality operator (same as <literal>!=</literal>)</entry> - <entry><literal>[1, 2, 1, 3]</literal></entry> - <entry><literal>$[*] ? (@ <> 1)</literal></entry> - <entry><literal>2, 3</literal></entry> - </row> - <row> - <entry><literal><</literal></entry> - <entry>Less-than operator</entry> - <entry><literal>[1, 2, 3]</literal></entry> - <entry><literal>$[*] ? (@ < 2)</literal></entry> - <entry><literal>1</literal></entry> - </row> - <row> - <entry><literal><=</literal></entry> - <entry>Less-than-or-equal-to operator</entry> - <entry><literal>[1, 2, 3]</literal></entry> - <entry><literal>$[*] ? (@ <= 2)</literal></entry> - <entry><literal>1, 2</literal></entry> - </row> - <row> - <entry><literal>></literal></entry> - <entry>Greater-than operator</entry> - <entry><literal>[1, 2, 3]</literal></entry> - <entry><literal>$[*] ? (@ > 2)</literal></entry> - <entry><literal>3</literal></entry> - </row> - <row> - <entry><literal>>=</literal></entry> - <entry>Greater-than-or-equal-to operator</entry> - <entry><literal>[1, 2, 3]</literal></entry> - <entry><literal>$[*] ? (@ >= 2)</literal></entry> - <entry><literal>2, 3</literal></entry> - </row> - <row> - <entry><literal>true</literal></entry> - <entry>Value used to perform comparison with JSON <literal>true</literal> literal</entry> - <entry><literal>[{"name": "John", "parent": false}, - {"name": "Chris", "parent": true}]</literal></entry> - <entry><literal>$[*] ? (@.parent == true)</literal></entry> - <entry><literal>{"name": "Chris", "parent": true}</literal></entry> - </row> - <row> - <entry><literal>false</literal></entry> - <entry>Value used to perform comparison with JSON <literal>false</literal> literal</entry> - <entry><literal>[{"name": "John", "parent": false}, - {"name": "Chris", "parent": true}]</literal></entry> - <entry><literal>$[*] ? (@.parent == false)</literal></entry> - <entry><literal>{"name": "John", "parent": false}</literal></entry> - </row> - <row> - <entry><literal>null</literal></entry> - <entry>Value used to perform comparison with JSON <literal>null</literal> value</entry> - <entry><literal>[{"name": "Mary", "job": null}, - {"name": "Michael", "job": "driver"}]</literal></entry> - <entry><literal>$[*] ? (@.job == null) .name</literal></entry> - <entry><literal>"Mary"</literal></entry> - </row> - <row> - <entry><literal>&&</literal></entry> - <entry>Boolean AND</entry> - <entry><literal>[1, 3, 7]</literal></entry> - <entry><literal>$[*] ? (@ > 1 && @ < 5)</literal></entry> - <entry><literal>3</literal></entry> - </row> - <row> - <entry><literal>||</literal></entry> - <entry>Boolean OR</entry> - <entry><literal>[1, 3, 7]</literal></entry> - <entry><literal>$[*] ? (@ < 1 || @ > 5)</literal></entry> - <entry><literal>7</literal></entry> - </row> - <row> - <entry><literal>!</literal></entry> - <entry>Boolean NOT</entry> - <entry><literal>[1, 3, 7]</literal></entry> - <entry><literal>$[*] ? (!(@ < 5))</literal></entry> - <entry><literal>7</literal></entry> - </row> - <row> - <entry><literal>like_regex</literal></entry> - <entry> - Tests whether the first operand matches the regular expression - given by the second operand, optionally with modifications - described by a string of <literal>flag</literal> characters (see - <xref linkend="jsonpath-regular-expressions"/>) - </entry> - <entry><literal>["abc", "abd", "aBdC", "abdacb", "babc"]</literal></entry> - <entry><literal>$[*] ? (@ like_regex "^ab.*c" flag "i")</literal></entry> - <entry><literal>"abc", "aBdC", "abdacb"</literal></entry> - </row> - <row> - <entry><literal>starts with</literal></entry> - <entry>Tests whether the second operand is an initial substring of the first operand</entry> - <entry><literal>["John Smith", "Mary Stone", "Bob Johnson"]</literal></entry> - <entry><literal>$[*] ? (@ starts with "John")</literal></entry> - <entry><literal>"John Smith"</literal></entry> - </row> - <row> - <entry><literal>exists</literal></entry> - <entry>Tests whether a path expression matches at least one SQL/JSON item</entry> - <entry><literal>{"x": [1, 2], "y": [2, 4]}</literal></entry> - <entry><literal>strict $.* ? (exists (@ ? (@[*] > 2)))</literal></entry> - <entry><literal>2, 4</literal></entry> - </row> - <row> - <entry><literal>is unknown</literal></entry> - <entry>Tests whether a Boolean condition is <literal>unknown</literal></entry> - <entry><literal>[-1, 2, 7, "infinity"]</literal></entry> - <entry><literal>$[*] ? ((@ > 0) is unknown)</literal></entry> - <entry><literal>"infinity"</literal></entry> - </row> - </tbody> - </tgroup> - </table> - </sect3> - - </sect2> - - <sect2 id="functions-pgjson"> - <title>JSON Functions and Operators</title> - <indexterm zone="functions-json"> - <primary>JSON</primary> - <secondary>functions and operators</secondary> - </indexterm> - - <para> - <xref linkend="functions-json-op-table"/> shows the operators that - are available for use with JSON data types (see <xref - linkend="datatype-json"/>). - </para> - - <table id="functions-json-op-table"> - <title><type>json</type> and <type>jsonb</type> Operators</title> - <tgroup cols="6"> - <thead> - <row> - <entry>Operator</entry> - <entry>Right Operand Type</entry> - <entry>Return type</entry> - <entry>Description</entry> - <entry>Example</entry> - <entry>Example Result</entry> - </row> - </thead> - <tbody> - <row> - <entry><literal>-></literal></entry> - <entry><type>int</type></entry> - <entry><type>json</type> or <type>jsonb</type></entry> - <entry>Get JSON array element (indexed from zero, negative - integers count from the end)</entry> - <entry><literal>'[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json->2</literal></entry> - <entry><literal>{"c":"baz"}</literal></entry> - </row> - <row> - <entry><literal>-></literal></entry> - <entry><type>text</type></entry> - <entry><type>json</type> or <type>jsonb</type></entry> - <entry>Get JSON object field by key</entry> - <entry><literal>'{"a": {"b":"foo"}}'::json->'a'</literal></entry> - <entry><literal>{"b":"foo"}</literal></entry> - </row> - <row> - <entry><literal>->></literal></entry> - <entry><type>int</type></entry> - <entry><type>text</type></entry> - <entry>Get JSON array element as <type>text</type></entry> - <entry><literal>'[1,2,3]'::json->>2</literal></entry> - <entry><literal>3</literal></entry> - </row> - <row> - <entry><literal>->></literal></entry> - <entry><type>text</type></entry> - <entry><type>text</type></entry> - <entry>Get JSON object field as <type>text</type></entry> - <entry><literal>'{"a":1,"b":2}'::json->>'b'</literal></entry> - <entry><literal>2</literal></entry> - </row> - <row> - <entry><literal>#></literal></entry> - <entry><type>text[]</type></entry> - <entry><type>json</type> or <type>jsonb</type></entry> - <entry>Get JSON object at the specified path</entry> - <entry><literal>'{"a": {"b":{"c": "foo"}}}'::json#>'{a,b}'</literal></entry> - <entry><literal>{"c": "foo"}</literal></entry> - </row> - <row> - <entry><literal>#>></literal></entry> - <entry><type>text[]</type></entry> - <entry><type>text</type></entry> - <entry>Get JSON object at the specified path as <type>text</type></entry> - <entry><literal>'{"a":[1,2,3],"b":[4,5,6]}'::json#>>'{a,2}'</literal></entry> - <entry><literal>3</literal></entry> - </row> - </tbody> - </tgroup> - </table> - - <note> - <para> - There are parallel variants of these operators for both the - <type>json</type> and <type>jsonb</type> types. - The field/element/path extraction operators - return the same type as their left-hand input (either <type>json</type> - or <type>jsonb</type>), except for those specified as - returning <type>text</type>, which coerce the value to text. - The field/element/path extraction operators return NULL, rather than - failing, if the JSON input does not have the right structure to match - the request; for example if no such element exists. The - field/element/path extraction operators that accept integer JSON - array subscripts all support negative subscripting from the end of - arrays. - </para> - </note> - <para> - The standard comparison operators shown in <xref - linkend="functions-comparison-op-table"/> are available for - <type>jsonb</type>, but not for <type>json</type>. They follow the - ordering rules for B-tree operations outlined at <xref - linkend="json-indexing"/>. - </para> - <para> - Some further operators also exist only for <type>jsonb</type>, as shown - in <xref linkend="functions-jsonb-op-table"/>. - Many of these operators can be indexed by - <type>jsonb</type> operator classes. For a full description of - <type>jsonb</type> containment and existence semantics, see <xref - linkend="json-containment"/>. <xref linkend="json-indexing"/> - describes how these operators can be used to effectively index - <type>jsonb</type> data. - </para> - <table id="functions-jsonb-op-table"> - <title>Additional <type>jsonb</type> Operators</title> - <tgroup cols="4"> - <thead> - <row> - <entry>Operator</entry> - <entry>Right Operand Type</entry> - <entry>Description</entry> - <entry>Example</entry> - </row> - </thead> - <tbody> - <row> - <entry><literal>@></literal></entry> - <entry><type>jsonb</type></entry> - <entry>Does the left JSON value contain the right JSON - path/value entries at the top level?</entry> - <entry><literal>'{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonb</literal></entry> - </row> - <row> - <entry><literal><@</literal></entry> - <entry><type>jsonb</type></entry> - <entry>Are the left JSON path/value entries contained at the top level within - the right JSON value?</entry> - <entry><literal>'{"b":2}'::jsonb <@ '{"a":1, "b":2}'::jsonb</literal></entry> - </row> - <row> - <entry><literal>?</literal></entry> - <entry><type>text</type></entry> - <entry>Does the <emphasis>string</emphasis> exist as a top-level - key within the JSON value?</entry> - <entry><literal>'{"a":1, "b":2}'::jsonb ? 'b'</literal></entry> - </row> - <row> - <entry><literal>?|</literal></entry> - <entry><type>text[]</type></entry> - <entry>Do any of these array <emphasis>strings</emphasis> - exist as top-level keys?</entry> - <entry><literal>'{"a":1, "b":2, "c":3}'::jsonb ?| array['b', 'c']</literal></entry> - </row> - <row> - <entry><literal>?&</literal></entry> - <entry><type>text[]</type></entry> - <entry>Do all of these array <emphasis>strings</emphasis> exist - as top-level keys?</entry> - <entry><literal>'["a", "b"]'::jsonb ?& array['a', 'b']</literal></entry> - </row> - <row> - <entry><literal>||</literal></entry> - <entry><type>jsonb</type></entry> - <entry>Concatenate two <type>jsonb</type> values into a new <type>jsonb</type> value</entry> - <entry><literal>'["a", "b"]'::jsonb || '["c", "d"]'::jsonb</literal></entry> - </row> - <row> - <entry><literal>-</literal></entry> - <entry><type>text</type></entry> - <entry>Delete key/value pair or <emphasis>string</emphasis> - element from left operand. Key/value pairs are matched based - on their key value.</entry> - <entry><literal>'{"a": "b"}'::jsonb - 'a' </literal></entry> - </row> - <row> - <entry><literal>-</literal></entry> - <entry><type>text[]</type></entry> - <entry>Delete multiple key/value pairs or <emphasis>string</emphasis> - elements from left operand. Key/value pairs are matched based - on their key value.</entry> - <entry><literal>'{"a": "b", "c": "d"}'::jsonb - '{a,c}'::text[] </literal></entry> - </row> - <row> - <entry><literal>-</literal></entry> - <entry><type>integer</type></entry> - <entry>Delete the array element with specified index (Negative - integers count from the end). Throws an error if top level - container is not an array.</entry> - <entry><literal>'["a", "b"]'::jsonb - 1 </literal></entry> - </row> - <row> - <entry><literal>#-</literal></entry> - <entry><type>text[]</type></entry> - <entry>Delete the field or element with specified path (for - JSON arrays, negative integers count from the end)</entry> - <entry><literal>'["a", {"b":1}]'::jsonb #- '{1,b}'</literal></entry> - </row> - <row> - <entry><literal>@?</literal></entry> - <entry><type>jsonpath</type></entry> - <entry>Does JSON path return any item for the specified JSON value?</entry> - <entry><literal>'{"a":[1,2,3,4,5]}'::jsonb @? '$.a[*] ? (@ > 2)'</literal></entry> - </row> - <row> - <entry><literal>@@</literal></entry> - <entry><type>jsonpath</type></entry> - <entry>Returns the result of JSON path predicate check for the specified JSON value. - Only the first item of the result is taken into account. If the - result is not Boolean, then <literal>null</literal> is returned.</entry> - <entry><literal>'{"a":[1,2,3,4,5]}'::jsonb @@ '$.a[*] > 2'</literal></entry> - </row> - </tbody> - </tgroup> - </table> + </table> <note> <para> @@ -13407,6 +12770,640 @@ table2-mapping </para> </sect2> + + <sect2 id="functions-sqljson-path"> + <title>The SQL/JSON Path Language</title> + + <indexterm zone="functions-sqljson-path"> + <primary>SQL/JSON path language</primary> + </indexterm> + + <para> + SQL/JSON path expressions specify the items to be retrieved + from the JSON data, similar to XPath expressions used + for SQL access to XML. In <productname>PostgreSQL</productname>, + path expressions are implemented as the <type>jsonpath</type> + data type and can use any elements described in + <xref linkend="datatype-jsonpath"/>. + </para> + + <para>JSON query functions and operators + pass the provided path expression to the <firstterm>path engine</firstterm> + for evaluation. If the expression matches the queried JSON data, + the corresponding SQL/JSON item is returned. + Path expressions are written in the SQL/JSON path language + and can also include arithmetic expressions and functions. + Query functions treat the provided expression as a + text string, so it must be enclosed in single quotes. + </para> + + <para> + A path expression consists of a sequence of elements allowed + by the <type>jsonpath</type> data type. + The path expression is evaluated from left to right, but + you can use parentheses to change the order of operations. + If the evaluation is successful, a sequence of SQL/JSON items + (<firstterm>SQL/JSON sequence</firstterm>) is produced, + and the evaluation result is returned to the JSON query function + that completes the specified computation. + </para> + + <para> + To refer to the JSON data to be queried (the + <firstterm>context item</firstterm>), use the <literal>$</literal> sign + in the path expression. It can be followed by one or more + <link linkend="type-jsonpath-accessors">accessor operators</link>, + which go down the JSON structure level by level to retrieve the + content of context item. Each operator that follows deals with the + result of the previous evaluation step. + </para> + + <para> + For example, suppose you have some JSON data from a GPS tracker that you + would like to parse, such as: +<programlisting> +{ "track" : + { + "segments" : [ + { "location": [ 47.763, 13.4034 ], + "start time": "2018-10-14 10:05:14", + "HR": 73 + }, + { "location": [ 47.706, 13.2635 ], + "start time": "2018-10-14 10:39:21", + "HR": 135 + } ] + } +} +</programlisting> + </para> + + <para> + To retrieve the available track segments, you need to use the + <literal>.<replaceable>key</replaceable></literal> accessor + operator for all the preceding JSON objects: +<programlisting> +'$.track.segments' +</programlisting> + </para> + + <para> + If the item to retrieve is an element of an array, you have + to unnest this array using the <literal>[*]</literal> operator. For example, + the following path will return location coordinates for all + the available track segments: +<programlisting> +'$.track.segments[*].location' +</programlisting> + </para> + + <para> + To return the coordinates of the first segment only, you can + specify the corresponding subscript in the <literal>[]</literal> + accessor operator. Note that the SQL/JSON arrays are 0-relative: +<programlisting> +'$.track.segments[0].location' +</programlisting> + </para> + + <para> + The result of each path evaluation step can be processed + by one or more <type>jsonpath</type> operators and methods + listed in <xref linkend="functions-sqljson-path-operators"/>. + Each method name must be preceded by a dot. For example, + you can get an array size: +<programlisting> +'$.track.segments.size()' +</programlisting> + For more examples of using <type>jsonpath</type> operators + and methods within path expressions, see + <xref linkend="functions-sqljson-path-operators"/>. + </para> + + <para> + When defining the path, you can also use one or more + <firstterm>filter expressions</firstterm> that work similar to the + <literal>WHERE</literal> clause in SQL. A filter expression begins with + a question mark and provides a condition in parentheses: + + <programlisting> +? (<replaceable>condition</replaceable>) + </programlisting> + </para> + + <para> + Filter expressions must be specified right after the path evaluation step + to which they are applied. The result of this step is filtered to include + only those items that satisfy the provided condition. SQL/JSON defines + three-valued logic, so the condition can be <literal>true</literal>, <literal>false</literal>, + or <literal>unknown</literal>. The <literal>unknown</literal> value + plays the same role as SQL <literal>NULL</literal> and can be tested + for with the <literal>is unknown</literal> predicate. Further path + evaluation steps use only those items for which filter expressions + return <literal>true</literal>. + </para> + + <para> + Functions and operators that can be used in filter expressions are listed + in <xref linkend="functions-sqljson-filter-ex-table"/>. The path + evaluation result to be filtered is denoted by the <literal>@</literal> + variable. To refer to a JSON element stored at a lower nesting level, + add one or more accessor operators after <literal>@</literal>. + </para> + + <para> + Suppose you would like to retrieve all heart rate values higher + than 130. You can achieve this using the following expression: +<programlisting> +'$.track.segments[*].HR ? (@ > 130)' +</programlisting> + </para> + + <para> + To get the start time of segments with such values instead, you have to + filter out irrelevant segments before returning the start time, so the + filter expression is applied to the previous step, and the path used + in the condition is different: +<programlisting> +'$.track.segments[*] ? (@.HR > 130)."start time"' +</programlisting> + </para> + + <para> + You can use several filter expressions on the same nesting level, if + required. For example, the following expression selects all segments + that contain locations with relevant coordinates and high heart rate values: +<programlisting> +'$.track.segments[*] ? (@.location[1] < 13.4) ? (@.HR > 130)."start time"' +</programlisting> + </para> + + <para> + Using filter expressions at different nesting levels is also allowed. + The following example first filters all segments by location, and then + returns high heart rate values for these segments, if available: +<programlisting> +'$.track.segments[*] ? (@.location[1] < 13.4).HR ? (@ > 130)' +</programlisting> + </para> + + <para> + You can also nest filter expressions within each other: +<programlisting> +'$.track ? (exists(@.segments[*] ? (@.HR > 130))).segments.size()' +</programlisting> + This expression returns the size of the track if it contains any + segments with high heart rate values, or an empty sequence otherwise. + </para> + + <para> + <productname>PostgreSQL</productname>'s implementation of SQL/JSON path + language has the following deviations from the SQL/JSON standard: + </para> + + <itemizedlist> + <listitem> + <para> + <literal>.datetime()</literal> item method is not implemented yet + mainly because immutable <type>jsonpath</type> functions and operators + cannot reference session timezone, which is used in some datetime + operations. Datetime support will be added to <type>jsonpath</type> + in future versions of <productname>PostgreSQL</productname>. + </para> + </listitem> + + <listitem> + <para> + A path expression can be a Boolean predicate, although the SQL/JSON + standard allows predicates only in filters. This is necessary for + implementation of the <literal>@@</literal> operator. For example, + the following <type>jsonpath</type> expression is valid in + <productname>PostgreSQL</productname>: +<programlisting> +'$.track.segments[*].HR < 70' +</programlisting> + </para> + </listitem> + + <listitem> + <para> + There are minor differences in the interpretation of regular + expression patterns used in <literal>like_regex</literal> filters, as + described in <xref linkend="jsonpath-regular-expressions"/>. + </para> + </listitem> + </itemizedlist> + + <sect3 id="strict-and-lax-modes"> + <title>Strict and Lax Modes</title> + <para> + When you query JSON data, the path expression may not match the + actual JSON data structure. An attempt to access a non-existent + member of an object or element of an array results in a + structural error. SQL/JSON path expressions have two modes + of handling structural errors: + </para> + + <itemizedlist> + <listitem> + <para> + 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. + </para> + </listitem> + <listitem> + <para> + strict — if a structural error occurs, an error is raised. + </para> + </listitem> + </itemizedlist> + + <para> + The lax mode facilitates matching of a JSON document structure and path + expression if the JSON data does not conform to the expected schema. + If an operand does not match the requirements of a particular operation, + it can be automatically wrapped as an SQL/JSON array or unwrapped by + converting its elements into an SQL/JSON sequence before performing + this operation. Besides, comparison operators automatically unwrap their + operands in the lax mode, so you can compare SQL/JSON arrays + out-of-the-box. An array of size 1 is considered equal to its sole element. + Automatic unwrapping is not performed only when: + <itemizedlist> + <listitem> + <para> + The path expression contains <literal>type()</literal> or + <literal>size()</literal> methods that return the type + and the number of elements in the array, respectively. + </para> + </listitem> + <listitem> + <para> + The queried JSON data contain nested arrays. In this case, only + the outermost array is unwrapped, while all the inner arrays + remain unchanged. Thus, implicit unwrapping can only go one + level down within each path evaluation step. + </para> + </listitem> + </itemizedlist> + </para> + + <para> + For example, when querying the GPS data listed above, you can + abstract from the fact that it stores an array of segments + when using the lax mode: +<programlisting> +'lax $.track.segments.location' +</programlisting> + </para> + + <para> + In the strict mode, the specified path must exactly match the structure of + the queried JSON document to return an SQL/JSON item, so using this + path expression will cause an error. To get the same result as in + the lax mode, you have to explicitly unwrap the + <literal>segments</literal> array: +<programlisting> +'strict $.track.segments[*].location' +</programlisting> + </para> + + </sect3> + + <sect3 id="jsonpath-regular-expressions"> + <title>Regular Expressions</title> + + <indexterm zone="jsonpath-regular-expressions"> + <primary><literal>LIKE_REGEX</literal></primary> + <secondary>in SQL/JSON</secondary> + </indexterm> + + <para> + SQL/JSON path expressions allow matching text to a regular expression + with the <literal>like_regex</literal> filter. For example, the + following SQL/JSON path query would case-insensitively match all + strings in an array that start with an English vowel: +<programlisting> +'$[*] ? (@ like_regex "^[aeiou]" flag "i")' +</programlisting> + </para> + + <para> + The optional <literal>flag</literal> string may include one or more of + the characters + <literal>i</literal> for case-insensitive match, + <literal>m</literal> to allow <literal>^</literal> + and <literal>$</literal> to match at newlines, + <literal>s</literal> to allow <literal>.</literal> to match a newline, + and <literal>q</literal> to quote the whole pattern (reducing the + behavior to a simple substring match). + </para> + + <para> + The SQL/JSON standard borrows its definition for regular expressions + from the <literal>LIKE_REGEX</literal> operator, which in turn uses the + XQuery standard. PostgreSQL does not currently support the + <literal>LIKE_REGEX</literal> operator. Therefore, + the <literal>like_regex</literal> filter is implemented using the + POSIX regular expression engine described in + <xref linkend="functions-posix-regexp"/>. This leads to various minor + discrepancies from standard SQL/JSON behavior, which are cataloged in + <xref linkend="posix-vs-xquery"/>. + Note, however, that the flag-letter incompatibilities described there + do not apply to SQL/JSON, as it translates the XQuery flag letters to + match what the POSIX engine expects. + </para> + + <para> + Keep in mind that the pattern argument of <literal>like_regex</literal> + is a JSON path string literal, written according to the rules given in + <xref linkend="datatype-jsonpath"/>. This means in particular that any + backslashes you want to use in the regular expression must be doubled. + For example, to match strings that contain only digits: +<programlisting> +'$ ? (@ like_regex "^\\d+$")' +</programlisting> + </para> + + </sect3> + + <sect3 id="functions-sqljson-path-operators"> + <title>SQL/JSON Path Operators and Methods</title> + + <para> + <xref linkend="functions-sqljson-op-table"/> shows the operators and + methods available in <type>jsonpath</type>. <xref + linkend="functions-sqljson-filter-ex-table"/> shows the available filter + expression elements. + </para> + + <table id="functions-sqljson-op-table"> + <title><type>jsonpath</type> Operators and Methods</title> + <tgroup cols="5"> + <thead> + <row> + <entry>Operator/Method</entry> + <entry>Description</entry> + <entry>Example JSON</entry> + <entry>Example Query</entry> + <entry>Result</entry> + </row> + </thead> + <tbody> + <row> + <entry><literal>+</literal> (unary)</entry> + <entry>Plus operator that iterates over the SQL/JSON sequence</entry> + <entry><literal>{"x": [2.85, -14.7, -9.4]}</literal></entry> + <entry><literal>+ $.x.floor()</literal></entry> + <entry><literal>2, -15, -10</literal></entry> + </row> + <row> + <entry><literal>-</literal> (unary)</entry> + <entry>Minus operator that iterates over the SQL/JSON sequence</entry> + <entry><literal>{"x": [2.85, -14.7, -9.4]}</literal></entry> + <entry><literal>- $.x.floor()</literal></entry> + <entry><literal>-2, 15, 10</literal></entry> + </row> + <row> + <entry><literal>+</literal> (binary)</entry> + <entry>Addition</entry> + <entry><literal>[2]</literal></entry> + <entry><literal>2 + $[0]</literal></entry> + <entry><literal>4</literal></entry> + </row> + <row> + <entry><literal>-</literal> (binary)</entry> + <entry>Subtraction</entry> + <entry><literal>[2]</literal></entry> + <entry><literal>4 - $[0]</literal></entry> + <entry><literal>2</literal></entry> + </row> + <row> + <entry><literal>*</literal></entry> + <entry>Multiplication</entry> + <entry><literal>[4]</literal></entry> + <entry><literal>2 * $[0]</literal></entry> + <entry><literal>8</literal></entry> + </row> + <row> + <entry><literal>/</literal></entry> + <entry>Division</entry> + <entry><literal>[8]</literal></entry> + <entry><literal>$[0] / 2</literal></entry> + <entry><literal>4</literal></entry> + </row> + <row> + <entry><literal>%</literal></entry> + <entry>Modulus</entry> + <entry><literal>[32]</literal></entry> + <entry><literal>$[0] % 10</literal></entry> + <entry><literal>2</literal></entry> + </row> + <row> + <entry><literal>type()</literal></entry> + <entry>Type of the SQL/JSON item</entry> + <entry><literal>[1, "2", {}]</literal></entry> + <entry><literal>$[*].type()</literal></entry> + <entry><literal>"number", "string", "object"</literal></entry> + </row> + <row> + <entry><literal>size()</literal></entry> + <entry>Size of the SQL/JSON item</entry> + <entry><literal>{"m": [11, 15]}</literal></entry> + <entry><literal>$.m.size()</literal></entry> + <entry><literal>2</literal></entry> + </row> + <row> + <entry><literal>double()</literal></entry> + <entry>Approximate floating-point number converted from an SQL/JSON number or a string</entry> + <entry><literal>{"len": "1.9"}</literal></entry> + <entry><literal>$.len.double() * 2</literal></entry> + <entry><literal>3.8</literal></entry> + </row> + <row> + <entry><literal>ceiling()</literal></entry> + <entry>Nearest integer greater than or equal to the SQL/JSON number</entry> + <entry><literal>{"h": 1.3}</literal></entry> + <entry><literal>$.h.ceiling()</literal></entry> + <entry><literal>2</literal></entry> + </row> + <row> + <entry><literal>floor()</literal></entry> + <entry>Nearest integer less than or equal to the SQL/JSON number</entry> + <entry><literal>{"h": 1.3}</literal></entry> + <entry><literal>$.h.floor()</literal></entry> + <entry><literal>1</literal></entry> + </row> + <row> + <entry><literal>abs()</literal></entry> + <entry>Absolute value of the SQL/JSON number</entry> + <entry><literal>{"z": -0.3}</literal></entry> + <entry><literal>$.z.abs()</literal></entry> + <entry><literal>0.3</literal></entry> + </row> + <row> + <entry><literal>keyvalue()</literal></entry> + <entry> + Sequence of object's key-value pairs represented as array of items + containing three fields (<literal>"key"</literal>, + <literal>"value"</literal>, and <literal>"id"</literal>). + <literal>"id"</literal> is a unique identifier of the object + key-value pair belongs to. + </entry> + <entry><literal>{"x": "20", "y": 32}</literal></entry> + <entry><literal>$.keyvalue()</literal></entry> + <entry><literal>{"key": "x", "value": "20", "id": 0}, {"key": "y", "value": 32, "id": 0}</literal></entry> + </row> + </tbody> + </tgroup> + </table> + + <table id="functions-sqljson-filter-ex-table"> + <title><type>jsonpath</type> Filter Expression Elements</title> + <tgroup cols="5"> + <thead> + <row> + <entry>Value/Predicate</entry> + <entry>Description</entry> + <entry>Example JSON</entry> + <entry>Example Query</entry> + <entry>Result</entry> + </row> + </thead> + <tbody> + <row> + <entry><literal>==</literal></entry> + <entry>Equality operator</entry> + <entry><literal>[1, 2, 1, 3]</literal></entry> + <entry><literal>$[*] ? (@ == 1)</literal></entry> + <entry><literal>1, 1</literal></entry> + </row> + <row> + <entry><literal>!=</literal></entry> + <entry>Non-equality operator</entry> + <entry><literal>[1, 2, 1, 3]</literal></entry> + <entry><literal>$[*] ? (@ != 1)</literal></entry> + <entry><literal>2, 3</literal></entry> + </row> + <row> + <entry><literal><></literal></entry> + <entry>Non-equality operator (same as <literal>!=</literal>)</entry> + <entry><literal>[1, 2, 1, 3]</literal></entry> + <entry><literal>$[*] ? (@ <> 1)</literal></entry> + <entry><literal>2, 3</literal></entry> + </row> + <row> + <entry><literal><</literal></entry> + <entry>Less-than operator</entry> + <entry><literal>[1, 2, 3]</literal></entry> + <entry><literal>$[*] ? (@ < 2)</literal></entry> + <entry><literal>1</literal></entry> + </row> + <row> + <entry><literal><=</literal></entry> + <entry>Less-than-or-equal-to operator</entry> + <entry><literal>[1, 2, 3]</literal></entry> + <entry><literal>$[*] ? (@ <= 2)</literal></entry> + <entry><literal>1, 2</literal></entry> + </row> + <row> + <entry><literal>></literal></entry> + <entry>Greater-than operator</entry> + <entry><literal>[1, 2, 3]</literal></entry> + <entry><literal>$[*] ? (@ > 2)</literal></entry> + <entry><literal>3</literal></entry> + </row> + <row> + <entry><literal>>=</literal></entry> + <entry>Greater-than-or-equal-to operator</entry> + <entry><literal>[1, 2, 3]</literal></entry> + <entry><literal>$[*] ? (@ >= 2)</literal></entry> + <entry><literal>2, 3</literal></entry> + </row> + <row> + <entry><literal>true</literal></entry> + <entry>Value used to perform comparison with JSON <literal>true</literal> literal</entry> + <entry><literal>[{"name": "John", "parent": false}, + {"name": "Chris", "parent": true}]</literal></entry> + <entry><literal>$[*] ? (@.parent == true)</literal></entry> + <entry><literal>{"name": "Chris", "parent": true}</literal></entry> + </row> + <row> + <entry><literal>false</literal></entry> + <entry>Value used to perform comparison with JSON <literal>false</literal> literal</entry> + <entry><literal>[{"name": "John", "parent": false}, + {"name": "Chris", "parent": true}]</literal></entry> + <entry><literal>$[*] ? (@.parent == false)</literal></entry> + <entry><literal>{"name": "John", "parent": false}</literal></entry> + </row> + <row> + <entry><literal>null</literal></entry> + <entry>Value used to perform comparison with JSON <literal>null</literal> value</entry> + <entry><literal>[{"name": "Mary", "job": null}, + {"name": "Michael", "job": "driver"}]</literal></entry> + <entry><literal>$[*] ? (@.job == null) .name</literal></entry> + <entry><literal>"Mary"</literal></entry> + </row> + <row> + <entry><literal>&&</literal></entry> + <entry>Boolean AND</entry> + <entry><literal>[1, 3, 7]</literal></entry> + <entry><literal>$[*] ? (@ > 1 && @ < 5)</literal></entry> + <entry><literal>3</literal></entry> + </row> + <row> + <entry><literal>||</literal></entry> + <entry>Boolean OR</entry> + <entry><literal>[1, 3, 7]</literal></entry> + <entry><literal>$[*] ? (@ < 1 || @ > 5)</literal></entry> + <entry><literal>7</literal></entry> + </row> + <row> + <entry><literal>!</literal></entry> + <entry>Boolean NOT</entry> + <entry><literal>[1, 3, 7]</literal></entry> + <entry><literal>$[*] ? (!(@ < 5))</literal></entry> + <entry><literal>7</literal></entry> + </row> + <row> + <entry><literal>like_regex</literal></entry> + <entry> + Tests whether the first operand matches the regular expression + given by the second operand, optionally with modifications + described by a string of <literal>flag</literal> characters (see + <xref linkend="jsonpath-regular-expressions"/>) + </entry> + <entry><literal>["abc", "abd", "aBdC", "abdacb", "babc"]</literal></entry> + <entry><literal>$[*] ? (@ like_regex "^ab.*c" flag "i")</literal></entry> + <entry><literal>"abc", "aBdC", "abdacb"</literal></entry> + </row> + <row> + <entry><literal>starts with</literal></entry> + <entry>Tests whether the second operand is an initial substring of the first operand</entry> + <entry><literal>["John Smith", "Mary Stone", "Bob Johnson"]</literal></entry> + <entry><literal>$[*] ? (@ starts with "John")</literal></entry> + <entry><literal>"John Smith"</literal></entry> + </row> + <row> + <entry><literal>exists</literal></entry> + <entry>Tests whether a path expression matches at least one SQL/JSON item</entry> + <entry><literal>{"x": [1, 2], "y": [2, 4]}</literal></entry> + <entry><literal>strict $.* ? (exists (@ ? (@[*] > 2)))</literal></entry> + <entry><literal>2, 4</literal></entry> + </row> + <row> + <entry><literal>is unknown</literal></entry> + <entry>Tests whether a Boolean condition is <literal>unknown</literal></entry> + <entry><literal>[-1, 2, 7, "infinity"]</literal></entry> + <entry><literal>$[*] ? ((@ > 0) is unknown)</literal></entry> + <entry><literal>"infinity"</literal></entry> + </row> + </tbody> + </tgroup> + </table> + </sect3> + </sect2> </sect1> <sect1 id="functions-sequence"> -- GitLab