У меня в таблице есть столбец 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)
Как это может быть сделано?
@Islingre да, параметр пути был неправильным, и последний аргумент не был преобразован в JSONB.


Я находил совпадение, превращал его в 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"}
]
Какая ошибка отображается в функции
jsonb_set()? Возможно, ваши параметры имеют неправильные типы? Оно должно существовать. Возможно, предоставьте код, который вы пробовали и не работает.