Удаление элемента массива jsonb по имени - PullRequest
3 голосов
/ 12 июля 2020

У меня есть следующая таблица

CREATE TABLE country (
    id      INTEGER NOT NULL PRIMARY KEY ,
    name        VARCHAR(50),
    extra_info  JSONB
);
 
INSERT INTO country(id,extra_info)
VALUES (1, '{ "name" : "France", "population" : "65000000", "flag_colours": ["red", "blue","white"]}');
 
INSERT INTO country(id,extra_info)
VALUES (2, '{ "name": "Spain", "population" : "47000000", "borders": ["Portugal", "France"] }');

, и я могу добавить элемент в массив, например,

UPDATE country SET extra_info = jsonb_set(extra_info, '{flag_colours,999999999}', '"green"', true);

и обновить, как это

UPDATE country SET extra_info = jsonb_set(extra_info, '{flag_colours,0}', '"yellow"');

Теперь я хочу удалить элемент массива с известным индексом или именем.

Как мне удалить элемент flag_color по индексу или по имени?

Обновить

Удалить по индексу

UPDATE country SET extra_info = extra_info #- '{flag_colours,-1}'

Как удалить по имени?

Ответы [ 2 ]

1 голос
/ 21 июля 2020

На самом деле PG12 позволяет делать это без LATERAL JOIN:

SELECT jsonb_path_query_array(j #> '{flag_colours}', '$[*] ? (@ != "red")'),
       jsonb_set(j, '{flag_colours}', jsonb_path_query_array(j #> '{flag_colours}', '$[*] ? (@ != "red")'))
  FROM (SELECT '{ "name" : "France", "population" : "65000000",
                  "flag_colours": ["red", "blue","white"]}'::jsonb AS j
       ) AS j
 WHERE j @? '$.flag_colours[*] ? (@ == "red")';

 jsonb_path_query_array |                                    jsonb_set                                    
------------------------+---------------------------------------------------------------------------------
 ["blue", "white"]      | {"name": "France", "population": "65000000", "flag_colours": ["blue", "white"]}
(1 row)
1 голос
/ 12 июля 2020

Поскольку массивы не имеют прямого доступа к элементам простым способом, мы можем попытаться подойти к этому по-другому, отключив вложенность -> фильтруя элементы -> объединяя элементы вместе. Я сформулировал пример кода с упорядоченными комментариями, чтобы помочь.

CREATE TABLE new_country AS
-- 4. Return a new array (for immutability) that contains the new desired set of colors
SELECT id, name, jsonb_set(extra_info, '{flag_colours}', new_colors, FALSE)
FROM country
       -- 3. Use Lateral join to apply this to every row
       LEFT JOIN LATERAL (
  -- 1. First unnest the desired elements from the Json array as text (to enable filtering)
  WITH prep AS (SELECT jsonb_array_elements_text(extra_info -> 'flag_colours') colors FROM country)
  SELECT jsonb_agg(colors) new_colors -- 2. Form a new jsonb array after filtering
  FROM prep
  WHERE colors <> 'red') lat ON TRUE;

Если вы хотите обновить только затронутый столбец без воссоздания основной таблицы, вы можете:

UPDATE country
SET extra_info=new_extra_info
FROM new_country
WHERE country.id = new_country.id;

Я разбил его на два запроса, чтобы улучшить читаемость; однако вы также можете использовать подзапрос вместо создания новой таблицы (new_country).

С подзапросом он должен выглядеть так:

UPDATE country
SET extra_info=new_extra_info
FROM (SELECT id, name, jsonb_set(extra_info, '{flag_colours}', new_colors, FALSE) new_extra_info
      FROM country
             -- 3. Use Lateral join to scale this across tables
             LEFT JOIN LATERAL (
        -- 1. First unnest the desired elements from the Json array as text (to enable filtering)
        WITH prep AS (SELECT jsonb_array_elements_text(extra_info -> 'flag_colours') colors FROM country)
        SELECT jsonb_agg(colors) new_colors -- 2. Form a new jsonb array after filtering
        FROM prep
        WHERE colors <> 'red') lat ON TRUE) new_country
WHERE country.id = new_country.id;

Кроме того, вы можете фильтровать строки с помощью (Как из PostgreSQL 9,4):

SELECT *
FROM country
WHERE (extra_info -> 'flag_colours') ? 'red'
...