Aggiornamento : Con PostgreSQL 9.5 ci sono alcune jsonb
funzionalità di manipolazione all'interno di PostgreSQL stesso (ma nessuna per json
; i cast sono necessari per manipolare i json
valori).
Unione di 2 (o più) oggetti JSON (o concatenazione di matrici):
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]'
Pertanto, l' impostazione di una chiave semplice può essere eseguita utilizzando:
SELECT jsonb '{"a":1}' || jsonb_build_object('<key>', '<value>')
Dove <key>
dovrebbe essere stringa e <value>
può essere qualunque tipo to_jsonb()
accetta.
Per impostare un valore in profondità in una gerarchia JSON , è jsonb_set()
possibile utilizzare la funzione:
SELECT jsonb_set('{"a":[null,{"b":[]}]}', '{a,1,b,0}', jsonb '{"c":3}')
-- will yield jsonb '{"a":[null,{"b":[{"c":3}]}]}'
Elenco completo dei parametri di jsonb_set()
:
jsonb_set(target jsonb,
path text[],
new_value jsonb,
create_missing boolean default true)
path
può contenere anche indici di array JSON e numeri interi negativi che vengono visualizzati contano dalla fine degli array JSON. Tuttavia, un indice di array JSON inesistente ma positivo aggiungerà l'elemento alla fine dell'array:
SELECT jsonb_set('{"a":[null,{"b":[1,2]}]}', '{a,1,b,1000}', jsonb '3', true)
-- will yield jsonb '{"a":[null,{"b":[1,2,3]}]}'
Per l' inserimento nell'array JSON (pur mantenendo tutti i valori originali) , è jsonb_insert()
possibile utilizzare la funzione ( in 9.6+; solo questa funzione, in questa sezione ):
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]}]}'
Elenco completo dei parametri di jsonb_insert()
:
jsonb_insert(target jsonb,
path text[],
new_value jsonb,
insert_after boolean default false)
Ancora una volta, numeri interi negativi che appaiono in path
conteggio dalla fine delle matrici JSON.
Quindi, f.ex. l'aggiunta alla fine di un array JSON può essere eseguita con:
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
Tuttavia, questa funzione funziona in modo leggermente diverso (rispetto a jsonb_set()
) quando path
in target
è la chiave di un oggetto JSON. In tal caso, aggiungerà una nuova coppia chiave-valore per l'oggetto JSON solo quando la chiave non viene utilizzata. Se viene utilizzato, genererà un errore:
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
L'eliminazione di una chiave (o di un indice) da un oggetto JSON (o, da un array) può essere eseguita con l' -
operatore:
SELECT jsonb '{"a":1,"b":2}' - 'a', -- will yield jsonb '{"b":2}'
jsonb '["a",1,"b",2]' - 1 -- will yield jsonb '["a","b",2]'
L'eliminazione, dal profondo di una gerarchia JSON, può essere eseguita con l' #-
operatore:
SELECT '{"a":[null,{"b":[3.14]}]}' #- '{a,1,b,0}'
-- will yield jsonb '{"a":[null,{"b":[]}]}'
Per 9.4 , è possibile utilizzare una versione modificata della risposta originale (di seguito), ma invece di aggregare una stringa JSON, è possibile aggregare direttamente in un oggetto JSON json_object_agg()
.
Risposta originale : è possibile (senza plpython o plv8) anche in SQL puro (ma necessita di 9.3+, non funzionerà con 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
Modifica :
Una versione, che imposta più chiavi e valori:
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$;
Modifica 2 : come notato da @ErwinBrandstetter, queste funzioni sopra funzionano come una cosiddetta UPSERT
(aggiorna un campo se esiste, inserisce se non esiste). Ecco una variante, che solo 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$;
Modifica 3 : Ecco la variante ricorsiva, che può impostare ( UPSERT
) un valore foglia (e utilizza la prima funzione da questa risposta), situata in un percorso chiave (dove le chiavi possono riferirsi solo a oggetti interni, array interni non supportati):
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$;
Aggiornamento : le funzioni sono ora compattate.
select json_object_set_key((select data from test where data->>'b' = '2'), 'b', 'two');
il messaggio di errore èERROR: could not determine polymorphic type because input has type "unknown"