In un database Postgres 9.1, ho una tabella table1con ~ 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?
unaccentimmutabile. Ho aggiunto questo alla domanda.
unaccentmodulo. 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 .