Interroga gli elementi dell'array all'interno del tipo JSON


118

Sto provando a testare il jsontipo in PostgreSQL 9.3.
Ho una jsoncolonna chiamata datain una tabella chiamata reports. Il JSON ha un aspetto simile a questo:

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

Vorrei interrogare la tabella per tutti i rapporti che corrispondono al valore "src" nella matrice "oggetti". Ad esempio, è possibile interrogare il DB per tutti i report che corrispondono 'src' = 'foo.png'? Ho scritto con successo una query che può corrispondere a "background":

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

Ma poiché "objects"ha una serie di valori, non riesco a scrivere qualcosa che funzioni. È possibile interrogare il DB per tutti i report che corrispondono 'src' = 'foo.png'? Ho esaminato queste fonti ma ancora non riesco a ottenerlo:

Ho anche provato cose come questa ma senza successo:

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

Non sono un esperto di SQL, quindi non so cosa sto facendo di sbagliato.

Risposte:


215

json in Postgres 9.3+

Annulla l'annidamento dell'array JSON con la funzione json_array_elements()in un join laterale nella FROMclausola e verifica i suoi elementi:

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

La CTE ( WITHquery) sostituisce solo una tabella reports.
Oppure, equivalente per un solo livello di nidificazione:

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

->>, ->e gli #>operatori sono spiegati nel manuale.

Entrambe le query usano un implicito JOIN LATERAL.

SQL Fiddle.

Risposta strettamente correlata:

jsonb in Postgres 9.4+

Usa l'equivalente jsonb_array_elements().

Meglio ancora, usa il nuovo operatore "contiene" @>(migliore in combinazione con un indice GIN corrispondente sull'espressione 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"}]';

Poiché la chiave objectscontiene un array JSON , dobbiamo abbinare la struttura nel termine di ricerca e racchiudere anche l'elemento dell'array tra parentesi quadre. Rilascia le parentesi dell'array durante la ricerca di un record normale.

Spiegazione dettagliata e altre opzioni:


1
@pacothelovetaco: aggiunto un aggiornamento per jsonb/ pg 9.4. A parte: per il caso semplice (1 livello di annidamento), l' ->operatore esegue anche il trucco per jsona pag. 9.3.
Erwin Brandstetter

1
@pacothelovetaco, per pg 9.3, "#>" non è la salsa segreta, "->" andrebbe bene per il tuo caso in quanto restituisce anche json objec. "#>" sarebbe più utile nel caso del percorso json nidificato in quanto consente di specificare facilmente il percorso in "{}"
Gob00st

1
@> '[{"src": "foo.png"}]'; funziona bene in dove condizione ma come eliminare un oggetto particolare come questo? non conosco l'indice di questo oggetto. voglio eliminare per valore chiave.
Pranay Soni

1
@PranaySoni: si prega di porre la nuova domanda come domanda . I commenti non sono il posto giusto. Puoi sempre collegarti a questo per il contesto.
Erwin Brandstetter

caro @ErwinBrandstetter, è possibile trovare entrambi i documenti mediante abbinamento parziale? Ad esempio, vorrei ottenere entrambi i record qualcosa del genere "[{" src ":". Png "}]"
Pyrejkee

8

Crea una tabella con colonna come tipo json

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

Ora inseriamo i dati 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}');

Ora facciamo alcune query per recuperare i dati

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

Avrai notato che i risultati vengono forniti con virgole invertite (") e parentesi ([])

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

Ora per recuperare solo i valori basta usare ->>

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
Questo è un rumore piacevolmente formattato senza connessione visibile alla domanda.
Erwin Brandstetter

4
L'ho trovato utile. Mostra come eseguire il drill nella matrice in un jsonb
GavinBelson

0

seleziona data -> 'objects' -> 0 -> 'src' come SRC dalla tabella dove data -> 'objects' -> 0 -> 'src' = 'foo.png'


2
Questo sarebbe utile SOLO SE conosci l'indice, che era 0.
Buyut Joko Rivai

sì, ma c'è un modo per esplodere un oggetto array che mapperà saggiamente le righe e possiamo usarlo. Correggimi se sbaglio.
anand shukla

non è una buona soluzione perché non puoi esserne sicuro, "src" è in posizione 0.
simUser
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.