PostgreSQL 11+
Ако вече сте на PostgreSQL v. 11 (заради нов JSONB
поддръжка на типово преобразуване
) най-добрият ви залог вероятно ще бъде персонализирана функция, написана на Perl или Python.
Тъй като предпочитам Python 3, ето функционален пример:
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"}]';
Така че да, условието се дублира, но само за да се ограничи броят на редовете, които да се докоснат на първо място.
За да работите в обикновена инсталация на PostgreSQL 11, имате нужда от разширения 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 запис съвпада, тогава презапишете/добавете съдържание с предоставената замяна.
Надявам се, че това отива в посоката, която търсите.
Разглеждайки текущите възможности на PostgreSQL, свързани с модификацията на JSON, би било много сложно (ако не и сложно) и би довело до много допълнителни разходи, за да се направи същото с чист SQL.
PostgreSQL 9.6+
В случай, че все още нямате налична версия 11, следната функция ще направи същото за сметка на самата обработка на преобразуванията на типове, но ще я запази напълно съвместима с API, така че след като надстроите, единственото нещо, което трябва да направите е замяна на функцията (не се изисква промяна на никакви изрази, използващи тази функция):
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)
$$;