PostgreSQL 11+
Jeśli jesteś już na PostgreSQL v. 11 (z powodu nowy JSONB
obsługa konwersji typów
) najlepszym rozwiązaniem byłaby prawdopodobnie niestandardowa funkcja napisana w Perlu lub Pythonie.
Ponieważ wolę Pythona 3, oto funkcjonalny przykład:
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
$$;
...które następnie można wykorzystać w następujący sposób:
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"}]';
Więc tak, warunek jest zduplikowany, ale tylko w celu ograniczenia liczby wierszy do dotknięcia w pierwszej kolejności.
Aby faktycznie pracować na zwykłej instalacji PostgreSQL 11, potrzebujesz rozszerzeń plpython3u
i jsonb_plpython3u
:
CREATE EXTENSION plpython3u;
CREATE EXTENSION jsonb_plpython3u;
Wyjaśnienie logiki Pythona:
for e in path_to_array:
tmp = tmp[e]
...sprowadza nas do tablicy wpisów, które musimy przejrzeć.
for item in tmp:
if (entry_filters is None or entry_filters.items() <= item.items()):
item.update(replacement)
...dla każdego elementu w tablicy sprawdzamy, czy kryteria filtrowania są null
(entry_filters is None
=pasuje do dowolnego wpisu) lub czy wpis "zawiera" podany przykład, w tym klucze i wartości (entry_filters.items() <= item.items()
).
Jeśli wpis pasuje, nadpisz/dodaj zawartość dostarczonym zamiennikiem.
Mam nadzieję, że to idzie w kierunku, w którym szukasz.
Patrząc na obecne możliwości PostgreSQL związane z modyfikacją JSON, byłoby to bardzo złożone (jeśli nie skomplikowane) i wprowadzałoby wiele narzutów, aby zrobić to samo z czystym SQL.
PostgreSQL 9.6+
W przypadku, gdy nie masz jeszcze dostępnej wersji 11, poniższa funkcja zrobi to samo kosztem obsługi samej konwersji typów, ale zachowa ją w pełni zgodną z API, więc po aktualizacji jedyne, co musisz zrobić zastępuje funkcję (nie są wymagane żadne zmiany w instrukcjach korzystających z tej funkcji):
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)
$$;