sql >> Base de Datos >  >> RDS >> PostgreSQL

Actualización de matriz JSONB para elemento específico

PostgreSQL 11+

Si ya está en PostgreSQL v. 11 (debido a la nuevo JSONB soporte de conversión de tipo ) su mejor apuesta probablemente sería una función personalizada escrita en Perl o Python.

Como prefiero Python 3, aquí hay un ejemplo funcional:

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

...que luego se puede usar de la siguiente manera:

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

Entonces, sí, la condición está duplicada, pero solo para limitar la cantidad de filas a tocar en primer lugar.

Para trabajar realmente en una instalación simple de PostgreSQL 11, necesita extensiones plpython3u y jsonb_plpython3u :

CREATE EXTENSION plpython3u;
CREATE EXTENSION jsonb_plpython3u;

La lógica de Python explicada:

for e in path_to_array:
    tmp = tmp[e]

...nos lleva al arreglo de entradas que necesitamos mirar.

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

...para cada elemento de la matriz, comprobamos si el criterio de filtro es null (entry_filters is None =coincide con cualquier entrada) o si la entrada "contiene" el ejemplo proporcionado, incluidas las claves y los valores (entry_filters.items() <= item.items() ).

Si la entrada coincide, sobrescriba/agregue contenido con el reemplazo provisto.

Espero que vaya en la dirección que estabas buscando.

Mirando las capacidades actuales de PostgreSQL relacionadas con la modificación de JSON, sería muy complejo (si no complicado) e introduciría muchos gastos generales para hacer lo mismo con SQL puro.

PostgreSQL 9.6+

En caso de que aún no tenga disponible la versión 11, la siguiente función hará lo mismo a expensas de manejar las conversiones de tipos por sí misma, pero manteniendo la API completamente compatible, por lo que una vez que actualice, lo único que tiene que hacer es reemplazar la función (no se requieren cambios en ninguna declaración que use esta función):

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