DadosAbertosDeFeira / maria-quiteria

Backend para coleta e disponibilização dos dados 📜
https://mq.dadosabertosdefeira.com.br/painel
MIT License
167 stars 69 forks source link

Melhorar a busca no conteúdo dos arquivos #124

Open anapaulagomes opened 4 years ago

anapaulagomes commented 4 years ago

Atualmente as bases de dados do painel tem usado a busca simples, que é mais lenta e cara em termos computacionais. Já temos uma busca com full-text search mas pode ser mais rápida e apresentar resultados mais satisfatórios ao buscar no conteúdo dos arquivos.

andrewsmedina commented 3 years ago

@anapaulagomes temos algum exemplo de busca que está lenta?

andrewsmedina commented 3 years ago

Como pode ser visto nesse post: https://findwork.dev/blog/optimizing-postgres-full-text-search-in-django/, a forma mais performática em se tratando de full text search com postgres é usando um campo específico para armazenar o search vector e usar um índice GIN ou GiST.

O projeto já segue essas práticas, como poder ver aqui https://github.com/andrewsmedina/maria-quiteria/blob/main/datasets/models.py#L65 e aqui https://github.com/andrewsmedina/maria-quiteria/blob/main/datasets/models.py#L70

Rodando local as queries de busca tem executado bem rapidamente (é possível verificar isso usando o debug toolbar) e imprimindo a query e o explain da query podemos ver que o índice e o campo com o serch vector estão sendo utilizados:

(Pdb) print(queryset.query)
SELECT "datasets_gazette"."id", "datasets_gazette"."created_at", "datasets_gazette"."updated_at", "datasets_gazette"."crawled_at", "datasets_gazette"."crawled_from", "datasets_gazette"."notes", "datasets_gazette"."date", "datasets_gazette"."power", "datasets_gazette"."year_and_edition", "datasets_gazette"."is_legacy" FROM "datasets_gazette" INNER JOIN "datasets_file" ON ("datasets_gazette"."id" = "datasets_file"."object_id" AND ("datasets_file"."content_type_id" = 8)) WHERE "datasets_file"."search_vector" @@ plainto_tsquery(portuguese::regconfig, Secretaria  Municipal da Fazenda) ORDER BY "datasets_gazette"."date" DESC NULLS LAST, "datasets_gazette"."id" DESC
(Pdb) print(queryset.explain())
Sort  (cost=52.33..52.33 rows=1 width=212)
  Sort Key: datasets_gazette.date DESC NULLS LAST, datasets_gazette.id DESC
  ->  Nested Loop  (cost=40.28..52.32 rows=1 width=212)
        ->  Bitmap Heap Scan on datasets_file  (cost=40.00..44.02 rows=1 width=4)
              Recheck Cond: (search_vector @@ '''secret'' & ''municipal'' & ''fazend'''::tsquery)
              Filter: (content_type_id = 8)
              ->  Bitmap Index Scan on datasets_fi_search__52321c_gin  (cost=0.00..40.00 rows=1 width=0)
                    Index Cond: (search_vector @@ '''secret'' & ''municipal'' & ''fazend'''::tsquery)
        ->  Index Scan using datasets_gazette_pkey on datasets_gazette  (cost=0.28..8.30 rows=1 width=212)
              Index Cond: (id = datasets_file.object_id)

Se em produção a pesquisa está com problemas de performance, eu penso em duas hipóteses:

@anapaulagomes essas hipóteses fazem sentido? Como proceder aqui?

anapaulagomes commented 3 years ago

O @andrewsmedina descobriu que o índice não está funcionando corretamente em produção (na verdade, não está funcionando). Dado que vamos mudar a infra em breve (🙏🏽), vamos deixar como está por hora e revisitar essa issue quando a migração for feita (#134).

geraldo-castro commented 3 years ago

@anapaulagomes Com a mudança de infra feita, cê acha que podemos avaliar a performance dessa busca?

anapaulagomes commented 3 years ago

Sim, tudo já foi migrado. 🚀 Pode checar se funciona local e depois na Absam (a galera do canal #infraestrutura do nosso Discord pode dar mais informações e eu posso ajudar com as credenciais tb). @geraldo-castro

anapaulagomes commented 3 years ago

Configurei o pghero hoje. Vou deixar alguns dias rodando por lá e posto os resultados aqui. Nesse PR habilito os campos que estavam causando gargalos: https://github.com/DadosAbertosDeFeira/maria-quiteria/pull/401.

cc @turicas @cuducos

turicas commented 3 years ago

As otimizações iniciais que eu sugeriria já estão feitas (popular o vetor em segundo plano numa coluna dedicada e criar o índice do tipo GIN nessa coluna), mas talvez dê pra melhorar a forma como o Django Admin faz as buscas (pode ser que o ordering do model, por exemplo, esteja influenciando em não usar o índice que existe). Proponho o seguinte:

  1. Monitorar as consultas SQL que o Django Admin está fazendo, para então selecionar as mais custosas e daí entender o porquê da demora com o EXPLAIN. Isso pode ser feito de 2 formas:
  2. Verificar a possibilidade de:
    • Colocar tudo que deve ser buscável na coluna do vetor de busca, assim o Django procura num lugar só, em vez de fazer filtros em múltiplas colunas (que não estão indexadas juntas); ou
    • Indexar todas as colunas que precisam ser buscadas juntamente com o vetor de busca

Notas: no caso de colocar tudo na coluna do vetor de busca:

  • É possível adicionar pesos diferentes aos dados de cada coluna (exemplo: peso maior para o sumário e menor para o texto que vem do Tika)
  • Precisarão ser alteradas as classes ModelAdmin, o management command search_vector e criar uma migração de dados pra re-popular a coluna com os novos dados em produção
  • O comando de indexação ficará mais lento por conta do join entre modelo especializado e arquivo correspondente, mas creio que isso não será um impeditivo. Nesse caso o comando precisaria só atualizar os registros que foram afetados com o resultado do crawler, então a quantidade de registros atualizados não deve ser muito grande.
exageraldo commented 3 years ago

Saiu um artigo hoje falando um pouco sobre esse tópico. Deixando o link aqui pra caso alguem queira entender mais/ganhar mais contexto. 🤗

Basic and Full-text Search with Django and Postgres