jsonb in Postgres 9.4+
Il tipo di dati JSON binario jsonb migliora ampiamente le opzioni dell'indice. Ora puoi avere un indice GIN su un jsonb array direttamente:
CREATE TABLE tracks (id serial, artists jsonb); -- !
CREATE INDEX tracks_artists_gin_idx ON tracks USING gin (artists);
Non c'è bisogno di una funzione per convertire l'array. Ciò supporterebbe una query:
SELECT * FROM tracks WHERE artists @> '[{"name": "The Dirty Heads"}]';
@> essendo il jsonb operatore "contiene", che può utilizzare l'indice GIN. (Non per json , solo jsonb !)
Oppure usi la classe dell'operatore GIN più specializzata e non predefinita jsonb_path_ops per l'indice:
CREATE INDEX tracks_artists_gin_idx ON tracks
USING gin (artists jsonb_path_ops); -- !
Stessa domanda.
Attualmente jsonb_path_ops supporta solo il @> operatore. Ma in genere è molto più piccolo e veloce. Ci sono più opzioni per l'indice, dettagli nel manuale .
Se la colonna artists contiene solo i nomi visualizzati nell'esempio, sarebbe più efficiente memorizzare solo i valori come testo JSON primitive e la chiave ridondante può essere il nome della colonna.
Nota la differenza tra oggetti JSON e tipi primitivi:
- Utilizzo degli indici nell'array json 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); Domanda:
SELECT * FROM tracks WHERE artistnames ? 'The Dirty Heads';
? non funziona per i valori degli oggetti , solo chiavi e elementi della matrice .
Oppure:
CREATE INDEX tracks_artistnames_gin_idx ON tracks
USING gin (artistnames jsonb_path_ops);
Domanda:
SELECT * FROM tracks WHERE artistnames @> '"The Dirty Heads"'::jsonb;
Più efficiente se i nomi sono altamente duplicati.
json in Postgres 9.3+
Questo dovrebbe funzionare con un IMMUTABLE funzione :
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)';
Crea questo indice funzionale :
CREATE INDEX tracks_artists_gin_idx ON tracks
USING gin (json2arr(artists, 'name'));
E usa una query come questo. L'espressione nel WHERE la clausola deve corrispondere a quella nell'indice:
SELECT * FROM tracks
WHERE '{"The Dirty Heads"}'::text[] <@ (json2arr(artists, 'name'));
Aggiornato con feedback nei commenti. Dobbiamo utilizzare operatori di array per supportare l'indice GIN.
L'operatore "è contenuto da" <@ in questo caso.
Note sulla volatilità delle funzioni
Puoi dichiarare la tua funzione IMMUTABLE anche se json_array_elements() non è non lo era.
La maggior parte dei JSON le funzioni erano solo STABLE , non IMMUTABLE . C'è stata una discussione sull'elenco degli hacker per cambiarlo. La maggior parte sono IMMUTABLE adesso. Verifica con:
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%';
Gli indici funzionali funzionano solo con IMMUTABLE funzioni.