他のバージョンの文書 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

9.16. JSON関数と演算子

この節では次のことを説明します。

SQL/JSON標準を更に学ぶためには、[sqltr-19075-6]をご覧ください。 PostgreSQLでサポートされているJSON型の詳細に関しては、8.14をご覧ください。

9.16.1. JSONデータの処理と生成

表 9.44にJSONデータ型(8.14を参照)で使用可能な演算子を示します。 加えて表 9.1で示す通常の比較演算子がjsonbで利用できますが、jsonでは利用できません。 比較演算子は8.14.4で概要が示されているように示すBツリー操作用の順序付け規則にしたがいます。

表9.44 jsonjsonb演算子

演算子

説明

json -> integerjson

jsonb -> integerjsonb

JSON配列のn番目の要素を取り出します。 (配列要素はゼロから始まりますが、負の整数は最後から数えられます。)

'[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json -> 2{"c":"baz"}

'[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json -> -3{"a":"foo"}

json -> textjson

jsonb -> textjsonb

与えられたキーでJSONオブジェクトフィールドを取り出します。

'{"a": {"b":"foo"}}'::json -> 'a'{"b":"foo"}

json ->> integertext

jsonb ->> integertext

JSON配列のn番目の要素をtextとして取り出します。

'[1,2,3]'::json ->> 23

json ->> texttext

jsonb ->> texttext

与えられたキーでJSONオブジェクトフィールドをtextとして取り出します。

'{"a":1,"b":2}'::json ->> 'b'2

json #> text[]json

jsonb #> text[]jsonb

指定したパスにおけるJSONの副オブジェクトを取り出します。パス要素はフィールドキーあるいは配列のインデックスでも構いません。

'{"a": {"b": ["foo","bar"]}}'::json #> '{a,b,1}'"bar"

json #>> text[]text

jsonb #>> text[]text

指定したパスにおけるJSONの副オブジェクトをtextとして取り出します。

'{"a": {"b": ["foo","bar"]}}'::json #>> '{a,b,1}'bar


注記

JSON入力が要求と一致する正しい構造をしていなければ、フィールド/要素/パス抽出演算子は失敗するのではなくNULLを返します。例えばそのような要素が存在しない場合です。

ほかにjsonbだけで利用可能な演算子もいくつか存在します。 それらを表 9.45に示します。 8.14.4には、インデックス付されたjsonbデータを効率的に検索するためにこれらの演算子をどのように利用できるかについて書いてあります。

表9.45 追加jsonb演算子

演算子

説明

jsonb @> jsonbboolean

最初のJSON値は二番目を含んでいるか? (包含の詳細は8.14.3を参照してください。)

'{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonbt

jsonb <@ jsonbboolean

最初のJSON値は二番目に含まれているか?

'{"b":2}'::jsonb <@ '{"a":1, "b":2}'::jsonbt

jsonb ? textboolean

そのテキスト文字列はトップレベルのキーあるいは配列要素としてJSON値中に存在しているか?

'{"a":1, "b":2}'::jsonb ? 'b't

'["a", "b", "c"]'::jsonb ? 'b't

jsonb ?| text[]boolean

テキスト配列中のどれかの文字列がトップレベルのキーあるいは配列要素として存在しているか?

'{"a":1, "b":2, "c":3}'::jsonb ?| array['b', 'd']t

jsonb ?& text[]boolean

テキスト配列のすべての文字列がトップレベルのキーあるいは配列要素として存在しているか?

'["a", "b", "c"]'::jsonb ?& array['a', 'b']t

jsonb || jsonbjsonb

2つのjsonb値を結合します。 2つの配列を結合するとそれらのキーの和を持つ配列を生成します。 キーが重複している場合は2番目のオブジェクトの値が使用されます。 それ以外の場合には非配列入力を単一の要素を持つ配列に変換し、次に2つの配列として取り扱います。 再帰操作は行いません。トップレベルの配列あるいはオブジェクト構造だけがマージされます。

'["a", "b"]'::jsonb || '["a", "d"]'::jsonb["a", "b", "a", "d"]

'{"a": "b"}'::jsonb || '{"c": "d"}'::jsonb{"a": "b", "c": "d"}

'[1, 2]'::jsonb || '3'::jsonb[1, 2, 3]

'{"a": "b"}'::jsonb || '42'::jsonb[{"a": "b"}, 42]

一つの要素を持つとして配列を他の配列に追加するには、例のように配列の追加のレイヤー中に含めてください。

'[1, 2]'::jsonb || jsonb_build_array('[3, 4]'::jsonb)[1, 2, [3, 4]]

jsonb - textjsonb

キー(及びその値)をJSONオブジェクトから削除します。あるいはマッチする文字列値をJSON配列から削除します。

'{"a": "b", "c": "d"}'::jsonb - 'a'{"c": "d"}

'["a", "b", "c", "b"]'::jsonb - 'b'["a", "c"]

jsonb - text[]jsonb

左のオペランドからマッチするすべてのキーあるいは配列要素を削除します。

'{"a": "b", "c": "d"}'::jsonb - '{a,c}'::text[]{}

jsonb - integerjsonb

指定したインデックス(負の整数は最後から数えます)の配列要素を削除します。 JSON値が配列でなければエラーが生じます。

'["a", "b"]'::jsonb - 1 ["a"]

jsonb #- text[]jsonb

指定パスのフィールドあるいは配列要素を削除します。パス要素はフィールドキーあるいは配列インデックスが指定できます。

'["a", {"b":1}]'::jsonb #- '{1,b}'["a", {}]

jsonb @? jsonpathboolean

JSONパスは指定したJSON値の要素を返すか?

'{"a":[1,2,3,4,5]}'::jsonb @? '$.a[*] ? (@ > 2)'t

jsonb @@ jsonpathboolean

指定したJSON値に対するJSONパス述語チェックの結果を返します。 結果の最初の項目だけが考慮されます。 結果が論理値でなければNULLが返ります。

'{"a":[1,2,3,4,5]}'::jsonb @@ '$.a[*] > 2't


注記

jsonpath演算子の@?および@@演算子は以下のエラーを抑止します。 オブジェクトフィールドあるいは配列要素の欠如、期待しないJSON要素型、日付時刻及び数値エラー。 以下に示すjsonpath関連の関数もこれらのエラーを抑止するようにすることもできます。 この振る舞いは、異なる構造のJSON文書集合を検索する際に役に立つかも知れません。

表 9.46に、json値およびjsonb値を作成するために利用可能な関数を示します。

表9.46 JSON作成関数

関数

説明

to_json ( anyelement ) → json

to_jsonb ( anyelement ) → jsonb

SQL値をjsonあるいはjsonbに変換します。 配列と複合型は再帰的に配列とオブジェクトに変換されます。(多次元配列はJSONにおける配列の配列になります。) それ以外は、そのSQLデータ型からjsonにキャストがあれば、キャスト関数が変換のために用いられます。[a] そうでなければスカラーJSON値が生成されます。 数値、論理値、NULL以外のスカラーには、有効なJSON文字列値にするための必要なエスケープ処理が施されたテキスト表現が使われます。

to_json('Fred said "Hi."'::text)"Fred said \"Hi.\""

to_jsonb(row(42, 'Fred said "Hi."'::text)){"f1": 42, "f2": "Fred said \"Hi.\""}

array_to_json ( anyarray [, boolean ] ) → json

SQL配列をJSON配列に変換します。 追加の論理引数が真であるときに改行がトップレベルの配列要素の間に加えられる以外は、その振る舞いはto_jsonと同じです。

array_to_json('{{1,5},{99,100}}'::int[])[[1,5],[99,100]]

row_to_json ( record [, boolean ] ) → json

SQL複合値をJSONオブジェクトに変換します。 追加の論理引数が真であるときに改行がトップレベルの配列要素の間に加えられる以外は、その振る舞いはto_jsonと同じです。

row_to_json(row(1,'foo')){"f1":1,"f2":"foo"}

json_build_array ( VARIADIC "any" ) → json

jsonb_build_array ( VARIADIC "any" ) → jsonb

異なる型から構成される可能性のあるJSON配列をvariadic引数リストから作成します。 各々の引数はto_jsonあるいはto_jsonbに従って変換されます。

json_build_array(1, 2, 'foo', 4, 5)[1, 2, "foo", 4, 5]

json_build_object ( VARIADIC "any" ) → json

jsonb_build_object ( VARIADIC "any" ) → jsonb

variadic引数リストからJSONオブジェクトを作成します。 慣例により引数リストは代替キーと値が交互に並んだものです。 キー引数はテキストに強制的に変換されます。 値引数はto_jsonあるいはto_jsonbに従って変換されます。

json_build_object('foo', 1, 2, row(3,'bar')){"foo" : 1, "2" : {"f1":3,"f2":"bar"}}

json_object ( text[] ) → json

jsonb_object ( text[] ) → jsonb

テキスト配列からJSONオブジェクトを作成します。 配列は、偶数個の要素からなる1次元(キー/値の対が交互に並んでいるものと扱われます)あるいは内側の配列が2つの要素を持つ2次元(2つの要素がキー/値のペアとして扱われます)のいずれかでなければなりません。 すべての値はJSON文字列に変換されます。

json_object('{a, 1, b, "def", c, 3.5}'){"a" : "1", "b" : "def", "c" : "3.5"}

json_object('{{a, 1}, {b, "def"}, {c, 3.5}}'){"a" : "1", "b" : "def", "c" : "3.5"}

json_object ( keys text[], values text[] ) → json

jsonb_object ( keys text[], values text[] ) → jsonb

この形のjson_objectは2つの別々の配列からキーと値の対を取ります。 他の点ではすべて、引数1つの形と同じです。

json_object('{a,b}', '{1,2}'){"a": "1", "b": "2"}

[a] たとえばhstore拡張にはhstoreからjsonへのキャストがあり、JSON生成関数で変換されたhstore値は、原始的な文字列値としてではなく、JSONオブジェクトとして表示されます。


表 9.47jsonjsonb値を処理するのに使える関数を示します。

表9.47 JSON処理関数

関数

説明

json_array_elements ( json ) → setof json

jsonb_array_elements ( jsonb ) → setof jsonb

トップレベルのJSON配列をJSON値の集合に展開します。

select * from json_array_elements('[1,true, [2,false]]')

   value
-----------
 1
 true
 [2,false]

json_array_elements_text ( json ) → setof text

jsonb_array_elements_text ( jsonb ) → setof text

トップレベルのJSON配列をtext値の集合に展開します。

select * from json_array_elements_text('["foo", "bar"]')

   value
-----------
 foo
 bar

json_array_length ( json ) → integer

jsonb_array_length ( jsonb ) → integer

トップレベルのJSON配列の要素数を返します。

json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]')5

jsonb_array_length('[]')0

json_each ( json ) → setof record ( key text, value json )

jsonb_each ( jsonb ) → setof record ( key text, value jsonb )

トップレベルのJSONオブジェクトをキー/値のペアの集合に展開します。

select * from json_each('{"a":"foo", "b":"bar"}')

 key | value
-----+-------
 a   | "foo"
 b   | "bar"

json_each_text ( json ) → setof record ( key text, value text )

jsonb_each_text ( jsonb ) → setof record ( key text, value text )

トップレベルのJSONオブジェクトをキー/値のペアの集合に展開します。 返り値のvaluetext型です。

select * from json_each_text('{"a":"foo", "b":"bar"}')

 key | value
-----+-------
 a   | foo
 b   | bar

json_extract_path ( from_json json, VARIADIC path_elems text[] ) → json

jsonb_extract_path ( from_json jsonb, VARIADIC path_elems text[] ) → jsonb

指定したパスにおけるJSONの副オブジェクトを取り出します。 (これは#>演算子と機能的に同じですが、パスをvariadicリストで書き出す方がより便利な場合があります。)

json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}', 'f4', 'f6')"foo"

json_extract_path_text ( from_json json, VARIADIC path_elems text[] ) → text

jsonb_extract_path_text ( from_json jsonb, VARIADIC path_elems text[] ) → text

指定したパスにおけるJSONの副オブジェクトをtextとして取り出します。 (これは機能的には#>>演算子と同じです。)

json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}', 'f4', 'f6')foo

json_object_keys ( json ) → setof text

jsonb_object_keys ( jsonb ) → setof text

トップレベルのJSONオブジェクト中のキーの集合を返します。

select * from json_object_keys('{"f1":"abc","f2":{"f3":"a", "f4":"b"}}')

 json_object_keys
------------------
 f1
 f2

json_populate_record ( base anyelement, from_json json ) → anyelement

jsonb_populate_record ( base anyelement, from_json jsonb ) → anyelement

トップレベルのJSONオブジェクトをbase引数である複合型を持つ行に展開します。 JSONオブジェクトは出力行型の列名と一致するフィールドが検査されます。 (出力列名と関連のないフィールドは無視されます。) 典型的な使い方としては、baseの値が単にNULLで、これはオブジェクトフィールドと一致しない出力列にはNULLがセットされることを意味します。 しかし、baseNULLでないなら、それが持つ値が一致しない列に使われます。

JSON値を出力列のSQL型に変換する際に以下のルールが順に適用されます。

  • すべての場合にJSONのNULL値はSQLのNULLに変換されます。

  • 出力列がjson型あるいはjsonb型なら、JSON値は単にそのまま複製されます。

  • 出力行が複合(行)型でJSON値がJSONオブジェクトなら、これらのルールを再帰的に適用することによって、オブジェクトのフィールドが出力行型の列に変換されます。

  • 同様に、出力行が配列型でJSON値がJSON配列なら、これらのルールを再帰的に適用することによって、JSON配列の要素が出力配列の要素に変換されます。

  • それ以外の場合で、JSON値が文字列なら、その文字列の内容が列のデータ型に対応する入力変換関数に送られます。

  • さもなければ、通常のJSON値のテキスト表現が列のデータ型に対応する入力変換関数に送られます。

これらの関数の例ではJSON定数を使用していますが、典型的な使用法はそのjsonまたはjsonb列をFROM句の別のテーブルから外側に参照することです。 FROM句でjson_populate_recordを書くのは良い練習になります。 すべての取り出された列を重複した関数呼び出しなしに利用できるからです。

create type subrowtype as (d int, e text); create type myrowtype as (a int, b text[], c subrowtype);

select * from json_populate_record(null::myrowtype, '{"a": 1, "b": ["2", "a b"], "c": {"d": 4, "e": "a b c"}, "x": "foo"}')

 a |   b       |      c
---+-----------+-------------
 1 | {2,"a b"} | (4,"a b c")

json_populate_recordset ( base anyelement, from_json json ) → setof anyelement

jsonb_populate_recordset ( base anyelement, from_json jsonb ) → setof anyelement

トップレベルのJSONオブジェクトをbase引数である複合型を持つ行の集合に展開します。 JSON配列の個々の要素は上のjson[b]_populate_recordで説明したように処理されます。

create type twoints as (a int, b int);

select * from json_populate_recordset(null::twoints, '[{"a":1,"b":2}, {"a":3,"b":4}]')

 a | b
---+---
 1 | 2
 3 | 4

json_to_record ( json ) → record

jsonb_to_record ( jsonb ) → record

トップレベルのJSONオブジェクトをAS句で定義した複合型を持つ行に展開します。 (recordを返すすべての関数では、呼び出す問い合わせは明示的にAS句でレコードの構造を定義しなければなりません。) 上のjson[b]_populate_recordで説明した方法で、出力レコードはJSONオブジェクトのフィールドで満たされます。 入力レコード値がないので、一致しない列は常にNULLで満たされます。

create type myrowtype as (a int, b text);

select * from json_to_record('{"a":1,"b":[1,2,3],"c":[1,2,3],"e":"bar","r": {"a": 123, "b": "a b c"}}') as x(a int, b text, c int[], d text, r myrowtype)

 a |    b    |    c    | d |       r
---+---------+---------+---+---------------
 1 | [1,2,3] | {1,2,3} |   | (123,"a b c")

json_to_recordset ( json ) → setof record

jsonb_to_recordset ( jsonb ) → setof record

トップレベルのJSON配列をAS句で定義した複合型を持つ行に展開します。 (recordを返すすべての関数では、呼び出す問い合わせは明示的にAS句でレコードの構造を定義しなければなりません。) 上のjson[b]_populate_recordで説明した方法で、JSON配列の要素は処理されます。

select * from json_to_recordset('[{"a":1,"b":"foo"}, {"a":"2","c":"bar"}]') as x(a int, b text)

 a |  b
---+-----
 1 | foo
 2 |

jsonb_set ( target jsonb, path text[], new_value jsonb [, create_if_missing boolean ] ) → jsonb

pathで指定された要素をnew_valueで置き換えてtargetを返します。 create_if_missingが真なら(デフォルトです)、pathで指定された項目が無い時にnew_valueが追加されます。 パス中のすべての初期のステップは存在しなければならず、さもなければtargetは変わらないままに返却されます。 パスの位置についての演算子については、pathの中にある負の整数はJSON配列の終わりから数えます。 パスの最後のステップが範囲外の配列のインデックスで、create_if_missingが真のときは、インデックスが負なら配列の最初に、正なら配列の最後に新しい値が追加されます。

jsonb_set('[{"f1":1,"f2":null},2,null,3]', '{0,f1}', '[2,3,4]', false)[{"f1": [2, 3, 4], "f2": null}, 2, null, 3]

jsonb_set('[{"f1":1,"f2":null},2]', '{0,f3}', '[2,3,4]')[{"f1": 1, "f2": null, "f3": [2, 3, 4]}, 2]

jsonb_set_lax ( target jsonb, path text[], new_value jsonb [, create_if_missing boolean [, null_value_treatment text ]] ) → jsonb

new_valueNULLでないなら、jsonb_setと同じ振る舞いをします。 そうでなければnull_value_treatmentにしたがいます。 null_value_treatmentは、'raise_exception''use_json_null''delete_key''return_target'のいずれかでなければなりません。 デフォルトは'use_json_null'です。

jsonb_set_lax('[{"f1":1,"f2":null},2,null,3]', '{0,f1}', null)[{"f1":null,"f2":null},2,null,3]

jsonb_set_lax('[{"f1":99,"f2":null},2]', '{0,f3}', null, true, 'return_target')[{"f1": 99, "f2": null}, 2]

jsonb_insert ( target jsonb, path text[], new_value jsonb [, insert_after boolean ] ) → jsonb

new_valueを挿入してtargetを返します。 pathで指定した項目が配列要素で、insert_afterが偽(デフォルトです)ならばnew_valueはその項目の前に挿入され、insert_afterが真であれば後に挿入されます。 pathで指定した項目がオブジェクトフィールドならば、オブジェクトがすでにそのキーを含んでいない場合にのみnew_valueが挿入されます。 パス中のすべての初期のステップは存在しなければならず、さもなければtargetは変わらないままに返却されます。 pathについての演算子について言うと、path内の負の整数はJSON配列の終わりから数えます。 パスの最後のステップが範囲外の配列のインデックスで、インデックスが負なら配列の最初に、正なら配列の最後に新しい値が追加されます。

jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"'){"a": [0, "new_value", 1, 2]}

jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"', true){"a": [0, 1, "new_value", 2]}

json_strip_nulls ( json ) → json

jsonb_strip_nulls ( jsonb ) → jsonb

与えられたJSON値からNULLを持つオブジェクトフィールドをすべて削除します。 オブジェクトフィールドではないNULL値は変わりません。

json_strip_nulls('[{"f1":1, "f2":null}, 2, null, 3]')[{"f1":1},2,null,3]

jsonb_path_exists ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → boolean

JSONパスが指定したJSON値に対して項目を返すかどうかをチェックします。 varsが指定されるなら、それはJSONオブジェクトでなければならず、そのフィールドはjsonpath式に置き換えられる名前を持つ値を提供します。 silent引数が指定されていてtrueなら、この関数は@?@@演算子が生成するのと同じエラーを抑止します。

jsonb_path_exists('{"a":[1,2,3,4,5]}', '$.a[*] ? (@ >= $min && @ <= $max)', '{"min":2, "max":4}')t

jsonb_path_match ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → boolean

指定したJSON値のJSONパス述語チェックの結果を返します。 結果の最初の項目だけが考慮されます。 結果がBooleanでないなら、nullが返ります。 オプションのvarssilent引数はjsonb_path_existsと同じように働きます。

jsonb_path_match('{"a":[1,2,3,4,5]}', 'exists($.a[*] ? (@ >= $min && @ <= $max))', '{"min":2, "max":4}')t

jsonb_path_query ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → setof jsonb

JSON値に対するJSONパスによって返されるすべてのJSON項目を返します。 オプションのvarssilent引数はjsonb_path_existsと同じように働きます。

select * from jsonb_path_query('{"a":[1,2,3,4,5]}', '$.a[*] ? (@ >= $min && @ <= $max)', '{"min":2, "max":4}')

 jsonb_path_query
------------------
 2
 3
 4

jsonb_path_query_array ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → jsonb

JSON値に対するJSONパスによって返されるすべてのJSON項目をJSON配列として返します。 オプションのvarssilent引数はjsonb_path_existsと同じように働きます。

jsonb_path_query_array('{"a":[1,2,3,4,5]}', '$.a[*] ? (@ >= $min && @ <= $max)', '{"min":2, "max":4}')[2, 3, 4]

jsonb_path_query_first ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → jsonb

JSON値に対するJSONパスによって返される最初のJSON項目を返します。 結果がなければNULLが返ります。 オプションのvarssilent引数はjsonb_path_existsと同じように働きます。

jsonb_path_query_first('{"a":[1,2,3,4,5]}', '$.a[*] ? (@ >= $min && @ <= $max)', '{"min":2, "max":4}')2

jsonb_path_exists_tz ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → boolean

jsonb_path_match_tz ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → boolean

jsonb_path_query_tz ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → setof jsonb

jsonb_path_query_array_tz ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → jsonb

jsonb_path_query_first_tz ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → jsonb

これらの関数は、時間帯を考慮する日時値の比較をサポートすることを除いて、上で述べた、_tz接尾を除いた片割れの関数のように動作します。 以下の例では日付のみの値2015-08-02を時間帯付きタイムスタンプとして解釈することが必要で、結果はTimeZone設定に依存します。 この依存性のために、これらの関数は安定(stable)、として印付けされており、インデックスにはこれらの関数は使えないことを意味します。 これらの関数の片割れは不変(immutable)なので、インデックスで使えます。しかし、そうした比較を要求されるとエラーを吐きます。

jsonb_path_exists_tz('["2015-08-01 12:00:00 -05"]', '$[*] ? (@.datetime() < "2015-08-02".datetime())')t

jsonb_pretty ( jsonb ) → text

与えられたJSON値を整形されたインデント付きテキストに変換します。

jsonb_pretty('[{"f1":1,"f2":null}, 2]')

[
    {
        "f1": 1,
        "f2": null
    },
    2
]

json_typeof ( json ) → text

jsonb_typeof ( jsonb ) → text

トップレベルのJSON値の型をテキスト文字列として返します。 可能な型は次のとおりです。 objectarraystringnumberbooleannull。 (nullの結果をSQLのNULLと混同してはいけません。以下の例をご覧ください。)

json_typeof('-123.4')number

json_typeof('null'::json)null

json_typeof(NULL::json) IS NULLt


レコードの値をJSONに集約するjson_agg集約関数、値の対をJSONオブジェクトに集約するjson_object_agg集約関数、およびそれらのjsonb版のjsonb_aggjsonb_object_aggについては9.21を参照して下さい。

9.16.2. SQL/JSONパス言語

SQL/JSONパス式は、XMLへのSQLアクセスで使用されるXPath同様、JSONデータから取り出す項目を指定します。 PostgreSQLではパス式はjsonpathデータ型として実装されており、8.14.7で説明されているすべての要素を使うことができます。

JSON問い合わせ関数と演算子は与えられたパス式をpath engineに渡して評価します。 式が問い合わせ対象のJSONデータにマッチすれば、関連するSQL/JSON項目が返却されます。 パス式はSQL/JSONパス言語で書かれ、算術式と関数を含むことができます。

パス式はjsonpathデータ型で認められた一連の要素からなります。 パス式は通常左から右へと評価されますが、括弧を使って演算の順序を変更することができます。 評価が成功すれば、一連のJSON項目が生成され、評価結果が指定した計算を完了したJSON問い合わせ関数に戻されます。

問い合わせ対象(context item)のJSONデータを参照するには、パス式内で$値を使います。 複数のアクセサ演算子をその後に記述することもできます。 それによってJSON構造をレベル順に訪れて文脈項目の副項目の内容を取り出します。 後続の個々の演算子はその前の評価段階の結果を処理します。

たとえば、次のようなパースしたいGPSトラッカーからのJSONデータがあるとします。

{
  "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
      }
    ]
  }
}

存在するトラックセグメントを取り出すには、.keyアクセサ演算子を使用して、周辺のJSONオブジェクトを下っていく必要があります。

$.track.segments

配列の内容を取り出すには、典型的には[*]演算子を使います。 たとえば次のパスはすべての存在するトラックセグメントの位置座標を返します。

$.track.segments[*].location

最初のセグメントの座標だけを返すには、[]アクセサ演算子の中で対応する添え字を指定することができます。 JSON配列インデックスは0スタートであることに注意してください。

$.track.segments[0].location

各段階でのパス評価結果は9.16.2.2に列挙されている一つ以上のjsonpath演算子とメソッドで処理することができます。 各々のメソッド名の前にピリオドを付けなければなりません。 たとえば配列の大きさを得ることができます。

$.track.segments.size()

パス式内のjsonpath演算子とメソッドを使用する他の例については以下の9.16.2.2を参照してください。

パスを定義する際にはSQLのWHERE節のように働く一つ以上のフィルター式が利用できます。 フィルター式はクェスチョンマークで始まり、カッコ内に条件を記述します。

? (condition)

フィルター式はそれを適用するパス評価段階の直後に指定しなければなりません。 この段階の結果は、指定した条件を満たす項目だけが含まれるようにフィルターされます。 SQL/JSONは3値論理を定義しており、条件はtruefalseunknownのどれかです。 unknownは値はSQLのNULLと同じ役割を果たし、is unknown述語で評価できます。 その後の評価段階ではtrueを返すフィルター式に対応する項目だけが使われます。

フィルター式内で利用できる関数と演算子は表 9.49にリストされています。 フィルター式内では、フィルターする必要のある値は@変数で示します。(つまり以前のパスステップの結果の一つです。) コンポーネント項目を取得するためにアクセサ演算子を@の後に記述することができます。

たとえば130より高いすべての心拍数を取り出したいとします。次の式を使ってそれを得ることができます。

$.track.segments[*].HR ? (@ > 130)

そうした値を持つセグメントの開始時刻を得たい場合は、開始時刻を返す前に無関係のセグメントを取り除く必要があります。 そうすることにより前の段階にフィルター式が適用されるので、その条件で適用されるパスは異なります。

$.track.segments[*] ? (@.HR > 130)."start time"

必要なら複数のフィルター式を順に使用することができます。 たとえば次の式は指定した座標と高い心拍数値を持つ位置を持つすべてのセグメントを選択します。

$.track.segments[*] ? (@.location[1] < 13.4) ? (@.HR > 130)."start time"

異なる入れ子レベルに対してフィルター式を適用することもできます。 次の例では、まず位置ですべてのセグメントをフィルターし、もしあれば高い心拍数値を返します。

$.track.segments[*] ? (@.location[1] < 13.4).HR ? (@ > 130)

フィルター式をお互いに入れ子にすることもできます。

$.track ? (exists(@.segments[*] ? (@.HR > 130))).segments.size()

この式は高い心拍数値を含むトラックがあればそのすべてのサイズを返します。もしなければ空のシーケンスが返ります。

PostgreSQLのSQL/JSONパス言語の実装はSQL/JSON標準と次の点が異なります。

  • SQL/JSON標準ではフィルター内でのみ述語が使えますが、パス式はBoolean述語でも構いません。 これは@@演算子を実装するために必要です。 たとえば、次のjsonpath式はPostgreSQLでは有効です。

    $.track.segments[*].HR < 70
    

  • 9.16.2.3で述べるように、like_regexフィルターで使用される正規表現パターンの解釈には些細な違いがあります。

9.16.2.1. 厳密モードと非厳密モード

JSONデータを問い合わせる際、パス式は実際のJSONデータ構造に一致しないかも知れません。 存在しないオブジェクトのメンバーあるいは配列要素にアクセスしようとすると、構造上のエラーとなります。 SQL/JSONパス式には構造上のエラーを扱うための2つのモードがあります。

  • 非厳密(lax)モード(デフォルト)— パスエンジンは指定したパスを問い合わせデータに暗黙的に適合させます。 構造上のエラーは抑止され、空のSQL/JSONシーケンスへと変換されます。

  • 厳密(strict)モード — 構造上のエラーがあるとエラーが発生します。

非厳密モードは、JSONデータが期待されるスキーマに沿わないときにJSON文書構造とパス式のマッチングを助けます。 あるオペランドが操作の要件に合わないときにはそれをSQL/JSON配列にまとめたり、あるいは操作を行う前にそれをSQL/JSONシーケンスに展開することもできます。 また非厳密モードにおいては、比較演算子は自動的にオペランドを展開し、SQL/JSON配列をそのまま比較することができます。 大きさ1の配列はその単独要素と同じものとして扱われます。 自動展開は以下の場合にのみ行われません。

  • それぞれ配列の型、要素数を返すtype()size()をパス式が含む。

  • 問い合わせ対象のJSONデータが入れ子の配列を含む。 この場合はもっとも外側の配列のみが展開され、内側の配列は変わりません。 ですから、それぞれの評価段階において1レベルのみに暗黙的な展開が行われます。

たとえば、上述のGPSデータに問い合わせする際、非厳密モードでは配列のセグメントを含んでいることを抽象化できます。

lax $.track.segments.location

厳密モードでは、指定したパスはSQL/JSON項目を返す問い合わせ対象のJSON文書の構造に正確に一致していなければなりません。ですから、このパス式を使うとエラーになります。 非厳密モードと同じ結果を得るためには、segments配列を明示的に展開する必要があります。

strict $.track.segments[*].location

.**アクセサを非厳密モードで使うと驚くべき結果をもたらすことがあります。 たとえば、次の問い合わせはすべてのHR値を2回検索します。

lax $.**.HR

これは.**アクセサがsegmentsとその各々の要素の両方を検索するからです。 一方、.HRアクセサは非厳密モードでは自動的に配列を展開します。 予期しない結果を避けるには、.**アクセサを厳密モードでのみ使うことをお勧めします。 次の問い合わせはHRの各値を一度だけ検索します。

strict $.**.HR

9.16.2.2. SQL/JSONパス演算子とメソッド

表 9.48jsonpathで利用可能な演算子とメソッドを示します。 単項演算子とメソッドは以前のパスステップから生じた複数の値に適用できますが、二項演算子(加算など)は単一の値にしか適用できないことに注意してください。

表9.48 jsonpath演算子とメソッド

演算子/メソッド

説明

number + numbernumber

加算

jsonb_path_query('[2]', '$[0] + 3')5

+ numbernumber

単項のプラス(演算なし)。加算と違って、複数の値に渡って適用できます。

jsonb_path_query_array('{"x": [2,3,4]}', '+ $.x')[2, 3, 4]

number - numbernumber

減算

jsonb_path_query('[2]', '7 - $[0]')5

- numbernumber

負符号。減算と違って、複数の値に渡って適用できます。

jsonb_path_query_array('{"x": [2,3,4]}', '- $.x')[-2, -3, -4]

number * numbernumber

乗算

jsonb_path_query('[4]', '2 * $[0]')8

number / numbernumber

除算

jsonb_path_query('[8.5]', '$[0] / 2')4.2500000000000000

number % numbernumber

剰余(残り)

jsonb_path_query('[32]', '$[0] % 10')2

value . type()string

JSON項目の型(json_typeofを参照)

jsonb_path_query_array('[1, "2", {}]', '$[*].type()')["number", "string", "object"]

value . size()number

JSON項目の大きさ(配列の要素数。配列でなければ1)

jsonb_path_query('{"m": [11, 15]}', '$.m.size()')2

value . double()number

JSON数値あるいは文字列から変換した概算の浮動小数点数

jsonb_path_query('{"len": "1.9"}', '$.len.double() * 2')3.8

number . ceiling()number

引数より大きいか等しく、与えられた数に最も近い整数

jsonb_path_query('{"h": 1.3}', '$.h.ceiling()')2

number . floor()number

引数より小さいか等しく、与えられた数に最も近い整数

jsonb_path_query('{"h": 1.7}', '$.h.floor()')1

number . abs()number

与えられた数の絶対値

jsonb_path_query('{"z": -0.3}', '$.z.abs()')0.3

string . datetime()datetime_type (see note)

文字列から変換した日時値

jsonb_path_query('["2015-8-1", "2015-08-12"]', '$[*] ? (@.datetime() < "2015-08-2".datetime())')"2015-8-1"

string . datetime(template)datetime_type (see note)

指定のto_timestampテンプレートを使って文字列から変換した日時値

jsonb_path_query_array('["12:30", "18:40"]', '$[*].datetime("HH24:MI")')["12:30:00", "18:40:00"]

object . keyvalue()array

以下の3つのフィールドを含むオブジェクトの配列で表現したオブジェクトのキー/値ペア。 "key""value""id""id"はキー/値ペアが属するオブジェクトのユニーク識別子です。

jsonb_path_query_array('{"x": "20", "y": 32}', '$.keyvalue()')[{"id": 0, "key": "x", "value": "20"}, {"id": 0, "key": "y", "value": 32}]


注記

datetime()datetime(template)の結果型はdatetimetztimetimestamptz、あるいはtimestampです。 両方のメソッドは結果型を動的に決定します。

datetime()メソッドは入力文字列をdatetimetztimetimestamptztimestampのISO形式に対して順にマッチを試みます。 最初にマッチした形式で停止し、関連するデータ型を出力します。

datetime(template)メソッドは与えられたテンプレート文字列にあるフィールドに従って結果型を決定します。

datetime()datetime(template)to_timestampSQL関数と同じ解析ルール(参照9.8)を用いますが、3つの例外があります。 一番目に、これらのメソッドは一致しないテンプレートパターンを許容しません。二番目に次の区切り文字のみを許容します。負符号、ピリオド、斜線(スラッシュ)、カンマ、アポストロフィー、セミコロン、コロン、空白、です。 三番目にテンプレート文字列中の区切り文字は正確に入力文字列と一致しなければなりません。

異なる日時型の比較が必要なら、暗黙的なキャストが適用されます。 date値はtimestampあるいはtimestamptzにキャストできます。 timestamptimestamptzに、timetimetzにキャストできます。 しかし、これらの変換の最初のものは現在のTimeZone設定に依存します。ですから時間帯を認識するjsonpath関数中でのみ実行可能です。

表 9.49に利用可能なフィルター式要素を示します。

表9.49 jsonpathフィルター式要素

述語/値

説明

value == valueboolean

等値比較(これと他の比較演算子はすべてのJSONスカラー値で使えます)

jsonb_path_query_array('[1, "a", 1, 3]', '$[*] ? (@ == 1)')[1, 1]

jsonb_path_query_array('[1, "a", 1, 3]', '$[*] ? (@ == "a")')["a"]

value != valueboolean

value <> valueboolean

非等値比較

jsonb_path_query_array('[1, 2, 1, 3]', '$[*] ? (@ != 1)')[2, 3]

jsonb_path_query_array('["a", "b", "c"]', '$[*] ? (@ <> "b")')["a", "c"]

value < valueboolean

未満比較

jsonb_path_query_array('[1, 2, 3]', '$[*] ? (@ < 2)')[1]

value <= valueboolean

以下比較

jsonb_path_query_array('["a", "b", "c"]', '$[*] ? (@ <= "b")')["a", "b"]

value > valueboolean

より大きい比較

jsonb_path_query_array('[1, 2, 3]', '$[*] ? (@ > 2)')[3]

value >= valueboolean

以上比較

jsonb_path_query_array('[1, 2, 3]', '$[*] ? (@ >= 2)')[2, 3]

trueboolean

JSON定数

jsonb_path_query('[{"name": "John", "parent": false}, {"name": "Chris", "parent": true}]', '$[*] ? (@.parent == true)'){"name": "Chris", "parent": true}

falseboolean

JSON定数

jsonb_path_query('[{"name": "John", "parent": false}, {"name": "Chris", "parent": true}]', '$[*] ? (@.parent == false)'){"name": "John", "parent": false}

nullvalue

JSON定数null(SQLとは違ってnullとの比較は通常通り動作することに注意してください。)

jsonb_path_query('[{"name": "Mary", "job": null}, {"name": "Michael", "job": "driver"}]', '$[*] ? (@.job == null) .name')"Mary"

boolean && booleanboolean

論理AND

jsonb_path_query('[1, 3, 7]', '$[*] ? (@ > 1 && @ < 5)')3

boolean || booleanboolean

論理OR

jsonb_path_query('[1, 3, 7]', '$[*] ? (@ < 1 || @ > 5)')7

! booleanboolean

論理NOT

jsonb_path_query('[1, 3, 7]', '$[*] ? (!(@ < 5))')7

boolean is unknownboolean

論理条件がunknownであるかどうかを検査します。

jsonb_path_query('[-1, 2, 7, "foo"]', '$[*] ? ((@ > 0) is unknown)')"foo"

string like_regex string [ flag string ] → boolean

最初のオペランドが2番目のオペランドで与えられる正規表現にマッチするかどうか検査します。 オプションでflag文字列で記述される変更を伴います。(9.16.2.3を参照してください。)

jsonb_path_query_array('["abc", "abd", "aBdC", "abdacb", "babc"]', '$[*] ? (@ like_regex "^ab.*c")')["abc", "abdacb"]

jsonb_path_query_array('["abc", "abd", "aBdC", "abdacb", "babc"]', '$[*] ? (@ like_regex "^ab.*c" flag "i")')["abc", "aBdC", "abdacb"]

string starts with stringboolean

2番目の文字列が1番目のオペランドの最初の部分文字列かどうかを検査します。

jsonb_path_query('["John Smith", "Mary Stone", "Bob Johnson"]', '$[*] ? (@ starts with "John")')"John Smith"

exists ( path_expression )boolean

パス式が少なくとも一つのSQL/JSON項目とマッチするかどうかを検査します。 パス式がエラーとなる場合はunknownを返します。2番目の例は厳密モードでキーが存在しないエラーを回避するためにこれを使っています。

jsonb_path_query('{"x": [1, 2], "y": [2, 4]}', 'strict $.* ? (exists (@ ? (@[*] > 2)))')[2, 4]

jsonb_path_query_array('{"value": 41}', 'strict $ ? (exists (@.name)) .name')[]


9.16.2.3. SQL/JSON正規表現

SQL/JSONパス式ではlike_regexフィルターを使ってテキストを正規表現にマッチさせることができます。 たとえば、次のSQL/JSONパス式問い合わせは、英語の母音で始まる配列内のすべての文字列に大文字小文字を無視してマッチするでしょう。

$[*] ? (@ like_regex "^[aeiou]" flag "i")

オプションのflag文字列は一つ以上の文字を含むことができます。 iは大文字小文字を無視したマッチ、m^$で改行にマッチ、s.が改行にマッチ、qはパターン全体を参照します。(振る舞いを単純な部分文字列マッチとします)

SQL/JSON標準は正規表現の定義を、XQuery標準を使用するLIKE_REGEX演算子から借りています。 PostgreSQLは今の所LIKE_REGEX演算子をサポートしていません。 ですから、like_regexフィルターは9.7.3で説明されているPOSIX正規表現で実装されています。 このことにより、9.7.3.8で列挙されているSQL/JSON標準の振る舞いとの小さな違いが生じます。 しかし、ここで述べているフラグ文字の非互換性はSQL/JSONには適用されないことに注意してください。SQL/JSONは、XQueryのフラグ文字をPOSIXエンジンが期待するのと一致するように解釈するからです。

like_regexのパターン引数は8.14.7で説明されているルールにしたがって書かれたJSONパス文字列リテラルであることに注意してください。 これは、正規表現で使用するすべてのバックスラッシュを二重に書かなければならないことを意味します。 たとえば、数字のみを含むroot文書の文字列値にマッチさせるには以下のようにします。

$.* ? (@ like_regex "^\\d+$")