PostgreSQL 11+
Pokud již používáte PostgreSQL v. 11 (kvůli nový JSONB
podpora konverze typu
) nejlepší by pravděpodobně byla vlastní funkce napsaná v Perlu nebo Pythonu.
Protože preferuji Python 3, zde je funkční příklad:
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
$$;
...který pak lze použít následovně:
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"}]';
Takže ano, podmínka je duplikovaná, ale pouze za účelem omezení počtu řádků, kterých se lze dotknout.
Chcete-li skutečně pracovat na jednoduché instalaci PostgreSQL 11, potřebujete rozšíření plpython3u
a jsonb_plpython3u
:
CREATE EXTENSION plpython3u;
CREATE EXTENSION jsonb_plpython3u;
Logika Pythonu vysvětlila:
for e in path_to_array:
tmp = tmp[e]
...dostaneme se k řadě položek, na které se musíme podívat.
for item in tmp:
if (entry_filters is None or entry_filters.items() <= item.items()):
item.update(replacement)
...pro každou položku v poli zkontrolujeme, zda je kritérium filtru null
(entry_filters is None
=odpovídá libovolné položce) nebo zda položka „obsahuje“ poskytnutý příklad včetně klíčů a hodnot (entry_filters.items() <= item.items()
).
Pokud se položka en shoduje, přepište/přidejte obsah poskytnutou náhradou.
Doufám, že to jde směrem, který hledáte.
Když se podíváme na současné možnosti PostgreSQL související s modifikací JSON, bylo by to velmi složité (ne-li komplikované) a znamenalo by to spoustu režie, aby bylo možné totéž udělat s čistým SQL.
PostgreSQL 9.6+
V případě, že ještě nemáte k dispozici verzi 11, následující funkce udělá totéž na úkor zpracování převodů typů sama, ale zachová ji zcela kompatibilní s API, takže po upgradu budete muset udělat jedinou věc je nahradit funkci (nevyžaduje se žádná změna příkazů používajících tuto funkci):
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)
$$;