Em um banco de dados do Postgres 9.1, tenho uma tabela table1
com ~ 1.5M linhas e uma coluna label
(nomes simplificados para fins de pergunta).
Existe um índice trigrama funcional ativado lower(unaccent(label))
( unaccent()
foi tornado imutável para permitir seu uso no índice).
A seguinte consulta é bastante rápida:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword%')));
count
-------
1
(1 row)
Time: 394,295 ms
Mas a seguinte consulta é mais 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 adicionar mais palavras é ainda mais lento, mesmo que a pesquisa seja mais rigorosa.
Tentei um truque simples para executar uma subconsulta para a primeira palavra e, em seguida, uma consulta com a string de pesquisa completa, mas (infelizmente) o planejador de consultas viu através de minhas maquinações:
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%'));
Análise de Heap de Bitmap na tabela1 (custo = 16216.01..16220.04 linhas = 1 largura = 212) (tempo real = 1824.017..1824.019 linhas = 1 loops = 1) Verifique novamente Cond: ((lower (unaccent ((label) :: text)) ~~ '% someord%' :: text) AND (lower (unaccent ((label) :: text)) ~~ '% someord e mais %'::texto)) -> Varredura de índice de bitmap na tabela1_label_hun_gin_trgm (custo = 0.00..16216.01 linhas = 1 largura = 0) (tempo real = 1823.900..1823.900 linhas = 1 loops = 1) Índice Cond: ((lower (unaccent ((label) :: text)) ~~ '% someord%' :: text) AND (lower (unaccent ((label) :: text)) ~~ '% someord e mais alguns %'::texto)) Tempo de execução total: 1824.064 ms
Meu problema final é que a string de pesquisa vem de uma interface da web que pode enviar strings bastante longas e, portanto, bastante lenta e também pode constituir um vetor DOS.
Então, minhas perguntas são:
- Como acelerar a consulta?
- Existe uma maneira de dividi-lo em subconsultas para que seja mais rápido?
- Talvez uma versão posterior do Postgres seja melhor? (Eu tentei a 9.4 e não parece mais rápido: ainda o mesmo efeito. Talvez uma versão posterior?)
- Talvez seja necessária uma estratégia de indexação diferente?
unaccent
imutável. Eu adicionei isso à pergunta.
unaccent
módulo. Uma das razões pelas quais sugiro um wrapper de função.
unaccent()
também é fornecido por um módulo adicional e o Postgres não suporta índices na função por padrão, pois não éIMMUTABLE
. Você deve ter alterado alguma coisa e deve mencionar o que fez exatamente na sua pergunta. Meu conselho permanente: stackoverflow.com/a/11007216/939860 . Além disso, os índices trigramas oferecem suporte à correspondência sem distinção entre maiúsculas e minúsculas. Você pode simplificar para:WHERE f_unaccent(label) ILIKE f_unaccent('%someword%')
- com um índice correspondente. Detalhes: stackoverflow.com/a/28636000/939860 .