PostgreSQL 11+
すでにPostgreSQLv。11を使用している場合(新しいJSONB
型変換のサポート
)最善の策は、おそらくPerlまたはPythonで記述されたカスタム関数でしょう。
私はPython3を好むので、ここに機能的な例があります:
CREATE OR REPLACE FUNCTION jsonb_replace_in_array (val jsonb, path_to_array text[], replacement jsonb, entry_filters jsonb)
RETURNS jsonb
TRANSFORM FOR TYPE jsonb
LANGUAGE plpython3u
AS $$
v_new = val
tmp = v_new
for e in path_to_array:
tmp = tmp[e]
for item in tmp:
if (entry_filters is None or entry_filters.items() <= item.items()):
item.update(replacement)
return v_new
$$;
...次のように使用できます:
UPDATE configuration
SET
config = jsonb_replace_in_array(
config,
'{data}',
'{"value":"changed"}'::jsonb,
'{"oid":"11.5.15.1.4","instance":"1.1.4"}'::jsonb
)
WHERE config->'data' @> '[{"oid":"11.5.15.1.4","instance":"1.1.4"}]';
そうです、条件は重複していますが、最初に触れる行の数を制限するためだけです。
プレーンなPostgreSQL11インストールで実際に機能するには、拡張機能plpython3u
が必要です。 およびjsonb_plpython3u
:
CREATE EXTENSION plpython3u;
CREATE EXTENSION jsonb_plpython3u;
Pythonロジックの説明:
for e in path_to_array:
tmp = tmp[e]
...確認する必要のあるエントリの配列にたどり着きます。
for item in tmp:
if (entry_filters is None or entry_filters.items() <= item.items()):
item.update(replacement)
...配列内の各アイテムについて、フィルター基準がnull
であるかどうかを確認します (entry_filters is None
=任意のエントリに一致)またはエントリがキーと値を含む提供された例を「含む」かどうか(entry_filters.items() <= item.items()
。
enエントリが一致する場合は、提供された置換でコンテンツを上書き/追加します。
それがあなたが探している方向に進むことを願っています。
JSON変更に関連するPostgreSQLの現在の機能を見ると、非常に複雑であり(複雑ではないにしても)、純粋なSQLで同じことを行うには多くのオーバーヘッドが発生します。
PostgreSQL9.6以降
バージョン11がまだ利用できない場合、次の関数は型変換自体を処理することを犠牲にして同じことを行いますが、完全にAPI互換性を維持するため、アップグレードしたら、必要なことは1つだけです。関数を置き換えます(この関数を使用するステートメントを変更する必要はありません):
CREATE OR REPLACE FUNCTION jsonb_replace_in_array (val jsonb, path_to_array text[], replacement jsonb, entry_filters jsonb)
RETURNS jsonb
LANGUAGE plpython3u
AS $$
import json
v_new = json.loads(val)
t_replace = json.loads(replacement)
t_filters = json.loads(entry_filters)
tmp = v_new
for e in path_to_array:
tmp = tmp[e]
for item in tmp:
if (entry_filters is None or t_filters.items() <= item.items()):
item.update(t_replace)
return json.dumps(v_new)
$$;