Zapytanie o elementy tablicy wewnątrz typu JSON

118

Próbuję przetestować jsontyp w PostgreSQL 9.3.
Mam jsonkolumnę o nazwie dataw tabeli o nazwie reports. JSON wygląda mniej więcej tak:

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

Chciałbym wyszukać w tabeli wszystkie raporty, które pasują do wartości „src” w tablicy „objects”. Na przykład, czy można wysyłać zapytania do bazy danych o wszystkie zgodne raporty 'src' = 'foo.png'? Pomyślnie napisałem zapytanie, które może pasować do "background":

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

Ale ponieważ "objects"ma tablicę wartości, nie mogę napisać czegoś, co działa. Czy można wysyłać zapytania do bazy danych o wszystkie pasujące raporty 'src' = 'foo.png'? Przejrzałem te źródła, ale nadal nie mogę ich zdobyć:

Próbowałem też takich rzeczy, ale bezskutecznie:

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

Nie jestem ekspertem od SQL, więc nie wiem, co robię źle.

pacothelovetaco
źródło

Odpowiedzi:

215

json w Postgres 9.3+

Rozpakuj tablicę JSON z funkcją json_array_elements()w złączeniu bocznym w FROMklauzuli i przetestuj jej elementy:

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';

CTE ( WITHzapytań) po prostu zastępuje stole reports.
Lub odpowiednik tylko dla jednego poziomu zagnieżdżenia:

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

->>, ->a #>operatorzy są objaśnieni w instrukcji.

Oba zapytania używają niejawnego JOIN LATERAL.

SQL Fiddle.

Ściśle powiązana odpowiedź:

jsonb w Postgres 9.4+

Użyj odpowiednika jsonb_array_elements().

Jeszcze lepiej , użyj nowego operatora „zawiera” @>(najlepiej w połączeniu z pasującym indeksem GIN w wyrażeniu 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"}]';

Ponieważ klucz objectszawiera tablicę JSON , musimy dopasować strukturę do wyszukiwanego hasła i zawinąć element tablicy również w nawiasy kwadratowe. Usuń nawiasy tablicowe podczas wyszukiwania zwykłego rekordu.

Szczegółowe wyjaśnienie i więcej opcji:

Erwin Brandstetter
źródło
1
@pacothelovetaco: dodano aktualizację dla jsonb/ pg 9.4. Poza tym: dla prostego przypadku (1 poziom zagnieżdżenia), ->operator również robi sztuczkę jsonw str. 9.3.
Erwin Brandstetter
1
@pacothelovetaco, dla pg 9.3, '#>' nie jest sekretnym sosem, '->' byłby w sam raz w twoim przypadku, ponieważ zwraca również objec json. '#>' byłby bardziej pomocny w przypadku zagnieżdżonej ścieżki JSON, ponieważ pozwala łatwo określić ścieżkę w '{}'
Gob00st
1
@> '[{"src": "foo.png"}]'; działa dobrze w jakim stanie, ale jak usunąć taki obiekt? nie znam indeksu tego obiektu. chcę usunąć według wartości klucza.
Pranay Soni,
1
@PranaySoni: Zadaj nowe pytanie jako pytanie . Komentarze nie są miejscem. Zawsze możesz utworzyć link do tego, aby uzyskać kontekst.
Erwin Brandstetter,
drogi @ErwinBrandstetter, czy można znaleźć oba dokumenty przez częściowe dopasowanie? Na przykład chciałbym uzyskać oba nagrania, coś takiego '[{"src": ".
Png
8

Utwórz tabelę z kolumną typu JSON

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

Teraz wstawmy dane 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}');

Zróbmy teraz kilka zapytań, aby pobrać dane

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

Być może zauważyłeś, że wyniki zawierają odwrócony przecinek (") i nawiasy kwadratowe ([])

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

Teraz, aby pobrać tylko wartości, po prostu użyj ->>

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';
Sandip Debnath
źródło
22
Jest to przyjemnie sformatowany szum bez zauważalnego związku z pytaniem.
Erwin Brandstetter
4
Znalazłem to przydatne. Pokazuje, jak drążyć tablicę w jsonb
GavinBelson
0

wybierz dane -> 'obiekty' -> 0 -> 'src' jako SRC z tabeli gdzie dane -> 'obiekty' -> 0 -> 'src' = 'foo.png'

anand shukla
źródło
2
Byłoby to przydatne TYLKO JEŚLI znasz indeks, który wynosił 0.
Buyut Joko Rivai
tak, ale istnieje sposób na rozbicie obiektu tablicy, który będzie mapował wiersz mądrze i możemy go użyć. Popraw mnie, jeśli się mylę.
anand shukla
nie jest to dobre rozwiązanie, ponieważ nie możesz być pewien, "src" jest na pozycji 0.
simUser