sql >> Databáze >  >> RDS >> PostgreSQL

Jak upravím pole v novém datovém typu PostgreSQL JSON?

Aktualizovat :S PostgreSQL 9.5 existuje několik jsonb funkce manipulace v rámci samotného PostgreSQL (ale žádná pro json; přetypování je nutné pro manipulaci s json hodnoty).

Sloučení 2 (nebo více) objektů JSON (nebo zřetězení polí):

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]'

Takže nastavení jednoduchého klíče lze provést pomocí:

SELECT jsonb '{"a":1}' || jsonb_build_object('<key>', '<value>')

Kde <key> by měl být řetězec a <value> může být jakéhokoli typu to_jsonb() přijímá.

Pro nastavení hodnoty hluboko v hierarchii JSON , jsonb_set() lze použít funkci:

SELECT jsonb_set('{"a":[null,{"b":[]}]}', '{a,1,b,0}', jsonb '{"c":3}')
-- will yield jsonb '{"a":[null,{"b":[{"c":3}]}]}'

Úplný seznam parametrů jsonb_set() :

jsonb_set(target         jsonb,
          path           text[],
          new_value      jsonb,
          create_missing boolean default true)

path může obsahovat také indexy pole JSON a záporná celá čísla, která se tam objevují, se počítají od konce polí JSON. Neexistující, ale kladný index pole JSON však připojí prvek na konec pole:

SELECT jsonb_set('{"a":[null,{"b":[1,2]}]}', '{a,1,b,1000}', jsonb '3', true)
-- will yield jsonb '{"a":[null,{"b":[1,2,3]}]}'

Pro vložení do pole JSON (při zachování všech původních hodnot) , jsonb_insert() lze použít funkci (ve verzi 9.6+; pouze tato funkce v této sekci ):

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

Úplný seznam parametrů jsonb_insert() :

jsonb_insert(target       jsonb,
             path         text[],
             new_value    jsonb,
             insert_after boolean default false)

Opět platí, že záporná celá čísla, která se objevují v path počítat od konce polí JSON.

Takže např. připojení na konec pole JSON lze provést pomocí:

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

Tato funkce však funguje mírně odlišně (než jsonb_set() ), když path v target je klíč objektu JSON. V takovém případě přidá nový pár klíč–hodnota pro objekt JSON pouze tehdy, když se klíč nepoužívá. Pokud je použit, vyvolá chybu:

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

Odstranění klíče (nebo indexu) z objektu JSON (nebo z pole) lze provést pomocí - operátor:

SELECT jsonb '{"a":1,"b":2}' - 'a', -- will yield jsonb '{"b":2}'
       jsonb '["a",1,"b",2]' - 1    -- will yield jsonb '["a","b",2]'

Odstranění hluboko v hierarchii JSON lze provést pomocí #- operátor:

SELECT '{"a":[null,{"b":[3.14]}]}' #- '{a,1,b,0}'
-- will yield jsonb '{"a":[null,{"b":[]}]}'

Pro 9.4 , můžete použít upravenou verzi původní odpovědi (níže), ale místo agregace řetězce JSON můžete agregovat do objektu json přímo pomocí json_object_agg() .

Původní odpověď :Je to možné (bez plpythonu nebo plv8) také v čistém SQL (ale potřebuje 9.3+, nebude fungovat s 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

Upravit :

Verze, která nastavuje více klíčů a hodnot:

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

Úprava 2 :jak poznamenal @ErwinBrandstetter, výše uvedené funkce fungují jako takzvaný UPSERT (aktualizuje pole, pokud existuje, vloží, pokud neexistuje). Zde je varianta, která pouze 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$;

Úprava 3 :Zde je rekurzivní varianta, kterou lze nastavit (UPSERT ) hodnotu listu (a používá první funkci z této odpovědi), umístěnou na cestě klíče (kde klíče mohou odkazovat pouze na vnitřní objekty, vnitřní pole nejsou podporována):

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

Aktualizováno:Přidána funkce pro nahrazení existujícího klíče pole json jiným daným klíčem. Může se hodit při aktualizaci datových typů při migracích nebo jiných scénářích, jako je změna struktury dat.

CREATE OR REPLACE FUNCTION json_object_replace_key(
    json_value json,
    existing_key text,
    desired_key text)
  RETURNS json AS
$BODY$
SELECT COALESCE(
(
    SELECT ('{' || string_agg(to_json(key) || ':' || value, ',') || '}')
    FROM (
        SELECT *
        FROM json_each(json_value)
        WHERE key <> existing_key
        UNION ALL
        SELECT desired_key, json_value -> existing_key
    ) AS "fields"
    -- WHERE value IS NOT NULL (Actually not required as the string_agg with value's being null will "discard" that entry)

),
    '{}'
)::json
$BODY$
  LANGUAGE sql IMMUTABLE STRICT
  COST 100;

Aktualizovat :funkce jsou nyní zkomprimovány.



  1. JPA flush vs

  2. SQL:Pokud jde o NOT IN a NOT EQUAL TO, co je efektivnější a proč?

  3. Nelze se připojit k místnímu serveru MySQL přes soket '/var/lib/mysql/mysql.sock' (2)

  4. Vztah předaný do #nebo musí být strukturálně kompatibilní. Nekompatibilní hodnoty:[:reference]