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


118

Я намагаюся перевірити jsonтип у PostgreSQL 9.3.
У мене jsonстовпчик, який називається dataв таблиці, називається reports. JSON виглядає приблизно так:

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

Я б хотів запитати таблицю щодо всіх звітів, які відповідають значенню 'src' в масиві 'об’єкти'. Наприклад, чи можна запитувати БД для всіх звітів, які відповідають '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, тому не знаю, що я роблю неправильно.

Відповіді:


214

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.
Erwin Brandstetter

1
@pacothelovetaco, для pg 9.3, "#>" - це не таємний соус, '->' було б добре для вашого випадку, оскільки він також повертає об'єкт json. '#>' було б корисніше у вкладеному випадку до шляху до json, оскільки це дозволяє легко вказати шлях у '{}'
Gob00st

1
@> '[{"src": "foo.png"}]'; добре працювати в якому стані, але як видалити такий об'єкт, як цей? Я не знаю індекс цього об’єкта. я хочу видалити за ключовим значенням.
Pranay Soni

1
@PranaySoni: Будь ласка, задайте нове питання як питання . Коментарі - це не місце. Ви завжди можете зв’язатись із цим для контексту.
Ервін Брандстеттер

шановний @ErwinBrandstetter, чи можна знайти обидва документи шляхом часткового узгодження? Наприклад, я хотів би отримати обидва записи щось подібне '[{"src": ".
Png

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

виберіть дані -> 'об’єкти' -> 0 -> 'src' як SRC з таблиці, де дані -> 'об'єкти' -> 0 -> 'src' = 'foo.png'


2
Це було б корисно ТОЛЬКО, якщо ви знаєте індекс, який був 0.
Buyut Joko Rivai

так, але є спосіб вибухнути об'єкт масиву, який буде відображати мудрі рядки, і ми можемо це використовувати. Виправте мене, якщо я помиляюся.
anand shukla

не гарне рішення, як ви не можете бути впевнені, "src" знаходиться на позиції 0.
simUser
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.