PostgreSQL 11+
Om du redan är på PostgreSQL v. 11 (på grund av ny JSONB
typ konverteringsstöd
) din bästa insats skulle förmodligen vara en anpassad funktion skriven i Perl eller Python.
Eftersom jag föredrar Python 3, här är ett funktionellt exempel:
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
$$;
...som sedan kan användas enligt följande:
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"}]';
Så ja, villkoret är duplicerat, men bara för att begränsa antalet rader som ska röras i första hand.
För att faktiskt arbeta med en vanlig PostgreSQL 11-installation behöver du tilläggen plpython3u
och jsonb_plpython3u
:
CREATE EXTENSION plpython3u;
CREATE EXTENSION jsonb_plpython3u;
Python-logiken förklarade:
for e in path_to_array:
tmp = tmp[e]
...får oss ner till den mängd poster vi behöver titta på.
for item in tmp:
if (entry_filters is None or entry_filters.items() <= item.items()):
item.update(replacement)
...för varje objekt i arrayen kontrollerar vi om filterkriteriet är null
(entry_filters is None
=matcha valfri post) eller om posten "innehåller" det angivna exemplet inklusive nycklar och värden (entry_filters.items() <= item.items()
).
Om en post matchar, skriv över/lägg till innehåll med den medföljande ersättningen.
Jag hoppas att det går i den riktning du letar efter.
Om man tittar på de nuvarande funktionerna hos PostgreSQL relaterade till JSON-modifiering skulle det vara mycket komplext (om inte komplicerat) och introducera en hel del overhead för att göra samma sak med ren SQL.
PostgreSQL 9.6+
Om du inte har version 11 tillgänglig än, kommer följande funktion att göra detsamma på bekostnad av att hantera typkonverteringarna av sig själv men hålla den helt API-kompatibel, så du när du uppgraderar är det enda du behöver göra är att ersätta funktionen (ingen ändring av några satser som använder denna funktion krävs):
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)
$$;