web-dev-qa-db-fra.com

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

J'essaie de tester le type json dans PostgreSQL 9.3.
J'ai une colonne json appelée data dans une table appelée reports. Le JSON ressemble à ceci:

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

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

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

Mais comme "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 correspondant à 'src' = 'foo.png'? J'ai parcouru ces sources mais je ne peux toujours pas l'obtenir:

J'ai aussi essayé des choses comme ça mais en vain:

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

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

64
pacothelovetaco

json dans Postgres 9.3+

Annulez l'activation du tableau JSON avec la fonction json_array_elements() dans une jointure latérale dans la clause FROM et testez-en les é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';

La requête CTE (requête WITH) ne fait que remplacer une table reports.
Ou équivalent pour seulement un célibataire niveau d'imbrication:

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

Les opérateurs ->>, -> et #> sont expliqués dans le manuel.

Les deux requêtes utilisent un 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" @> (idéal 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"}]';

Puisque la clé objects contient un JSON tableau, nous devons faire correspondre la structure dans le terme de recherche et placer l'élément de tableau entre crochets. Supprimez les crochets du tableau lors de la recherche d'un enregistrement brut.

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

148
Erwin Brandstetter

Créer une table avec une colonne de type json

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

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

Faisons maintenant 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 de virgules inversées (") 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';
0
Sandip Debnath