PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Atualização de matriz JSONB para elemento específico

PostgreSQL 11+


Se você já estiver no PostgreSQL v. 11 (por causa do novo JSONB suporte para conversão de tipo ) sua melhor aposta provavelmente seria uma função personalizada escrita em Perl ou Python.

Como sou a favor do Python 3, aqui está um exemplo 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 então pode ser usado da seguinte forma:
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"}]';

Então, sim, a condição é duplicada, mas apenas para limitar a quantidade de linhas a serem tocadas em primeiro lugar.

Para realmente trabalhar em uma instalação simples do PostgreSQL 11, você precisa das extensões plpython3u e jsonb_plpython3u :
CREATE EXTENSION plpython3u;
CREATE EXTENSION jsonb_plpython3u;

A lógica do Python explicada:
for e in path_to_array:
    tmp = tmp[e]

...nos leva ao array de entradas que precisamos ver.
for item in tmp:
    if (entry_filters is None or entry_filters.items() <= item.items()):
        item.update(replacement)

...para cada item no array, verificamos se o critério do filtro é null (entry_filters is None =corresponde a qualquer entrada) ou se a entrada "contém" o exemplo fornecido, incluindo chaves e valores (entry_filters.items() <= item.items() ).

Se a entrada corresponder, substitua/adicione o conteúdo pela substituição fornecida.

Espero que vá na direção que você procura.

Olhando para os recursos atuais do PostgreSQL relacionados à modificação de JSON, seria muito complexo (se não complicado) e introduziria muita sobrecarga para fazer o mesmo com SQL puro.

PostgreSQL 9.6+


Caso você ainda não tenha a versão 11 disponível, a função a seguir fará o mesmo às custas de lidar com as conversões de tipo por si mesma, mas a manterá totalmente compatível com a API, então você, uma vez atualizado, a única coisa que você precisa fazer é substituir a função (não é necessário alterar nenhuma instrução usando esta função):
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)
$$;