tabnews.com.br
tabnews.com.br copied to clipboard
feat: search
API para pesquisar por titulo de posts, por enquanto usei o comando o PostgreSQL ILIKE
para fazer a pesquisa por texto. Esse pull request foi tirada aqui: #927
@peeeuzin is attempting to deploy a commit to the TabNews Team on Vercel.
A member of the Team first needs to authorize it.
Ótima iniciativa! Essa feature ajudaria bastante @filipedeschamps!
No front podemos fazer um modal/popup ao digitar cmd + K ( no mac ) ou ctrl + K, muitos apps fazem desta forma hoje em dia e deixam a instrução desse comando no header. Ao ser aprovado aqui posso implementar para nós isso !
@peeeuzin, obrigado pelo PR! 💪
Não olhei todo o código, mas olhando por cima fiquei com algumas dúvidas:
- Se está implementando uma busca, por que tem alteração no algoritmo de relevantes?
- Você tomou medidas para evitar SQL Injection?
- A pesquisa é pelo título, pelo body ou ambos?
- Consegue criar os testes para essa feature?
A busca
de fato vai ser uma ótima feature e obrigado por propor esta implementação 🤝
Uma sugestão porém: não usar o model content
nem o endpoint /api/v1/contents
para isto. O escopo do model content
está muito grande, muito difícil de ler, dar manutenção e isso é péssimo para a segurança, ainda mais de um model que recebe tantos inputs. Sugiro fazer um outro model especializado para esta feature, assim com uma rota, e que ela tenha cache na CDN.
@peeeuzin, obrigado pelo PR! 💪
Não olhei todo o código, mas olhando por cima fiquei com algumas dúvidas:
- Se está implementando uma busca, por que tem alteração no algoritmo de relevantes?
- Você tomou medidas para evitar SQL Injection?
- A pesquisa é pelo título, pelo body ou ambos?
- Consegue criar os testes para essa feature?
Ok, vamos para as respostas das perguntas:
- Tentei reaproveitar o algoritmo de relevantes para o search, para que as pesquisas mais relevantes ficam no topo da query
- Boa pergunta, talvez precisarei de ajuda para detectar isso, porém, toda query de um request fica entre
%{query}%
- Por enquanto só pelo titulo, mas podemos implementar pelo body tbm
- Claro! Mas os testes seriam end-to-end?
Show @peeeuzin!
- Tentei reaproveitar o algoritmo de relevantes para o search, para que as pesquisas mais relevantes ficam no topo da query
Não vale a pena por alguns motivos, mas o principal é que a query de relevantes só pega conteúdos dos os últimos 7 dias e a forma como ela classifica também é bastante dependente do tempo, então não adiantaria só aumentar o intervalo.
É bem mais tranquilo criar uma consulta específica de pesquisa. 👍
- Boa pergunta, talvez precisarei de ajuda para detectar isso, porém, toda query de um request fica entre
%{query}%
Tranquilo! A gente aprende junto aqui, pois a Turma é nota 10 🤩
- Por enquanto só pelo titulo, mas podemos implementar pelo body tbm
Estou curioso com relação à performance do ILIKE no TabNews 🤞
- Claro! Mas os testes seriam end-to-end?
Maravilha! 🎉 Isso mesmo, precisamos testar todas as possibilidades do endpoint que você criou 🤝
@aprendendofelipe
Não vale a pena por alguns motivos, mas o principal é que a query de relevantes só pega conteúdos dos os últimos 7 dias e a forma como ela classifica também é bastante dependente do tempo, então não adiantaria só aumentar o intervalo.
Entendi! Vou tentar refatorar e repensar todo o código que criei e vou levar em conta o que o @filipedeschamps sugeriu.
@aprendendofelipe e @filipedeschamps
Fiz algumas mudanças e aqui está um resumo:
- Refiz do zero o algoritmo de search e criei o model
search
como o @filipedeschamps sugeriu - Criei um parser (bem simples) para a query do usuário para fazer pesquisas mais especificas:
-
title:<texto>
: pesquisa somente em titulos -
body:<texto>
: pesquisa somente no body
-
- Fiz os testes e2e
- Usei os placeholders para evitar SQL Injection
Importante notar que a ordem de relevância das pesquisas está relacionada a quando foi postada.
Qualquer outra feature que queira implementar não hesite a me falar 😁✨
@peeeuzin, acho que a sugestão do @filipedeschamps era fazer uma model especializado, não um flexível. Ele precisa ser mais simples.
Você se baseou muito no model content
e tem muita coisa ali que provavelmente não faz sentido para a busca.
Então o que era complexo, e difícil de dar manutenção, agora está em dois lugares diferentes.
Entendido! Eu realmente me inspirei no model de contents, vou tentar refatorar o novo model.
O postgress atualmente é um ótimo banco de dados relacional e o mais interressante dele é que mesmo com foco no relacional ele não fica para tras com outras features. O objetivo da pull request é uma sonhada pesquisa e nesse caso o que mais impacta, no meu ver, é o quanto isso vai ser relavante para plataforma nesse momento, mas isso é muito de momento pra momento.
Uma possível forma de implementação que pode ajudar na pull request seria o uso do "Full Text Search" do PostgreSQL mesmo. O PG (PostgreSQL) tem algumas features "escondidas" que podem ser ativadas. Nesse caso, a feature que eu recomendaria usar para um tipo "elastic search" seria o Full Text Search junto com um plugin (que vem desativado) pg_trgm para usar correspondência de string aproximada. Segundo a própria documentação do PG em tradução livre:
O PostgreSQL tem operadores ~, ~*, LIKE e ILIKE para tipos de dados textuais, mas eles não possuem muitas propriedades essenciais exigidas pelos sistemas de informação modernos
tais como rankings, ordenações e índices próprios para pesquisa.
Sobre o Full Text Seach:
Basicamente, ele vai permitir um pré-processamento e lidar com um índice para classificação mais rápida posteriormente. Para isso, o que vamos usar são duas funções: tsvector
e tsquery
.
A tsvector
em suma seria uma função que prepara o(s) dado(s) para serem pesquisados e cria um indice de localização do texto, por exemplo: na frase "The tabnews is wonderful" ele retorna só as palavras e a possição delas facilitando assim na pesquisa.
A tsvector
, em suma, seria uma função que prepara o(s) dado(s) para serem pesquisados e cria um índice de localização do texto. Por exemplo, na frase "The tabnews is wonderful", ela retorna só as palavras e a posição delas, facilitando assim na pesquisa.
"
Você não consideraria algumas coisas para pesquisar, como "The" e "is", sobrando assim só um "array" ['tabnew':2 'wonder':4], ficando assim top para fazer uma pesquisa, né? (veja que se tornou um token sem a palavra completa para facilitar na pesquisa sem plural e com outra terminação)
A tsquery
seria um auxílio para você gerar as palavras para fazer a pesquisa de fato. Por exemplo, você insere a pesquisa colocando o &
entre as palavras e ele vai retornar uma lista de tokens para ser pesquisado no texto: 'tabnew' & 'wonder' tokens sem serem no plural e com a parte canônica para maior abrangência.
Então, agora temos o texto em forma de vetores com posição e temos uma ajuda para gerar um padrão de pesquisa nesse texto. Agora, como seria a pesquisa de fato? Para pesquisar, ele retorna só um true, você passa a pesquisa e texto. Assim, ele responde se existe no texto o que foi pesquisado. É bem simples, mas tudo é simples quando você entende. :grin:
Vamos complicar a vida dessa pesquisa. Assim, usando um exemplo do tabnews: Novidades no tabnews
À primeira vista, você diria que é a mesma coisa que um LIKE
faria. Mas, nesse caso, vamos analisar como foi gerado os tokens para pesquisar no texto:
Se eu pesquisasse no texto "Página inicial", na verdade, ele converteria para uma pesquisa com texto em minúsculas e parte faltando, o inicial se torna inici, e isso se deve a uma implementação que "mapeia diferentes variações de uma palavra para uma forma canônica usando um dicionário Ispell." Os resultados não são mais um simples LIKE, mas sim uma busca por tokens de pesquisa.
Da mesma forma, o tsvector
gerou esse token na lista das palavras a serem pesquisadas:
'1340':134 'agora':30,80 'algum':61 'ant':47 'apó':28,42 'colocada':119 'começou':116 'concluída':126 'conta':99 'conteúdo':62 'de':9,36,40 'depoi':75 'desd':5 'detalh':130 'direcionado':70 'dos':16 'dua':1 'e':11,74,124 'efetuar':45 'ele':26,68,81 'em':60,88,120 'era':69 'essa':109 'estar':64 'estava':79,90 'estão':131 'fallback':102 'fazer':55 'foi':13,24,110,125 'implementado':14 'inici':106 'isso':48 'jgbispo':123 'logado':65 'login':29,46,73,97 'mai':18 'mas':22 'mesmo':91 'mim':128 'navegado':94 'nessa':66 'nisso':12 'novidad':10 'não':23 'o':43,52,72,96,101 'ocorr':33 'ocorria':50 'ond':78 'origem':41 'os':129 'ou':58 'para':38,71,77,85,95 'passaram':4 'pedido':19 'pelo':20,122 'por':98,127 'pr':133 'prática':121 'própria':100 'publicação':8,57 'página':39,87,105 'quando':51 'que':89,92,115 'rafatcb':114 'recurso':17 'redirecionamento':27,35 'se':3 'sem':63 'semana':2 'sempr':31 'ser':118 'situação':67 'sugestão':112 'só':25,49 'tabnew':108 'também':82 'tenha':93 'tentava':54 'um':15,34 'uma':56,111 'usuário':21,44,53 'vai':32,83 'volta':37 'voltar':84 'voltava':76 'votar':59 'é':103 'última':7
Dado que as duas funções são excelentes para trabalhar, também falaria sobre a função de ranquear as buscas:
Este texto todo é apenas uma prova de conceito, mas para o tabnews, a implementação pegaria não apenas o texto, mas também o título, o que é possível com esses métodos e seguindo a documentação. Feita toda a query, ele já garantiria um ganho melhor, mas você ainda pode gerar um índice específico para a pesquisa, já preparando para pesquisar usando índices do tipo GIN
Há um exemplo ótimo nesse link da implementação, o qual eu aprendi com detalhes.
Como está o andamento dessa feature? Tudo bem se eu tentar desenvolver uma solução?
Estou preso em alguns bugs, mas não há problema em desenvolver uma solução.
@peeeuzin, obrigado pelo PR! 💪
Pela demanda atual, onde são realizadas uma média de 300 buscas diárias usando o sistema que já foi implementado, acredito que ele está nos atendendo bem.
Vou fechar esse PR por enquanto, mas nada impede termos um sistema diferente no futuro