Запрос элементов массива внутри типа JSON

119

Я пытаюсь проверить jsonтип в PostgreSQL 9.3.
У меня есть jsonстолбец с именем dataв таблице reports. JSON выглядит примерно так:

{
  "objects": [
    {"src":"foo.png"},
    {"src":"bar.png"}
  ],
  "background":"background.png"
}

Я хотел бы запросить таблицу для всех отчетов, которые соответствуют значению 'src' в массиве 'objects'. Например, можно ли запросить в БД все соответствующие отчеты 'src' = 'foo.png'? Я успешно написал запрос, который может соответствовать "background":

SELECT data AS data FROM reports where data->>'background' = 'background.png'

Но поскольку "objects"есть массив значений, я не могу написать что-то, что работает. Можно ли запросить в БД все соответствующие отчеты 'src' = 'foo.png'? Я просмотрел эти источники, но до сих пор не могу понять:

Я тоже пробовал такие вещи, но безрезультатно:

SELECT json_array_elements(data->'objects') AS data from reports
WHERE  data->>'src' = 'foo.png';

Я не эксперт по SQL, поэтому не знаю, что делаю не так.

Pacothelovetaco
источник

Ответы:

215

json в Postgres 9.3+

Разложите массив JSON с функцией json_array_elements()в боковом соединении в FROMпредложении и проверьте его элементы:

WITH reports(data) AS (
   VALUES ('{"objects":[{"src":"foo.png"}, {"src":"bar.png"}]
           , "background":"background.png"}'::json)
   ) 
SELECT *
FROM   reports r, json_array_elements(r.data#>'{objects}') obj
WHERE  obj->>'src' = 'foo.png';

КТР ( WITHзапрос) просто заменяет таблицу reports.
Или, что эквивалентно только одному уровню вложенности:

SELECT *
FROM   reports r, json_array_elements(r.data->'objects') obj
WHERE  obj->>'src' = 'foo.png';

->>, ->и #>операторы объяснены в руководстве.

Оба запроса используют неявный JOIN LATERAL.

SQL Fiddle.

Близкий ответ:

jsonb в Postgres 9.4+

Используйте эквивалент jsonb_array_elements().

Еще лучше использовать новый оператор "содержит" @>(лучше всего в сочетании с соответствующим индексом GIN в выражении data->'objects'):

CREATE INDEX reports_data_gin_idx ON reports
USING gin ((data->'objects') jsonb_path_ops);

SELECT * FROM reports WHERE data->'objects' @> '[{"src":"foo.png"}]';

Поскольку ключ objectsсодержит массив JSON , нам необходимо сопоставить структуру в поисковом запросе и также заключить элемент массива в квадратные скобки. Снимите скобки массива при поиске простой записи.

Подробное объяснение и дополнительные параметры:

Эрвин Брандштеттер
источник
1
@pacothelovetaco: добавлено обновление для jsonb/ pg 9.4. Кроме того: для простого случая (1 уровень вложенности) ->оператор также выполняет трюк для jsonв стр. 9.3.
Эрвин Брандштеттер
1
@pacothelovetaco, для pg 9.3 '#>' не является секретным соусом, '->' подойдет для вашего случая, так как он также возвращает объект json. '#>' будет более полезным в случае вложенного пути json, поскольку он позволяет легко указать путь в '{}'
Gob00st
1
@> '[{"src": "foo.png"}]'; отлично работает в каком состоянии, но как удалить такой объект? я не знаю индекс этого объекта. я хочу удалить по ключевому значению.
Pranay Soni
1
@PranaySoni: Пожалуйста, задавайте новый вопрос как вопрос . Комментарии не к месту. Вы всегда можете ссылаться на этот для контекста.
Эрвин Брандштеттер
дорогой @ErwinBrandstetter, возможно ли найти оба документа путем частичного сопоставления? Например, я хотел бы получить обе записи что-то вроде этого '[{"src": ". Png"}]'
Pyrejkee
8

Создайте таблицу со столбцом типа json

CREATE TABLE friends ( id serial primary key, data jsonb);

Теперь вставим данные json

INSERT INTO friends(data) VALUES ('{"name": "Arya", "work": ["Improvements", "Office"], "available": true}');
INSERT INTO friends(data) VALUES ('{"name": "Tim Cook", "work": ["Cook", "ceo", "Play"], "uses": ["baseball", "laptop"], "available": false}');

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

select data->'name' from friends;
select data->'name' as name, data->'work' as work from friends;

Вы могли заметить, что в результатах есть кавычки (") и скобки ([]).

    name    |            work            
------------+----------------------------
 "Arya"     | ["Improvements", "Office"]
 "Tim Cook" | ["Cook", "ceo", "Play"]
(2 rows)

Теперь, чтобы получить только значения, просто используйте ->>

select data->>'name' as name, data->'work'->>0 as work from friends;
select data->>'name' as name, data->'work'->>0 as work from friends where data->>'name'='Arya';
Сэндип Дебнат
источник
22
Это приятно отформатированный шум без видимой связи с вопросом.
Эрвин Брандштеттер,
4
Я нашел это полезным. Показывает, как детализировать массив в jsonb
GavinBelson
0

выберите data -> 'objects' -> 0 -> 'src' как SRC из таблицы, где data -> 'objects' -> 0 -> 'src' = 'foo.png'

ананд шукла
источник
2
Это будет полезно, ТОЛЬКО ЕСЛИ вы знаете индекс, который
равен
да, но есть способ взорвать объект массива, который будет отображать строки, и мы можем это использовать. Поправьте меня, если я ошибаюсь.
ананд шукла
не
лучшее