Kueri untuk elemen array di dalam tipe JSON

118

Saya mencoba menguji jsontipe di PostgreSQL 9.3.
Saya memiliki jsonkolom yang disebut datadalam tabel bernama reports. JSON terlihat seperti ini:

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

Saya ingin meminta tabel untuk semua laporan yang cocok dengan nilai 'src' dalam larik 'objek'. Misalnya, apakah mungkin untuk menanyakan DB untuk semua laporan yang cocok 'src' = 'foo.png'? Saya berhasil menulis kueri yang cocok dengan "background":

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

Tetapi karena "objects"memiliki larik nilai, sepertinya saya tidak dapat menulis sesuatu yang berfungsi. Apakah mungkin untuk menanyakan DB untuk semua laporan yang cocok 'src' = 'foo.png'? Saya telah memeriksa sumber-sumber ini tetapi masih belum bisa mendapatkannya:

Saya juga sudah mencoba hal-hal seperti ini tetapi tidak berhasil:

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

Saya bukan ahli SQL, jadi saya tidak tahu apa yang saya lakukan salah.

pacothelovetaco.dll
sumber

Jawaban:

215

json di Postgres 9.3+

Batalkan senar array JSON dengan fungsi json_array_elements()dalam gabungan lateral dalam FROMklausa dan uji elemennya:

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

The CTE ( WITHquery) hanya pengganti meja reports.
Atau, setara hanya untuk satu tingkat bersarang:

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

->>, ->dan #>operator dijelaskan di manual.

Kedua kueri menggunakan implisit JOIN LATERAL.

SQL Fiddle.

Jawaban terkait erat:

jsonb di Postgres 9.4+

Gunakan yang setara jsonb_array_elements().

Lebih baik lagi, gunakan operator "berisi" baru @>(paling baik jika dikombinasikan dengan indeks GIN yang cocok pada ekspresi 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"}]';

Karena kunci objectsmenampung larik JSON , kita perlu mencocokkan struktur dalam istilah penelusuran dan menggabungkan elemen larik ke dalam tanda kurung siku juga. Lepaskan tanda kurung larik saat mencari rekaman biasa.

Penjelasan rinci dan lebih banyak pilihan:

Erwin Brandstetter
sumber
1
@pacothelovetaco: menambahkan pembaruan untuk jsonb/ hal 9.4. Selain: untuk kasus sederhana (1 level bersarang), ->operator juga melakukan trik untuk jsondi halaman 9.3.
Erwin Brandstetter
1
@pacothelovetaco, untuk pg 9.3, '#>' bukanlah saus rahasia, '->' akan baik-baik saja untuk kasus Anda karena ini juga mengembalikan objek json. '#>' akan lebih membantu dalam kasus jalur json bersarang karena ini memungkinkan Anda dengan mudah menentukan jalur di '{}'
Gob00st
1
@> '[{"src": "foo.png"}]'; berfungsi dengan baik dalam kondisi di mana tetapi bagaimana cara menghapus objek tertentu seperti ini? saya tidak tahu indeks objek ini. saya ingin menghapus dengan nilai kunci.
Pranay Soni
1
@PranaySoni: Silakan ajukan pertanyaan baru sebagai pertanyaan . Komentar bukanlah tempatnya. Anda selalu dapat menautkan yang ini untuk konteks.
Erwin Brandstetter
Dear @ErwinBrandstetter, apakah mungkin untuk menemukan kedua dokumen dengan pencocokan parsial? Misalnya saya ingin mendapatkan kedua rekaman seperti itu '[{"src": ". Png"}]'
Pyrejkee
8

Buat tabel dengan kolom tipe json

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

Sekarang mari kita masukkan data 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}');

Sekarang mari membuat beberapa kueri untuk mengambil data

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

Anda mungkin telah memperhatikan bahwa hasilnya muncul dengan koma terbalik (") dan tanda kurung ([])

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

Sekarang untuk mengambil hanya nilai-nilai saja gunakan ->>

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
sumber
22
Ini adalah suara berformat menyenangkan tanpa kaitan yang jelas dengan pertanyaan.
Erwin Brandstetter
4
Saya menemukan ini berguna. Menunjukkan cara menelusuri ke dalam larik di jsonb
GavinBelson
0

pilih data -> 'objek' -> 0 -> 'src' sebagai SRC dari tabel tempat data -> 'objek' -> 0 -> 'src' = 'foo.png'

anand shukla
sumber
2
Ini akan berguna HANYA JIKA Anda tahu indeksnya, yaitu 0.
Buyut Joko Rivai
ya tetapi ada cara untuk meledakkan objek array yang akan memetakan baris secara bijaksana dan kita dapat menggunakannya. Koreksi saya jika saya salah.
anand shukla
bukan solusi yang baik karena Anda tidak yakin, "src" ada di posisi 0.
simUser