Atualização : Com o PostgreSQL 9.5 , existem algumas jsonb
funcionalidades de manipulação no próprio PostgreSQL (mas nenhuma para json
; as conversões são necessárias para manipular json
valores).
Mesclando 2 (ou mais) objetos JSON (ou concatenando matrizes):
SELECT jsonb '{"a":1}' || jsonb '{"b":2}', -- will yield jsonb '{"a":1,"b":2}'
jsonb '["a",1]' || jsonb '["b",2]' -- will yield jsonb '["a",1,"b",2]'
Portanto, a configuração de uma chave simples pode ser feita usando:
SELECT jsonb '{"a":1}' || jsonb_build_object('<key>', '<value>')
Onde <key>
deve ser uma string e <value>
pode ser qualquer tipo que seja to_jsonb()
aceito.
Para definir um valor profundo em uma hierarquia JSON , a jsonb_set()
função pode ser usada:
SELECT jsonb_set('{"a":[null,{"b":[]}]}', '{a,1,b,0}', jsonb '{"c":3}')
-- will yield jsonb '{"a":[null,{"b":[{"c":3}]}]}'
Lista completa de parâmetros de jsonb_set()
:
jsonb_set(target jsonb,
path text[],
new_value jsonb,
create_missing boolean default true)
path
também pode conter índices de matriz JSON e números inteiros negativos que aparecem lá são contados a partir do final de matrizes JSON. No entanto, um índice de matriz JSON positivo, mas não existente, acrescentará o elemento ao final da matriz:
SELECT jsonb_set('{"a":[null,{"b":[1,2]}]}', '{a,1,b,1000}', jsonb '3', true)
-- will yield jsonb '{"a":[null,{"b":[1,2,3]}]}'
Para inserir na matriz JSON (preservando todos os valores originais) , a jsonb_insert()
função pode ser usada ( em 9.6+; somente nesta função, nesta seção ):
SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b,0}', jsonb '2')
-- will yield jsonb '{"a":[null,{"b":[2,1]}]}', and
SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b,0}', jsonb '2', true)
-- will yield jsonb '{"a":[null,{"b":[1,2]}]}'
Lista completa de parâmetros de jsonb_insert()
:
jsonb_insert(target jsonb,
path text[],
new_value jsonb,
insert_after boolean default false)
Novamente, números inteiros negativos que aparecem na path
contagem do final das matrizes JSON.
Então, f.ex. anexar ao final de uma matriz JSON pode ser feito com:
SELECT jsonb_insert('{"a":[null,{"b":[1,2]}]}', '{a,1,b,-1}', jsonb '3', true)
-- will yield jsonb '{"a":[null,{"b":[1,2,3]}]}', and
No entanto, essa função está funcionando de maneira ligeiramente diferente (que jsonb_set()
) quando path
in target
é a chave de um objeto JSON. Nesse caso, ele incluirá apenas um novo par de valores-chave para o objeto JSON quando a chave não for usada. Se for usado, ocorrerá um erro:
SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,c}', jsonb '[2]')
-- will yield jsonb '{"a":[null,{"b":[1],"c":[2]}]}', but
SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b}', jsonb '[2]')
-- will raise SQLSTATE 22023 (invalid_parameter_value): cannot replace existing key
A exclusão de uma chave (ou de um índice) de um objeto JSON (ou de uma matriz) pode ser feita com o -
operador:
SELECT jsonb '{"a":1,"b":2}' - 'a', -- will yield jsonb '{"b":2}'
jsonb '["a",1,"b",2]' - 1 -- will yield jsonb '["a","b",2]'
A exclusão de uma hierarquia JSON profunda pode ser feita com o #-
operador:
SELECT '{"a":[null,{"b":[3.14]}]}' #- '{a,1,b,0}'
-- will yield jsonb '{"a":[null,{"b":[]}]}'
Para a 9.4 , é possível usar uma versão modificada da resposta original (abaixo), mas, em vez de agregar uma sequência JSON, é possível agregar diretamente em um objeto json json_object_agg()
.
Resposta original : É possível (sem plpython ou plv8) também em SQL puro (mas precisa de 9.3+, não funcionará com 9.2)
CREATE OR REPLACE FUNCTION "json_object_set_key"(
"json" json,
"key_to_set" TEXT,
"value_to_set" anyelement
)
RETURNS json
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')::json
FROM (SELECT *
FROM json_each("json")
WHERE "key" <> "key_to_set"
UNION ALL
SELECT "key_to_set", to_json("value_to_set")) AS "fields"
$function$;
SQLFiddle
Editar :
Uma versão que define várias chaves e valores:
CREATE OR REPLACE FUNCTION "json_object_set_keys"(
"json" json,
"keys_to_set" TEXT[],
"values_to_set" anyarray
)
RETURNS json
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')::json
FROM (SELECT *
FROM json_each("json")
WHERE "key" <> ALL ("keys_to_set")
UNION ALL
SELECT DISTINCT ON ("keys_to_set"["index"])
"keys_to_set"["index"],
CASE
WHEN "values_to_set"["index"] IS NULL THEN 'null'::json
ELSE to_json("values_to_set"["index"])
END
FROM generate_subscripts("keys_to_set", 1) AS "keys"("index")
JOIN generate_subscripts("values_to_set", 1) AS "values"("index")
USING ("index")) AS "fields"
$function$;
Editar 2 : como o @ErwinBrandstetter observou que essas funções acima funcionam como as chamadas UPSERT
(atualiza um campo, se existir, insere, se não existir). Aqui está uma variante, que apenas UPDATE
:
CREATE OR REPLACE FUNCTION "json_object_update_key"(
"json" json,
"key_to_set" TEXT,
"value_to_set" anyelement
)
RETURNS json
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT CASE
WHEN ("json" -> "key_to_set") IS NULL THEN "json"
ELSE (SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')
FROM (SELECT *
FROM json_each("json")
WHERE "key" <> "key_to_set"
UNION ALL
SELECT "key_to_set", to_json("value_to_set")) AS "fields")::json
END
$function$;
Edit 3 : Aqui está uma variante recursiva, que pode definir ( UPSERT
) um valor de folha (e usa a primeira função desta resposta), localizada em um caminho de chave (onde as chaves podem se referir apenas a objetos internos, matrizes internas não suportadas):
CREATE OR REPLACE FUNCTION "json_object_set_path"(
"json" json,
"key_path" TEXT[],
"value_to_set" anyelement
)
RETURNS json
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT CASE COALESCE(array_length("key_path", 1), 0)
WHEN 0 THEN to_json("value_to_set")
WHEN 1 THEN "json_object_set_key"("json", "key_path"[l], "value_to_set")
ELSE "json_object_set_key"(
"json",
"key_path"[l],
"json_object_set_path"(
COALESCE(NULLIF(("json" -> "key_path"[l])::text, 'null'), '{}')::json,
"key_path"[l+1:u],
"value_to_set"
)
)
END
FROM array_lower("key_path", 1) l,
array_upper("key_path", 1) u
$function$;
Atualização : as funções são compactadas agora.
select json_object_set_key((select data from test where data->>'b' = '2'), 'b', 'two');
mensagem de erro éERROR: could not determine polymorphic type because input has type "unknown"