Условно заменить объект в массиве JSON в jsonb

У меня в таблице есть столбец jsonb, который выглядит так:

[
   {
     "id": "1231-213-331",
     "name" : "Object 1 Name"
   },
   {
     "id": "4113-13-131-21",
     "name" : "Object 2 Name"
   }
]

Я передаю объект json в свою функцию Postgres:

{
  "id": "1231-213-331",
  "name" : "New Name For Object One"
}

При этом я хотел бы полностью заменить существующий объект json внутри объекта jsonb соответствующим идентификатором (который является уникальным).

Я пробовал конкатенацию с помощью || и функции jsonb_set, которой, по-видимому, не существует. (Я использую Postgres 15.4)

Как это может быть сделано?

Какая ошибка отображается в функции jsonb_set()? Возможно, ваши параметры имеют неправильные типы? Оно должно существовать. Возможно, предоставьте код, который вы пробовали и не работает.

Islingre 27.05.2024 22:25

@Islingre да, параметр пути был неправильным, и последний аргумент не был преобразован в JSONB.

Ethan 28.05.2024 14:48
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
2
87
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я находил совпадение, превращал его в text[], а затем использовал jsonb_set() для выполнения замены:

with invars as (
  select '[
   {
     "id": "1231-213-331",
     "name" : "Object 1 Name"
   },
   {
     "id": "4113-13-131-21",
     "name" : "Object 2 Name"
   } 
]'::jsonb as jdata, 
   '{
  "id": "1231-213-331",
  "name" : "New Name For Object One"
}'::jsonb as newobj
), findindex as (
  select ARRAY[(n - 1)::text] as jpath
    from invars i
         join lateral jsonb_array_elements(i.jdata) with ordinality e(j, n)
           on e.j->'id' = i.newobj->'id'
)
select jsonb_set(i.jdata, f.jpath, i.newobj) as result
  from findindex f
       cross join invars i;

рабочая рабочий пример

Ваша ошибка в том, что параметры функции должны быть jsonb_set ( target jsonb, path text[], new_value jsonb [, create_if_missing boolean ] ). Имитируя вашу функцию с помощью анонимного блока, это будет что-то вроде этого

do $$
declare
 in_object jsonb; 
begin
    in_object = '{
                    "id": "1231-213-331",
                    "name" : "New Name For Object One"
                }'::jsonb; 
    update a set jsonb_column = jsonb_set(jsonb_column, jbpath, in_object)
    from (
        select id, array[(e.i-1)::text] jbpath
        from a
        , jsonb_array_elements(jsonb_column) with ordinality e(cont, i)
        where e.cont->'id' = in_object->'id') sq
    where a.id = sq.id;    
end $$;

Скрипка для проверки

Для управления значением jsonb внутри блока кода PL/pgSQL:

CREATE OR REPLACE FUNCTION jsb_test (_input jsonb)
  RETURNS jsonb
  LANGUAGE plpgsql AS
$func$
DECLARE
   _jsb jsonb := '[{"id":"1231-213-331", "name":"New Name For Object One"}
                 , {"id":"4113-13-131-21", "name":"Object 2 Name"}]';
BEGIN
   SELECT jsonb_agg(CASE WHEN obj->>'id' = _input->>'id'
                         THEN _input  -- replace whole object
                         ELSE obj END)
   FROM   jsonb_array_elements(_jsb) obj
   INTO   _jsb;

   RETURN _jsb;
END
$func$;

рабочий пример

Кроме того: jsonb_set() отлично работает в Postgres 15. Здесь он нам просто не нужен.

Ответ принят как подходящий

вы можете использовать развертывание массива, как предлагают другие ответы, или вы можете использовать jsonb_path_query_array, чтобы удалить значение из массива, а затем объединить его с помощью '||' со значением:

do $$
declare
    data jsonb;
    new_value jsonb;
begin
    data := '[
      {"id":"1231-213-331", "name":"Object 1 Name"},
      {"id":"4113-13-131-21", "name":"Object 2 Name"}
    ]';

    new_value = '{"id": "1231-213-331", "name" : "New Name For Object One"}';

    data := jsonb_path_query_array(data, '$[*] ? (@.id != $id)', new_value) || new_value;

    raise notice '%', data;
end $$;


[
    {"id": "4113-13-131-21", "name": "Object 2 Name"},
    {"id": "1231-213-331", "name": "New Name For Object One"}
]

Другие вопросы по теме