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.