Requête pour les éléments de tableau dans le type JSON

118

J'essaye de tester le jsontype dans PostgreSQL 9.3.
J'ai une jsoncolonne appelée datadans une table appelée reports. Le JSON ressemble à quelque chose comme ceci:

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

Je voudrais interroger la table pour tous les rapports qui correspondent à la valeur «src» dans le tableau «objets». Par exemple, est-il possible d'interroger la base de données pour tous les rapports qui correspondent 'src' = 'foo.png'? J'ai écrit avec succès une requête qui peut correspondre à "background":

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

Mais depuis "objects"a un tableau de valeurs, je n'arrive pas à écrire quelque chose qui fonctionne. Est-il possible d'interroger la base de données pour tous les rapports qui correspondent 'src' = 'foo.png'? J'ai parcouru ces sources mais je ne peux toujours pas l'obtenir:

J'ai également essayé des choses comme celle-ci, mais en vain:

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

Je ne suis pas un expert SQL, donc je ne sais pas ce que je fais de mal.

pacothelovetaco
la source

Réponses:

215

json dans Postgres 9.3+

Annulez le tableau JSON avec la fonction json_array_elements()dans une jointure latérale dans la FROMclause et testez ses éléments:

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

Le CTE ( WITHrequête) remplace simplement une table reports.
Ou, équivalent pour un seul niveau d'imbrication:

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

->>, ->Et #>dans le manuel des opérateurs sont expliqués.

Les deux requêtes utilisent un implicite JOIN LATERAL.

SQL Fiddle.

Réponse étroitement liée:

jsonb dans Postgres 9.4+

Utilisez l'équivalent jsonb_array_elements().

Mieux encore, utilisez le nouvel opérateur "contient" @>(le mieux en combinaison avec un index GIN correspondant sur l'expression 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"}]';

Étant donné que la clé objectscontient un tableau JSON , nous devons faire correspondre la structure dans le terme de recherche et envelopper l'élément du tableau entre crochets également. Supprimez les crochets du tableau lors de la recherche d'un enregistrement simple.

Explication détaillée et plus d'options:

Erwin Brandstetter
la source
1
@pacothelovetaco: ajout d'une mise à jour pour jsonb/ pg 9.4. A part: pour le cas simple (1 niveau d'imbrication), l' ->opérateur fait également le tour de la jsonpage 9.3.
Erwin Brandstetter
1
@pacothelovetaco, pour pg 9.3, '#>' n'est pas la sauce secrète, '->' conviendrait parfaitement à votre cas car il renvoie également l'objet json. '#>' serait plus utile dans le cas de chemin json imbriqué car il vous permet de spécifier facilement le chemin dans le '{}'
Gob00st
1
@> '[{"src": "foo.png"}]'; fonctionne bien dans la condition où mais comment supprimer un objet particulier comme celui-ci? je ne connais pas l'index de cet objet. je veux supprimer par valeur de clé.
Pranay Soni
1
@PranaySoni: Veuillez poser la nouvelle question comme question . Les commentaires ne sont pas l'endroit. Vous pouvez toujours créer un lien vers celui-ci pour le contexte.
Erwin Brandstetter
cher @ErwinBrandstetter, est-il possible de trouver les deux documents par correspondance partielle? Par exemple, j'aimerais obtenir les deux enregistrements quelque chose comme ça '[{"src": ". Png"}]'
Pyrejkee
8

Créer une table avec une colonne de type json

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

Maintenant, insérons des données 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}');

Maintenant, faisons quelques requêtes pour récupérer des données

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

Vous avez peut-être remarqué que les résultats sont accompagnés d'une virgule inversée (") et de crochets ([])

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

Maintenant, pour récupérer uniquement les valeurs, utilisez simplement ->>

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
la source
22
C'est un bruit agréablement formaté sans lien perceptible avec la question.
Erwin Brandstetter
4
J'ai trouvé cela utile. Montre comment percer dans le tableau dans un jsonb
GavinBelson
0

sélectionnez data -> 'objects' -> 0 -> 'src' as SRC from table where data -> 'objects' -> 0 -> 'src' = 'foo.png'

anand shukla
la source
2
Cela ne serait utile QUE SI vous connaissez l'indice, qui était de 0.
Buyut Joko Rivai
oui mais il existe un moyen d'exploser un objet de tableau qui mappera par ligne et nous pouvons l'utiliser. Corrigez-moi si je me trompe.
anand shukla
pas une bonne solution car vous ne pouvez pas être sûr, "src" est en position 0.
simUser