PostgreSQLのJSON []配列から既知の要素を削除する方法は?


8

PostgreSQLでのJSONデータ型の使用に関する問題に直面しています。非正規化されたJavaモデルをDBに格納しようとしています。モデルは複雑なオブジェクトのリストを備えています。そのため、これらをネイティブのPostgreSQL配列でJSONとしてモデル化することにしました。

これは私のテーブル作成ステートメントの抜粋です:

CREATE TABLE test.persons
(
  id UUID,
  firstName TEXT,
  lastName TEXT,
  communicationData JSON[],
  CONSTRAINT pk_person PRIMARY KEY (id)
);

ご覧のとおり、それはJSONで通信データオブジェクトのリストを備えた人物です。そのようなオブジェクトの1つは次のようになります。

{"value" : "03334/254147", "typeId" : "ea4e7d7e-7b87-4628-ba50-6a5f6e63dbf6"}

PostgreSQLのarray_appendを使用して、このようなJSONオブジェクトを配列に簡単に追加できます。ただし、既知の値をアレイから削除できません。次のSQLステートメントについて考えてみます。

UPDATE test.persons
SET communicationData = array_remove(
      communicationData, 
      '{"value" : "03334/254147", "typeId" : "ea4e7d7e-7b87-4628-ba50-6a5f6e63dbf6"}'::JSON
    )
WHERE id = 'f671eb6a-d603-11e3-bf6f-07ba007d953d';

これはで失敗しERROR: could not identify an equality operator for type jsonます。JSON配列から既知の値を削除する方法についてのヒントはありますか?また、配列内の位置によって削除することもできます。これも知っています...

PostgreSQLのバージョンは9.3.4です。

回答:


11

jsonb Postgres 9.4以降

Postgres 9.4のjsonbデータ型を検討してください-「バイナリ」の「b」。特に、には等価演算子があります。ほとんどの人は切り替えたくなるでしょう。=jsonb

jsonbに関するDepeszブログ。

json

値全体の同等性を確立するための明確に定義された方法がないため=、データ型に定義された演算子はありません。ただし、以下を参照してください。jsonjson

あなたは可能性にキャストtextしてから使用し=作業を。これは短いですが、テキスト表現がたまたま一致する場合にのみ機能します。コーナーケースを除いて、本質的に信頼できません。見る:

またはunnest、配列を作成して->>演算子を使用し、get JSON object field as text個々のフィールドを比較することができます。

テストテーブル

2行:最初の質問のようなもの、2番目の行は単純な値。

CREATE TABLE tbl (
   tbl_id int PRIMARY KEY
 , jar    json[]
);

INSERT INTO t VALUES
   (1, '{"{\"value\" : \"03334/254146\", \"typeId\" : \"ea4e7d7e-7b87-4628-ba50-f5\"}"
        ,"{\"value\" : \"03334/254147\", \"typeId\" : \"ea4e7d7e-7b87-4628-ba50-f6\"}"
        ,"{\"value\" : \"03334/254148\", \"typeId\" : \"ea4e7d7e-7b87-4628-ba50-f7\"}"}')

 , (2, '{"{\"value\" : \"a\", \"typeId\" : \"x\"}"
        ,"{\"value\" : \"b\", \"typeId\" : \"y\"}"
        ,"{\"value\" : \"c\", \"typeId\" : \"z\"}"}');

デモ

デモ1:あなたは可能性が使用array_remove()してtext表現(信頼できません)。

SELECT tbl_id
     , jar, array_length(jar, 1) AS jar_len
     , jar::text[] AS t, array_length(jar::text[], 1) AS t_len
     , array_remove(jar::text[], '{"value" : "03334/254147", "typeId" : "ea4e7d7e-7b87-4628-ba50-f6"}'::text) AS t_result
     , array_remove(jar::text[], '{"value" : "03334/254147", "typeId" : "ea4e7d7e-7b87-4628-ba50-f6"}'::text)::json[] AS j_result
FROM   tbl;

デモ2:個々の要素の配列とテストフィールドのネストを解除します。

SELECT tbl_id, array_agg(j) AS j_new
FROM   tbl, unnest(jar) AS j   -- LATERAL JOIN
WHERE  j->>'value' <> '03334/254146'
AND    j->>'typeId' <> 'ea4e7d7e-7b87-4628-ba50-6a5f6e63dbf5'
GROUP  BY 1;

デモ3:行タイプを使用した代替テスト。

SELECT tbl_id, array_agg(j) AS j_new
FROM   tbl, unnest(jar) AS j   -- LATERAL JOIN
WHERE  (j->>'value', j->>'typeId') NOT IN (
         ('03334/254146', 'ea4e7d7e-7b87-4628-ba50-6a5f6e63dbf5')
        ,('a', 'x')
       )
GROUP  BY 1;

UPDATE 要求通り

最後に、これは次のように実装できますUPDATE

UPDATE tbl t
SET    jar = j.jar
FROM   tbl t1
CROSS  JOIN LATERAL (
   SELECT ARRAY(
      SELECT j
      FROM   unnest(t1.jar) AS j  -- LATERAL JOIN
      WHERE  j->>'value'  <> 'a'
      AND    j->>'typeId' <> 'x'
      ) AS jar
   ) j
WHERE  t1.tbl_id = 2              -- only relevant rows
AND    t1.tbl_id = t.tbl_id;

ここに db <> fiddle

暗黙についてLATERAL JOIN

配列のネスト解除について:

DB設計

状況を単純化するために、正規化されたスキーマを検討しjsonます。メインのテーブルとの1:1の関係で結合された、値(配列列ではなく)の個別のテーブルです。


それは魅力のように働きます。はい、正規化されたデータの方が簡単ですが、私は98%読み取り、2%書き込みのシナリオにいます。だから私は非正規化を試してみたかった:-) Postgres 9.4で元の質問に役立つかもしれない何か関連する予定はありますか?
スパ

@spa:実際、Postgres 9.4はをもたらしjsonbます。きっと気に入ると思います。リンク付きの章を追加しました。
Erwin Brandstetter、2014年

かっこいい。ヘッドアップをありがとう。
スパ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.