web-dev-qa-db-ja.com

レコードのJSONB配列でキーを含む行を検索する

オブジェクトの配列に存在するキーを照会しようとしています。この構造:

column jdata
{"name": "Somedata",
 "array": [ {"name":"bla1", "attr": "somevalue"}, 
            {"name":"bla2", "otherdata": "somevalue2"},
            {"name":"bla3", "otherdata": "somevalue"}
           ],
"otherstuff": "stuff"
}

今、私はjdata->'name'または(jdata->'datetime')::castでbtreeを実行します。

また、jdata->'array' @> '[{"name":"bla3"}]'が本当に魅力的なjson_path_opsを実行します。

私の問題は、attrキーが配列内の任意のオブジェクトにある可能性があることです。キーが存在する場合は、レコードを気にしますが、値はほとんど何でもかまいません。これを照会する方法はありますか?インデックスを付ける方法はありますか? jdata->'array' @> '[{"attr": ?}]'を実行したいですか、それとも? 'attr'を内部で使用して配列を作成できますか?

現在、キーをスキャンしてからtrueまたはfalseなどのヘッダーに移動するトリガーを考えています。その後、通常のbtreeが機能します。もっと良い方法はありますか?この値を追加するには、平均的なサイトで約50万件のレコードを編集する必要があります。

方向を教えてください。

5
Kobus

その特定のユースケースは、現在(10ページ)組み込み演算子のプレーンインデックスではカバーされていません。

インデックスをサポートしない単純なクエリ

SELECT *
FROM   tbl
WHERE  EXISTS (
   SELECT 1
   FROM   jsonb_array_elements(jdata->'array') elem
   WHERE  elem ? 'attr' 
   );

EXISTSは、複数の配列要素にキーを含めることができる場合でも、各修飾行を1回必要とするためです。そして、それはより高速です。
しかし、このクエリはインデックスを使用できません。

式インデックス

レコードの指定されたjsonb配列で一意のキーのテキスト配列を生成し、式を単純なIMMUTABLE関数にラップできます。

CREATE OR REPLACE FUNCTION jsonb_arr_record_keys(jsonb)
  RETURNS text[] LANGUAGE sql IMMUTABLE AS
'SELECT ARRAY (
   SELECT DISTINCT k
   FROM   jsonb_array_elements($1) elem, jsonb_object_keys(elem) k
   )';

COMMENT ON FUNCTION jsonb_arr_record_keys(jsonb) IS '
   Generates text array of unique keys in jsonb array of records.
   Fails if any array element is not a record!';

次に、この関数に基づいてGIN式インデックスを作成します。

CREATE INDEX tbl_special_idx ON tbl USING gin (jsonb_arr_record_keys(jdata->'array'));

このようなクエリは、ジェネリック配列を使用して、演算子@>を含みます。

SELECT *
FROM   tbl
WHERE  jsonb_arr_record_keys(jdata->'array') @> '{attr}';

これで、インデックスを効率的に使用できます。

配列にネストされたキー名を提供します('{attr}')。 (この方法で複数のキーを簡単にチェックできます('{attr1, attr2}')または同様...)

dbfiddle ここ

関連:

5

インデックスを付ける方法はありますか?

はい、ただし、PostgreSQLはそのようなJSON配列を使用できないため、構造を簡略化する関数を作成する必要があります。次に、その関数にインデックスを付けることができます。

1
Evan Carroll