PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

JSONB-Array-Update für bestimmtes Element

PostgreSQL 11+

Wenn Sie bereits bei PostgreSQL v. 11 sind (aufgrund des neuer JSONB Typumwandlungsunterstützung ) wäre Ihre beste Wahl wahrscheinlich eine benutzerdefinierte Funktion, die in Perl oder Python geschrieben ist.

Da ich Python 3 bevorzuge, hier ein Funktionsbeispiel:

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
$$;

...die dann wie folgt verwendet werden können:

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"}]';

Also ja, die Bedingung wird dupliziert, aber nur, um die Anzahl der zu berührenden Zeilen zu begrenzen.

Um tatsächlich mit einer einfachen PostgreSQL 11-Installation zu arbeiten, benötigen Sie die Erweiterungen plpython3u und jsonb_plpython3u :

CREATE EXTENSION plpython3u;
CREATE EXTENSION jsonb_plpython3u;

Die Python-Logik erklärt:

for e in path_to_array:
    tmp = tmp[e]

... bringt uns zu dem Array von Einträgen, die wir uns ansehen müssen.

for item in tmp:
    if (entry_filters is None or entry_filters.items() <= item.items()):
        item.update(replacement)

...für jedes Element im Array prüfen wir, ob das Filterkriterium null ist (entry_filters is None =match any entry) oder ob der Eintrag das bereitgestellte Beispiel inklusive Schlüssel und Werte "enthält" (entry_filters.items() <= item.items()). ).

Wenn der Eintrag übereinstimmt, überschreibe/füge den Inhalt mit dem bereitgestellten Ersatz hinzu.

Ich hoffe, das geht in die Richtung, nach der Sie gesucht haben.

Betrachtet man die aktuellen Fähigkeiten von PostgreSQL im Zusammenhang mit der JSON-Modifikation, wäre es sehr komplex (wenn nicht kompliziert) und würde viel Overhead mit sich bringen, dasselbe mit reinem SQL zu tun.

PostgreSQL 9.6+

Falls Sie Version 11 noch nicht verfügbar haben, wird die folgende Funktion das Gleiche tun, auf Kosten der Typkonvertierung selbst, aber vollständig API-kompatibel bleiben, sodass Sie nach dem Upgrade das Einzige sind, was Sie tun müssen ersetzt die Funktion (keine Änderung an Anweisungen erforderlich, die diese Funktion verwenden):

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)
$$;