Buscas e listagens em MongoDB com stream em chunks: cursor, batchSize e getMore
Em MongoDB, buscas grandes nao voltam "de uma vez": o banco responde em lotes por cursor, e sua API pode transmitir os dados em chunks conforme esses lotes chegam.
Resumo executivo
Em MongoDB, buscas grandes nao voltam "de uma vez": o banco responde em lotes por cursor, e sua API pode transmitir os dados em chunks conforme esses lotes chegam.
Ultima atualizacao: 27/02/2026
Resumo executivo
O ponto central que quase sempre gera confusao e este:
- MongoDB nao devolve uma busca grande inteira em uma unica resposta.
- O banco abre um cursor e retorna a primeira batelada (
firstBatch). - Conforme o cliente consome, driver e servidor trocam chamadas
getMorepara buscar os proximos lotes.
Isso significa que "streaming" em listagens costuma ser um pipeline em duas camadas:
- stream em lotes entre banco e aplicacao (cursor + getMore);
- stream em chunks entre aplicacao e cliente HTTP (NDJSON/SSE/chunked transfer, por exemplo).
Quando esse modelo e entendido, fica muito mais facil projetar APIs de listagem sem explodir memoria nem latencia.
1) Como o MongoDB realmente entrega resultados
Consultas como find() e aggregate() retornam cursor. Pela documentacao oficial:
- o lote inicial padrao e o menor entre
101documentos e16 MiB; - lotes seguintes tambem respeitam limite de
16 MiB; batchSizepode diminuir esse volume por rodada.
Em termos operacionais:
- sua API envia
find; - MongoDB retorna
firstBatch+cursorId; - ao esgotar
firstBatch, o driver emitegetMore; - o ciclo se repete ate cursor encerrar.
Ou seja, o banco ja trabalha em "chunks" (batches), mesmo antes de voce decidir como responder no HTTP.
2) Busca/listagem em stream: o papel da sua API
A aplicacao pode escolher como expor esses dados para o cliente:
toArray()-> junta tudo em memoria e responde no fim;- iteracao de cursor (
for await) -> responde progressivamente; cursor.stream()-> integra com pipeline Node Readable.
Para listagens grandes, o segundo e o terceiro caminho costumam ser mais robustos, porque permitem comecar a enviar resposta antes do fim da consulta.
3) Exemplo pratico: listagem de pedidos em chunks
Imagine uma rota /orders/export.
- Colecao:
orders - Filtro: periodo + status
- Ordenacao: por
_idasc - Saida: NDJSON em streaming
tsimport { once } from 'node:events';
app.get('/orders/export', async (req, res) => {
res.setHeader('Content-Type', 'application/x-ndjson; charset=utf-8');
res.setHeader('Transfer-Encoding', 'chunked');
const cursor = db.collection('orders')
.find({ status: 'paid' })
.sort({ _id: 1 })
.batchSize(500);
try {
for await (const doc of cursor) {
const ok = res.write(JSON.stringify(doc) + '\n');
if (!ok) {
await once(res, 'drain'); // backpressure do socket HTTP
}
}
res.end();
} finally {
await cursor.close();
}
});O que acontece por baixo:
- o driver nao busca um documento por vez no banco;
- ele busca em lotes (chunks internos) e vai emitindo docs conforme consome o batch;
- sua API pode escrever cada doc (ou grupos de docs) para o cliente em chunks HTTP.
4) Onde times costumam errar
Erro A: usar toArray() para tudo
Funciona em volume pequeno. Em volume grande, vira consumo alto de memoria e tempo de resposta pior para o primeiro byte.
Erro B: batchSize extremo
- muito pequeno: excesso de round trips (
getMore) e overhead de rede; - muito grande: maior uso de memoria e latencia por lote.
batchSize deve ser calibrado por payload medio, rede e perfil de cliente.
Erro C: achar que chunked HTTP resolve query ruim
Se filtro/ordenacao nao usam indice adequado, o gargalo continua no banco. Streaming ajuda entrega progressiva, mas nao corrige plano de execucao ruim.
5) Como isso conversa com streaming de eventos (Kafka/Flink)
Para nao misturar conceitos:
- listagem/busca: leitura sob demanda, cursor baseado em query;
- event streaming: fluxo continuo de mudancas/eventos.
MongoDB entra nos dois cenarios:
- query/list por cursor para APIs de leitura;
- Change Streams para CDC e publicacao em pipelines de evento.
Os dois usam ideia de processamento progressivo, mas com contratos diferentes de consumo.
Checklist pratico para sua API de listagem MongoDB
- Evitar
toArray()em rotas de alto volume. - Usar cursor iteravel (
for await) oucursor.stream(). - Ajustar
batchSizecom testes de latencia/memoria. - Implementar backpressure ao escrever chunks HTTP.
- Fechar cursor explicitamente em cancelamento/erro.
- Medir TTFB, throughput e memoria por endpoint.
Conclusao
Se voce quer explicar corretamente "buscas e listagens em streaming no MongoDB", a formulacao mais precisa e:
- o MongoDB responde consulta em batches de cursor;
- o driver recupera proximos lotes via **
getMore**; - sua API pode transformar esse fluxo em chunks HTTP progressivos.
Pergunta para validar seu endpoint atual: hoje sua rota de listagem com maior volume transmite dados conforme chegam ou so responde quando terminou de carregar tudo em memoria?
Fontes
- cursor.batchSize() (MongoDB Docs) - documentacao oficial
- getMore command (MongoDB Docs) - documentacao oficial
- Cursors and Cursor Batches (MongoDB Docs) - documentacao oficial
- Access Data From a Cursor - Node.js Driver (MongoDB Docs) - documentacao oficial
- MongoDB Change Streams (MongoDB Docs) - documentacao oficial