
O atlas search é uma full-text search poderosa incorporada no MongoDB Atlas, plataforma que oferece o mongodb como serviço (DBaaS). No entanto, é importante tomar alguns cuidados ao implementar esse recurso em seu projeto. Nesse artigo vou falar sobre um erro comum ao começar a utilizar o atlas search.
Operador $search + $match no mesmo pipeline
Em projetos que utilizam o mongodb, é muito comum existirem aggregations com filtragem de documentos e paginação. Em um determinado momento, pode ser necessário incluir uma busca textual em algum campo do documento e a depender da complexidade da consulta, ser necessário incluir uma busca com atlas search ao invés de usar apenas regex. Nesse momento, decidir apenas incluir o operador $search no seu pipeline já existente, pode impactar drasticamente a performance da sua aplicação.
Exemplificando o problema
A titulo de exemplo, vamos trabalhar com uma API que retorna livros. Nessa API já existem os filtros comuns como filtro por ano, autor, etc… Abaixo, apresento p exemplo de um documento:
{
"_id": ObjectId("65f5a3b8c9e77a001c2d3e4f"),
"title": "O Senhor dos Anéis",
"author": "J.R.R. Tolkien",
"genre": "Fantasia",
"release_date": 1954,
"pages": 1178
}
Abaixo, temos o exemplo de uma consulta filtrando pelo gênero “Fantasia” e uma páginação comum:
[
{
"$match": {"genre": "Fantasia"}
},
{"$skip": 10},
{"$limit": 10}
]
Agora precisamos permitir uma busca textual no campo title. Um dos mótivos clássicos para optar pela implementação do atlas search para essa tarefa, é permitir erros de digitação por parte do usuário, classificar os resultados por relevância, busca parcial e performance. Tomada essa decisão, abaixo mostro como ficaria o pipeline se os desenvolvedores decidirem apenas inserir a a busca com atlas search no pipeline já existente. A etapa responsável pela busca textual fica dentro do estágio com $search. Observe abaixo:
[
{
"$search": {
"text": {
"query": "senhor dos aneis", # texto que o usuário inseriu
"path": "title", # campo filtrado
"fuzzy": {
"maxEdits": 2 # permite erros de digitação
}
}
}
},
{
"$match": {"genre": "Fantasia"}
},
{"$skip": 10},
{"$limit": 10}
]
Caso o usuário filtre pelo gênero e agora filtre também pelo título, a primeira etapa utiliza o $search para uma busca textual e em seguida é realizada uma filtragem por número de gênero no operador $match (a query antiga ainda é a mesma). Ao executar esse pipeline, internamente o banco utiliza o motor de busca do atlas search para processar o operador $search e em seguida outro para o $match. A troca de informações entre esses esses motores é extremamente custosa e deve ser evitada.
Resolvendo o problema de performance
No exemplo citado acima, o ideal é utilizar toda a consulta no atlas $search pois ele dá suporte para as mesmas buscas que o mongodb oferece. O novo pipeline otimizado seria:
[
{
"$search": {
"index": "default",
"compound": {
"must": [
{
"text": {
"query": "senhor dos aneis",
"path": "title",
"fuzzy": {
"maxEdits": 2
}
}
},
{
"equals": {
"path": "genre",
"value": "Fantasia"
}
}
]
}
}
},
{"$skip": 10},
{"$limit": 10}
]
Nesse novo pipeline, a busca textual e o filtro por gênero são executadas em apenas 1 máquina e terá um desempenho muito superior ao cenário anterior. Um ponto interessante é que os operadores $skip e $limit trabalham bem após o $search mas existem meios mais performáticos para fazer páginação no atlas search com searchAfter e searchBefore.
Considerações finais
Além da combinação $search + $match causar um enorme impacto na performance, ( mais explicações do porque acontece isso na sessão Exemplificando o problema), algo semelhante acontece com os operadores $group, $sort e $count. Porém, para esses operadores o impacto é menor mas ainda assim deve ser evitado. Sempre considere testar sua query em collections grandes, simulando um cenário real para evitar descobrir esse tipo de problema em produção.
referências:
https://www.mongodb.com/pt-br/docs/atlas/atlas-search/
https://www.mongodb.com/pt-br/docs/atlas/atlas-search/paginate-results/
https://www.mongodb.com/pt-br/docs/atlas/atlas-search/performance/query-performance/