Un cursor representa una consulta cuyos resultados se obtienen y consumen por batches. Permite procesar conjuntos mayores que la memoria disponible y separa la definición de la query de su consumo.
Texto
find / aggregate
↓
cursor en cliente
↓
primer batch
↓
getMore según consumo
↓
cierre o agotamiento
Un cursor no contiene inmediatamente todos los documentos. El driver solicita más resultados al servidor conforme la aplicación avanza.
Es adecuado cuando el resultado está estrictamente acotado, por ejemplo una página de 20 elementos. No es apropiado para colecciones o exports sin límite.
El problema no es el método en sí, sino utilizarlo sin conocer cardinalidad y tamaño de documentos.
El servidor puede mantener estado para cursores no agotados. Cursores abandonados consumen recursos hasta cerrarse o expirar según comportamiento y opciones.
Cuando interrumpes antes de agotar:
TypeScript
const cursor = orders.find(filter);try{forawait(const order of cursor){if(shouldStop(order))break;}}finally{await cursor.close();}
Un cursor normal no siempre representa una snapshot inmutable de horas. Inserts, updates y deletes concurrentes pueden afectar lo que se observa según operación, read concern y topología.
Si necesitas un export consistente:
fija un asOf;
usa snapshot semantics en un contexto compatible;
materializa el conjunto;
ejecuta desde backup o secondary dedicado según requisitos;
Un cursor abierto en un secondary puede observar lag. Si ocurre failover o error, la capacidad de continuar depende de driver y tipo de operación. No asumas que cualquier cursor se reanuda automáticamente.
Change Streams sí tienen resume tokens y semántica específica; un cursor de find no es un Change Stream.
Aunque el resultado se entregue por batches, stages como $sort o $group pueden necesitar procesar gran parte del input antes de producir resultados. Streaming de salida no significa que toda la pipeline use memoria constante.
Utiliza try/finally, AbortSignal cuando la API lo soporte y manejo diferenciado de errores.
Cancelar en cliente no garantiza siempre que todo el trabajo del servidor desaparezca instantáneamente; observa operaciones largas y utiliza límites como maxTimeMS donde corresponda.
Un cursor es una estrategia de consumo incremental, no una snapshot automática ni una cola. Su confiabilidad depende de límites, backpressure, cierre, orden estable, checkpoints e idempotencia. toArray() es seguro solo cuando el conjunto está acotado.
Comprueba lo aprendido
¿Qué diferencia existe entre cursor y array?
¿Qué controlan limit y batchSize?
¿Por qué necesitas backpressure?
¿Cuándo debes cerrar explícitamente?
¿Cómo diseñarías un export reanudable?
¿Por qué un aggregation cursor no implica memoria constante?