Обновление Postgres JSONb (внутри массива Json), удаляет все элементы в массиве, когда не найдено совпадений или нулевой параметр? - PullRequest
0 голосов
/ 30 августа 2018

С учетом следующей таблицы:

create table documents(docu_id text, attachments jsonb);
insert into documents values
('001', 
    '[
      {"name": "uno","id":"1"},
      { "name": "dos","id":"2"},   
      { "name": "tres","id":"3"}
    ]'
),
('002', 
    '[
      { "name": "eins","id":"1"  },
      { "name": "zwei", "id":"2" }
    ]'
);
select * from documents;

enter image description here

У меня есть этот запрос postgres, который отлично работает, когда я хочу удалить с существующим идентификатором. Но когда я использую несуществующий идентификатор, все элементы внутри вложений jsonarray удаляются, как избежать этого поведения?

UPDATE documents
   SET attachments = attachments #- /* #- Delete field/element with specified path */
   (
       '{' || /* Concat */ 
            (
            SELECT i
              FROM generate_series(0, jsonb_array_length(attachments) - 1) AS i
             WHERE (attachments->i->'id' = '"x"') /* <=====BUG origin */
           ) 
       || '}'
   )::text[] /* cast as text */
  where docu_id = '002';

Пример ссылки с рабочими данными: http://rextester.com/VZYSG74184

1 Ответ

0 голосов
/ 31 августа 2018

Вот два простых запроса, которые могут объяснить источник проблемы:

select '{' || (select 1 where false) || '}';
┌──────────┐
│ ?column? │
├──────────┤
│ NULL     │
└──────────┘

with t(x) as (values('[1,2,3]'::jsonb))
select *, x #- '{1}', x #- '{}', x #- null from t;
┌───────────┬──────────┬───────────┬──────────┐
│     x     │ ?column? │ ?column?  │ ?column? │
├───────────┼──────────┼───────────┼──────────┤
│ [1, 2, 3] │ [1, 3]   │ [1, 2, 3] │ NULL     │
└───────────┴──────────┴───────────┴──────────┘

Как вы можете видеть выше, когда заданный параметр имеет значение null с оператором #-, он удаляет весь контент из массива json.

Это легко исправить, построив массив более удобным способом:

UPDATE documents
   SET attachments = attachments #- /* #- Delete field/element with specified path */
   array(
            SELECT i
              FROM generate_series(0, jsonb_array_length(attachments) - 1) AS i
             WHERE (attachments->i->'id' = '"x"')
   )::text[] /* cast as text */
  where docu_id = '002';

тестовая ссылка: http://rextester.com/IIAS33106

...