Busca paginada agrupando dados no MongoDB
Tags: mongodb, todayilearned
Published: 2022-05-03 10:52:48

#### 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x7wkm1y76c11sk1u3wyp.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hi3gjxotvttgc2rx1pa0.png)

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

```js
{$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:

```js
{ $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

```js
{ $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:

```js
{
$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

```js
{ $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

```js
 $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

![Exemplo de resposta do Mongo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1sb5b95t1jvrs2sxq72v.png)
 

> 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/