PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Index zum Suchen eines Elements in einem JSON-Array

jsonb in Postgres 9.4+

Der binäre JSON-Datentyp jsonb verbessert die Indexoptionen erheblich. Sie können jetzt einen GIN-Index auf einem jsonb haben Array direkt:

CREATE TABLE tracks (id serial, artists jsonb);  -- !
CREATE INDEX tracks_artists_gin_idx ON tracks USING gin (artists);

Es ist keine Funktion zum Konvertieren des Arrays erforderlich. Dies würde eine Abfrage unterstützen:

SELECT * FROM tracks WHERE artists @> '[{"name": "The Dirty Heads"}]';

@> der jsonb ist "enthält"-Operator, der den GIN-Index verwenden kann. (Nicht für json , nur jsonb !)

Oder Sie verwenden die speziellere, nicht standardmäßige GIN-Operatorklasse jsonb_path_ops für den Index:

CREATE INDEX tracks_artists_gin_idx ON tracks
USING  gin (artists jsonb_path_ops);  -- !

Gleiche Abfrage.

Derzeit jsonb_path_ops unterstützt nur den @> Operator. Aber es ist normalerweise viel kleiner und schneller. Es gibt weitere Indexoptionen, Details im Handbuch .

Wenn die Spalte artists nur Namen enthält, wie im Beispiel angezeigt, wäre es effizienter, nur die Werte zu speichern als JSON-Text Primitive und der redundante Schlüssel kann der Spaltenname sein.

Beachten Sie den Unterschied zwischen JSON-Objekten und primitiven Typen:

  • Verwenden von Indizes in einem json-Array in PostgreSQL
CREATE TABLE tracks (id serial, artistnames jsonb);
INSERT INTO tracks  VALUES (2, '["The Dirty Heads", "Louis Richards"]');

CREATE INDEX tracks_artistnames_gin_idx ON tracks USING gin (artistnames);

Abfrage:

SELECT * FROM tracks WHERE artistnames ? 'The Dirty Heads';

? funktioniert nicht für Objektwerte , nur Schlüssel und Array-Elemente .

Oder:

CREATE INDEX tracks_artistnames_gin_idx ON tracks
USING  gin (artistnames jsonb_path_ops);

Abfrage:

SELECT * FROM tracks WHERE artistnames @> '"The Dirty Heads"'::jsonb;

Effizienter, wenn Namen stark dupliziert werden.

json in Postgres 9.3+

Dies sollte mit einem IMMUTABLE funktionieren Funktion :

CREATE OR REPLACE FUNCTION json2arr(_j json, _key text)
  RETURNS text[] LANGUAGE sql IMMUTABLE AS
'SELECT ARRAY(SELECT elem->>_key FROM json_array_elements(_j) elem)';

Erstellen Sie diesen funktionalen Index :

CREATE INDEX tracks_artists_gin_idx ON tracks
USING  gin (json2arr(artists, 'name'));

Und verwenden Sie eine Abfrage so was. Der Ausdruck im WHERE -Klausel muss mit der im Index übereinstimmen:

SELECT * FROM tracks
WHERE  '{"The Dirty Heads"}'::text[] <@ (json2arr(artists, 'name'));

Aktualisiert mit Feedback in den Kommentaren. Wir müssen Array-Operatoren verwenden um den GIN-Index zu unterstützen.
Der "ist enthalten in"-Operator <@ in diesem Fall.

Hinweise zur Funktionsvolatilität

Sie können Ihre Funktion IMMUTABLE deklarieren auch wenn json_array_elements() ist nicht war nicht.
Die meisten JSON Funktionen waren früher nur STABLE , nicht IMMUTABLE . Es gab eine Diskussion auf der Hackerliste, um das zu ändern. Die meisten sind IMMUTABLE jetzt. Überprüfen Sie mit:

SELECT p.proname, p.provolatile
FROM   pg_proc p
JOIN   pg_namespace n ON n.oid = p.pronamespace
WHERE  n.nspname = 'pg_catalog'
AND    p.proname ~~* '%json%';

Funktionale Indizes funktionieren nur mit IMMUTABLE Funktionen.