Today I Learned — 02/05/2022
Há uma semana venho utilizando o MongoDB para salvar dados vindos de um tópico no Kafka, para cada tópico é gerada uma nova coleciona no Mongo. Quando queremos resgatar as mensagens de um tópico precisamos fazer uma busca paginada no mongo, mas e se as mensagens do tópico forem atualizadas? por exemplo: um pagamento que estava aguardando ser confirmado e agora foi concluído? eu vou ter duas mensagens para a mesma transação, devo trazer as duas mensagens na busca paginada ou somente quando eu for buscar o histórico do pagamento que vou querer a mensagem anterior?
De um cenário parecido com esse surgiu a necessidade de uma busca paginada no MongoDB onde eu pudesse agrupar as mensagens pela mais recente e fazer buscas com filtragem e ordenação.
Para começar vamos precisar criar uma Aggregation que é uma operação que processa múltiplos documentos e retorna o resultado, ele pode receber uma pipeline de operações como a documentação do mongo fornece de exemplo:
Dentro da nossa aggregation vamos precisar de apenas um $facet na pipeline, como a documentação define um facet é uma forma de processar múltiplos processos em um único estagio da da pipeline do aggregation e também podemos definir como vai ser retornado o resultado:
No nosso facet vamos precisar de dois output fields (podem ser mais se necessário mas para o meu caso só precisei de dois), o data, que contem os documents e o total, que vai retornar o total de documents para determinado filtro.
Data:
O data vai ser uma pipeline que primeiramente vai conter um $group que funciona como um order by em um banco relacional, nesse group vamos dizer que
{$group: {
_id: '$key',
data: {$last: '$$ROOT'}
}}
o campo que ele vai usar para juntar os documents é o key, e ele vai inserir esses documents agrupados dentro do data, pegando o ultimo documento do grupo, que vai ser o mais recente
ps: A ordem das operações importa, primeiro fazemos o grupo para que ele consiga separar o ultimo como de fato o ultimo valor a entrar no mongo
em seguida fazemos o $sort:
{ $sort: { 'data.createdAt': 1 } },
Onde pegamos o campo createdAt do data que foi criado pelo group e dizemos se queremos ele de forma ascendente ou descendente,
1 para ascendente e -1 para descendente
em seguida o $skip e o $limit
{ $skip: 0},
{ $limit: 10},
O skip para informarmos quantos documento desejamos pular na paginação e o limit para quantos queremos trazer. Novamente a ordem é muito importante pois se fizermos o contrario em uma collection que só tenham 2 documents e colocarmos o limit antes do skip e no skip informamos que queremos pular 1 document, o que vai acontecer é, o mongo vai buscar apenas 1 document e o skip vai pular ele, retornando assim zero, enquanto o comportamento esperado é ele pular o primeiro document e trazer o segundo.
Count:
{
$group: {
_id: '$key',
},
No count precisamos novamente formar o grupo para que a nossa contagem não venha com mais valores do que realmente vamos ter de dados e em seguida vamos passar um count que recebe como argumento o nome que queremos q ele devolva na query
{ $count: 'total' }
nesse caso ele vai contar quantas entradas foram retornadas e devolver isso como um total.
No final o nosso código vai ficar mais ou menos assim, sendo que se quisermos fazer algum filtro podemos utilizar o operador de $match
$facet: {
data: [
{
$group: {
_id: '$key',
data: {
$last: '$$ROOT',
},
},
},
{ $sort: { 'data.createdAt': 1 } },
{ $skip: 0 },
{ $limit: 10 },
],
count: [
{
$group: {
_id: '$key',
},
},
{ $count: 'total' },
],
},
E o retorno que o mongo vai fornecer é o seguinte onde data contem o document mais recente
PS: limit e skip não é a melhor forma de fazer uma busca paginada esse artigo explora um pouco mais esse assunto https://scalegrid.io/blog/fast-paging-with-mongodb/
Top comments (0)