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

Mesclando valores JSONB no PostgreSQL?


Você deve mesclar elementos não aninhados usando jsonb_each() para ambos os valores. Fazer isso em uma consulta não trivial pode ser desconfortável, então eu preferiria uma função personalizada como esta:
create or replace function jsonb_my_merge(a jsonb, b jsonb)
returns jsonb language sql as $$
    select 
        jsonb_object_agg(
            coalesce(ka, kb), 
            case 
                when va isnull then vb 
                when vb isnull then va 
                else va || vb 
            end
        )
    from jsonb_each(a) e1(ka, va)
    full join jsonb_each(b) e2(kb, vb) on ka = kb
$$;

Usar:
select jsonb_my_merge(
    '{"a":{"b":2}, "d": {"e": 10}, "x": 1}'::jsonb, 
    '{"a":{"c":3}, "d": {"f": 11}, "y": 2}'::jsonb
)

                          jsonb_my_merge                          
------------------------------------------------------------------
 {"a": {"b": 2, "c": 3}, "d": {"e": 10, "f": 11}, "x": 1, "y": 2}
(1 row)

Você pode modificar ligeiramente a função usando recursão para obter uma solução que funcione em qualquer nível de aninhamento:
create or replace function jsonb_recursive_merge(a jsonb, b jsonb)
returns jsonb language sql as $$
    select 
        jsonb_object_agg(
            coalesce(ka, kb), 
            case 
                when va isnull then vb 
                when vb isnull then va 
                when jsonb_typeof(va) <> 'object' then va || vb
                else jsonb_recursive_merge(va, vb)
            end
        )
    from jsonb_each(a) e1(ka, va)
    full join jsonb_each(b) e2(kb, vb) on ka = kb
$$;

Exemplos:
select jsonb_recursive_merge( 
    '{"a":{"b":{"c":3},"x":5}}'::jsonb, 
    '{"a":{"b":{"d":4},"y":6}}'::jsonb);

             jsonb_recursive_merge              
------------------------------------------------
 {"a": {"b": {"c": 3, "d": 4}, "x": 5, "y": 6}}
(1 row)

select jsonb_recursive_merge(
    '{"a":{"b":{"c":{"d":{"e":1}}}}}'::jsonb, 
    '{"a":{"b":{"c":{"d":{"f":2}}}}}'::jsonb)

            jsonb_recursive_merge             
----------------------------------------------
 {"a": {"b": {"c": {"d": {"e": 1, "f": 2}}}}}
(1 row)

Por fim, a variante da função com as alterações propostas pelo OP (ver comentários abaixo):
create or replace function jsonb_recursive_merge(a jsonb, b jsonb) 
returns jsonb language sql as $$ 
select 
    jsonb_object_agg(
        coalesce(ka, kb), 
        case 
            when va isnull then vb 
            when vb isnull then va 
            when jsonb_typeof(va) <> 'object' or jsonb_typeof(vb) <> 'object' then vb 
            else jsonb_recursive_merge(va, vb) end 
        ) 
    from jsonb_each(a) e1(ka, va) 
    full join jsonb_each(b) e2(kb, vb) on ka = kb 
$$;