2016-03-18 20 views
14

Utilizzo di Postgres 9.4, Voglio creare un indice su una colonna JSON che verrà utilizzata durante la ricerca su chiavi specifiche all'interno della colonna.Indice PostgreSQL su JSON

Ad esempio, ho una tabella "farm" con una colonna JSON "animals".

La colonna animali ha oggetti JSON del formato generale:

'{"cow": 2, "chicken": 11, "horse": 3}' 

Ho provato una serie di indici (a parte):

(1) create INDEX animal_index ON farm ((animal ->> 'cow')); 
(2) create INDEX animal_index ON farm using gin ((animal ->> 'cow')); 
(3) create INDEX animal_index ON farm using gist ((animal ->> 'cow')); 

voglio eseguire le query come:

SELECT * FROM farm WHERE (animal ->> 'cow') > 3; 

e avere questa query utilizzare l'indice.

Quando faccio funzionare questa domanda:

SELECT * FROM farm WHERE (animal ->> 'cow') is null; 

poi la (1) Indice funziona, ma non riesco a ottenere qualsiasi degli indici a lavorare per la disuguaglianza.

Un tale indice è possibile?

La tabella della fattoria contiene solo ~ 5000 fattorie, ma alcune contengono 100 di animali e le query richiedono troppo tempo per il mio caso d'uso. Un indice come questo è l'unico metodo che posso immaginare per accelerare questa query, ma forse c'è un'altra opzione.

risposta

29

I suoi altri due indici non funzioneranno semplicemente perché i rendimenti ->> operatortext, mentre si hanno ovviamente le classi operatore jsonb gin in mente. Si noti che si parla solo di json, ma è effettivamente necessario jsonb per funzionalità di indicizzazione avanzate.

Per elaborare la migliore strategia di indicizzazione, è necessario definire più da vicino quali query coprire. Ti interessano solo le mucche? O tutti gli animali/tutti i tag? Quali operatori sono possibili? Il tuo documento JSON include anche chiavi non animali? Cosa fare con quelli? Vuoi includere righe nell'indice in cui le mucche (o qualsiasi altra cosa) non vengono visualizzate nel documento JSON?

presupponendo:

  • Siamo interessati solo mucche al primo livello di nidificazione.
  • Il valore è sempre valido integer.
  • Non siamo interessati a file senza mucche.

Suggerisco un indice btree funzionale, molto simile a quello che hai già, ma inserisci il valore in intero. Non credo che vorreste il confronto valutato come text (dove '2' è maggiore di '1111').

CREATE INDEX animal_index ON farm (((animal ->> 'cow')::int)); -- ! 

L'ulteriore set di parentesi è necessaria per la stenografia getto di rendere la sintassi per l'espressione di indice inequivocabile.

Utilizzare la stessa espressione nelle query per rendere Postgres realizzare l'indice è applicabile:

SELECT * FROM farm WHERE (animal ->> 'cow')::int > 3; 

Se avete bisogno di un più generico indice di jsonb, prendere in considerazione:

Per un noto, statico, banale numero di animali (come te u commentato), vi suggerisco di indici parziali come:

CREATE INDEX animal_index ON farm (((animal ->> 'cow')::int)) 
WHERE (animal ->> 'cow') IS NOT NULL; 

CREATE INDEX animal_index ON farm (((animal ->> 'chicken')::int)) 
WHERE (animal ->> 'chicken') IS NOT NULL; 

Ecc

Potrebbe essere necessario aggiungere la condizione dell'indice alla query:

SELECT * FROM farm 
WHERE (animal ->> 'cow')::int > 3 
AND (animal ->> 'cow') IS NOT NULL; 

può sembrare ridondante, ma può essere necessario . Prova con ANALYZE!

+0

Grazie! Meravigliosa risposta Mi interessa solo il primo livello. Il valore è sempre un numero intero. Comunque sono interessato ad alcuni altri animali. Ha senso replicare semplicemente questo indice per ogni animale a cui sono interessato? – lnhubbell

+0

@lnhubbell: Per un * numero di animali noto, statico, banale, questa dovrebbe essere la soluzione più semplice ed efficiente. Vorrei esplicitamente renderli indici parziali, però. Vedi l'addendum sopra. –

Problemi correlati