Добавить значение вложенного ключа в JSONB в Postgres - PullRequest
0 голосов
/ 25 июня 2018

Я пытаюсь добавить ключ к вложенному jsonb в postgres, но получаю ошибки. По сути, я начинаю с json как:

{"tel": "123", "name": "foo", "new_info": {"a": "bar"}}

и я хочу добавить {"b", "baz"} в "new_info" так, чтобы результирующий jsonb был:

{"tel": "123", "name": "foo", "new_info": {"a": "bar", "b":"baz"}}

Я использую следующие команды для перехода к исходному jsonb:

CREATE TABLE mytable (
 ID serial NOT NULL PRIMARY KEY,
 data jsonb NOT NULL
);


INSERT INTO mytable (data)
VALUES
 (
 '{ "name": "foo", "tel": "123"}'
 );

UPDATE mytable SET data = jsonb_set(data, '{new_info}', '{"a":"bar"}', TRUE) WHERE data @> '{"name": "foo"}' ;

и пытается использовать следующее для обновления «new_info», которая не работает:

WITH orig_new_info AS (SELECT data#>'{new_info}' FROM mytable WHERE data @> '{"name": "foo"}')
WITH updated_new_info AS (jsonb_set(orig_new_info, '{"b":"baz"}',TRUE ))
UPDATE mytable SET data = jsonb_set(data, '{new_info}', updated_new_info, TRUE) WHERE data @> '{"name": "foo"}';

Любые указатели очень ценятся!

ОБНОВЛЕНИЕ № 1:

За клинс отвечают следующие работы:

update mytable 
set data = jsonb_insert(data, '{new_info}', data->'new_info' || '{"b":"baz"}', TRUE)
where data @> '{"name": "foo"}'
returning *;

Однако как можно избежать перезаписи существующих ключей, используя что-то вроде jsonb_insert. Другими словами, почему не работают следующие примеры?

#ex 1
update mytable 
set data = jsonb_insert(data, '{new_info}', jsonb_insert(SELECT data->'new_info' FROM mytable WHERE data @> '{"name": "foo"}'), '{"b":"baz"}'),true)
where data @> '{"name": "foo"}'
returning *;

#ex2
WITH orig_new_info AS (SELECT data#>'{new_info}' FROM mytable WHERE data @> '{"name": "foo"}')
WITH updated_new_info AS(SELECT jsonb_insert(orig_new_info, orig_new_info ||'{"b":"bazer"}'))
update mytable 
set data = jsonb_set(data, '{new_info}', updated_new_info, TRUE)
where data @> '{"name": "foo"}'
returning *; 

Другими словами, в ответе клина рассматриваются только ключи верхнего уровня data jsonb, а не ключи вложенного "new_info" json, который находится внутри data.

ОБНОВЛЕНИЕ № 2:

За клин обновлен ответ на следующие работы:

update mytable 
set data = jsonb_insert(data, '{new_info, b}', '"baz"')
where data @> '{"name": "foo"}'

Однако, если "new_info" не существует в data, обновление успешно завершается без сохранения. Таким образом, следующие команды завершаются успешно, но данные не сохраняются:

DROP TABLE mytable;

CREATE TABLE mytable (
 ID serial NOT NULL PRIMARY KEY,
 data jsonb NOT NULL
);


INSERT INTO mytable (data)
VALUES
 (
 '{ "name": "foo", "tel": "123"}'
 );

update mytable 
set data = jsonb_insert(data, '{new_info, b}', '"baz"')
where data @> '{"name": "foo"}'
returning *;

Так что для меня это немного удивительно, так как создается впечатление, что оно спасло, хотя и не спасло. Я хочу избегать операторов case, поскольку в большинстве случаев это будет ненужной проверкой, и скорее всего она провалится, если «new_info» не существует (или просто создаст ее, если она не добавляет накладных расходов в ситуациях, когда «new_info» уже существует). существовать). Т.е. я хочу избежать того, что делают эти ответы:

Проверить, существует ли ключ в JSON с PL / pgSQL?

Обновление или создание вложенного значения jsonb с помощью одной команды обновления

1 Ответ

0 голосов
/ 25 июня 2018

Использование || (оператор конкатенации):

update mytable 
set data = jsonb_set(data, '{new_info}', data->'new_info' || '{"b":"baz"}')
where data @> '{"name": "foo"}'
returning *

 id |                                data                                 
----+---------------------------------------------------------------------
  1 | {"tel": "123", "name": "foo", "new_info": {"a": "bar", "b": "baz"}}
(1 row)

UPDATE 1    

Функция jsonb_set() была введена в Postgres 9.5. В Postgres 9.6+ вы также можете использовать jsonb_insert(),, что может быть проще:

update mytable 
set data = jsonb_insert(data, '{new_info, b}', '"baz"')
where data @> '{"name": "foo"}'

С документация:

jsonb_insert (целевой jsonb, текст пути [], новое_значение jsonb, [insert_after логический])

(...) Если секция target , обозначенная path , находится в объекте JSONB, new_value будет вставлена, только если target не существует.

Следовательно, путь должен указывать на несуществующий ключ (ключ, который вы хотите вставить).

...