In un database Postgres 9.1, ho una tabella table1
con ~ 1,5 M righe e una colonna label
(nomi semplificati per il bene di questa domanda).
C'è un trigramma-indice attivo su lower(unaccent(label))
( unaccent()
è stato reso immutabile per consentirne l'uso nell'indice).
La seguente query è abbastanza veloce:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword%')));
count
-------
1
(1 row)
Time: 394,295 ms
Ma la seguente query è più lenta:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword and some more%')));
count
-------
1
(1 row)
Time: 1405,749 ms
E l'aggiunta di più parole è ancora più lenta, anche se la ricerca è più rigorosa.
Ho provato un semplice trucco per eseguire una sottoquery per la prima parola e poi una query con la stringa di ricerca completa, ma (purtroppo) il pianificatore di query ha visto attraverso le mie macchinazioni:
EXPLAIN ANALYZE
SELECT * FROM (
SELECT id, title, label from table1
WHERE lower(unaccent(label)) like lower(unaccent('%someword%'))
) t1
WHERE lower(unaccent(label)) like lower(unaccent('%someword and some more%'));
Scansione heap bitmap su tabella1 (costo = 16216.01..16220.04 righe = 1 larghezza = 212) (tempo effettivo = 1824.017..1824.019 righe = 1 loop = 1) Ricontrolla Cond: ((lower (unaccent ((label) :: text)) ~~ '% someword%' :: text) AND (lower (unaccent ((label) :: text)) ~~ '% someword e alcuni altri %'::testo)) -> Scansione indice bitmap su table1_label_hun_gin_trgm (costo = 0,00..16216,01 righe = 1 larghezza = 0) (tempo effettivo = 1823.900..1823.900 righe = 1 loop = 1) Index Cond: ((lower (unaccent ((label) :: text)) ~~ '% someword%' :: text) AND (lower (unaccent ((label) :: text)) ~~ '% someword e alcuni altri %'::testo)) Durata totale: 1824.064 ms
Il mio ultimo problema è che la stringa di ricerca proviene da un'interfaccia web che può inviare stringhe piuttosto lunghe e quindi essere piuttosto lenta e può anche costituire un vettore DOS.
Quindi le mie domande sono:
- Come velocizzare la query?
- C'è un modo per dividerlo in sottoquery in modo che sia più veloce?
- Forse una versione successiva di Postgres è migliore? (Ho provato 9.4 e non sembra più veloce: sempre lo stesso effetto. Forse una versione successiva?)
- Forse è necessaria una diversa strategia di indicizzazione?
unaccent
immutabile. Ho aggiunto questo alla domanda.
unaccent
modulo. Uno dei motivi per cui suggerisco invece un wrapper di funzioni.
unaccent()
è fornito anche da un modulo aggiuntivo e Postgres non supporta gli indici sulla funzione di default poiché non lo èIMMUTABLE
. Devi aver modificato qualcosa e dovresti menzionare ciò che hai fatto esattamente nella tua domanda. Il mio consiglio permanente: stackoverflow.com/a/11007216/939860 . Inoltre, gli indici trigram supportano la corrispondenza senza distinzione tra maiuscole e minuscole. È possibile semplificare a:WHERE f_unaccent(label) ILIKE f_unaccent('%someword%')
- con un indice corrispondente. Dettagli: stackoverflow.com/a/28636000/939860 .