PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Mise à jour du tableau JSONB pour un élément spécifique

PostgreSQL 11+

Si vous êtes déjà sur PostgreSQL v. 11 (à cause du nouveau JSONB prise en charge de la conversion de type ) votre meilleur pari serait probablement une fonction personnalisée écrite en Perl ou Python.

Comme je préfère Python 3, voici un exemple fonctionnel :

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

...qui peut ensuite être utilisé comme suit :

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

Alors oui, la condition est dupliquée, mais seulement pour limiter le nombre de lignes à toucher en premier lieu.

Pour travailler réellement sur une installation simple de PostgreSQL 11, vous avez besoin des extensions plpython3u et jsonb_plpython3u :

CREATE EXTENSION plpython3u;
CREATE EXTENSION jsonb_plpython3u;

La logique Python expliquée :

for e in path_to_array:
    tmp = tmp[e]

... nous amène au tableau d'entrées que nous devons examiner.

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

...pour chaque élément du tableau, nous vérifions si le critère de filtre est null (entry_filters is None =correspond à n'importe quelle entrée) ou si l'entrée "contient" l'exemple fourni, y compris les clés et les valeurs (entry_filters.items() <= item.items() ).

Si l'entrée correspond, écrasez/ajoutez le contenu avec le remplacement fourni.

J'espère que cela va dans la direction que vous recherchiez.

En regardant les capacités actuelles de PostgreSQL liées à la modification de JSON, ce serait très complexe (voire compliqué) et introduirait beaucoup de temps système pour faire la même chose avec du SQL pur.

PostgreSQL 9.6+

Au cas où vous n'auriez pas encore la version 11 disponible, la fonction suivante fera de même au détriment de la gestion des conversions de type par elle-même mais la gardera complètement compatible avec l'API, de sorte qu'une fois la mise à niveau effectuée, la seule chose que vous aurez à faire remplace la fonction (aucune modification des instructions utilisant cette fonction n'est requise) :

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