Abfrage nach Array-Elementen innerhalb des JSON-Typs

118

Ich versuche, den jsonTyp in PostgreSQL 9.3 zu testen .
Ich habe eine jsonSpalte datain einer Tabelle namens aufgerufen reports. Der JSON sieht ungefähr so ​​aus:

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

Ich möchte die Tabelle nach allen Berichten abfragen, die mit dem Wert 'src' im Array 'objects' übereinstimmen. Ist es beispielsweise möglich, die Datenbank nach allen übereinstimmenden Berichten abzufragen 'src' = 'foo.png'? Ich habe erfolgreich eine Abfrage geschrieben, die mit Folgendem übereinstimmt "background":

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

Aber da "objects"es eine Reihe von Werten gibt, kann ich scheinbar nichts schreiben, was funktioniert. Ist es möglich, die Datenbank nach allen übereinstimmenden Berichten abzufragen 'src' = 'foo.png'? Ich habe diese Quellen durchgesehen, kann sie aber immer noch nicht verstehen:

Ich habe auch solche Dinge ausprobiert, aber ohne Erfolg:

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

Ich bin kein SQL-Experte, daher weiß ich nicht, was ich falsch mache.

Pacothelovetaco
quelle

Antworten:

214

json in Postgres 9.3+

Deaktivieren Sie das JSON-Array mit der Funktion json_array_elements()in einem lateralen Join in der FROMKlausel und testen Sie seine Elemente:

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

Der CTE ( WITHAbfrage) ersetzt nur eine Tabelle reports.
Oder, das entspricht nur für eine einzige Ebene der Verschachtelung:

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

->>, ->Und #>Operatoren im Handbuch erläutert.

Beide Abfragen verwenden ein implizites JOIN LATERAL.

SQL Fiddle.

Eng verwandte Antwort:

jsonb in Postgres 9.4+

Verwenden Sie das Äquivalent jsonb_array_elements().

Besser noch, verwenden Sie den neuen Operator "enthält" @>(am besten in Kombination mit einem passenden GIN-Index für den Ausdruck 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"}]';

Da der Schlüssel objectsein JSON- Array enthält , müssen wir die Struktur im Suchbegriff anpassen und das Array-Element auch in eckige Klammern setzen. Löschen Sie die Array-Klammern, wenn Sie einen einfachen Datensatz durchsuchen.

Detaillierte Erklärung und weitere Optionen:

Erwin Brandstetter
quelle
1
@pacothelovetaco: Update für jsonb/ pg 9.4 hinzugefügt . Nebenbei: Für den einfachen Fall (1 Verschachtelungsebene) macht der ->Bediener auch den Trick für jsonin S. 9.3.
Erwin Brandstetter
1
@pacothelovetaco, für Seite 9.3 ist '#>' nicht die geheime Sauce, '->' wäre in Ordnung für Ihren Fall, da es auch das json-Objekt zurückgibt. '#>' wäre im Fall eines verschachtelten JSON-Pfads hilfreicher, da Sie den Pfad auf einfache Weise im '{}'
Gob00st
1
@> '[{"src": "foo.png"}]'; funktionieren gut unter welchen Bedingungen, aber wie löscht man ein bestimmtes Objekt wie dieses? Ich kenne den Index dieses Objekts nicht. Ich möchte nach Schlüsselwert löschen.
Pranay Soni
1
@PranaySoni: Bitte stellen Sie die neue Frage als Frage . Kommentare sind nicht der richtige Ort. Sie können jederzeit einen Link zu diesem für den Kontext erstellen.
Erwin Brandstetter
Lieber @ErwinBrandstetter, ist es möglich, beide Dokumente durch partiellen Abgleich zu finden? Zum Beispiel möchte ich beide Platten so etwas wie '[{"src": ". Png"}]'
Pyrejkee
7

Erstellen Sie eine Tabelle mit einer Spalte vom Typ json

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

Fügen wir nun json-Daten ein

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

Lassen Sie uns nun einige Abfragen zum Abrufen von Daten durchführen

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

Möglicherweise haben Sie bemerkt, dass die Ergebnisse mit Anführungszeichen (") und Klammern ([]) versehen sind.

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

Um nur die Werte abzurufen, verwenden Sie einfach ->>

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
quelle
22
Dies ist angenehm formatiertes Rauschen ohne erkennbaren Zusammenhang mit der Frage.
Erwin Brandstetter
4
Ich fand das nützlich. Zeigt, wie man in einem jsonb in das Array bohrt
GavinBelson
0

Wählen Sie Daten -> 'Objekte' -> 0 -> 'src' als SRC aus der Tabelle, in der Daten -> 'Objekte' -> 0 -> 'src' = 'foo.png'

anand shukla
quelle
2
Dies wäre NUR dann nützlich, wenn Sie den Index kennen, der 0 war.
Buyut Joko Rivai
Ja, aber es gibt eine Möglichkeit, ein Array-Objekt zu explodieren, das zeilenweise zugeordnet wird, und wir können das verwenden. Korrigieren Sie mich, wenn ich falsch liege.
Anand Shukla
Keine