web-dev-qa-db-ja.com

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

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オブジェクトを配列に簡単に追加できます。ただし、既知の値をアレイから削除できません。 f.e.この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です。

8
spa

Postgres 9.4以降のjsonb

jsonb data type in Postgres 9.4-'b' for 'binary'について考えます。特に、jsonb等価演算子_=_があります があります。ほとんどの人は切り替えたくなるでしょう。

jsonbに関するDepeszブログ

json

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

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

または、 unnest 配列にして、 _->>_ operator to .. _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:あなたcouldtext表現でarray_remove()を使用します(信頼できません)。

_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 <>フィドル ここ

暗黙の_LATERAL JOIN_について:

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

DB設計

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

11