DEV Community

Cover image for Busca paginada agrupando dados no MongoDB
Yan.ts
Yan.ts

Posted on

3 3

Busca paginada agrupando dados no MongoDB

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:

Exemplo do uso de aggregation

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:

Exemplo de um uso de facet

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'}
}}
Enter fullscreen mode Exit fullscreen mode

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 } },
Enter fullscreen mode Exit fullscreen mode

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},
Enter fullscreen mode Exit fullscreen mode

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',
},
Enter fullscreen mode Exit fullscreen mode

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' }
Enter fullscreen mode Exit fullscreen mode

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' },
          ],
        },
Enter fullscreen mode Exit fullscreen mode

E o retorno que o mongo vai fornecer é o seguinte onde data contem o document mais recente

Exemplo de resposta do Mongo

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/

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay