Skip to main content

Working with Cursors

1. Understanding Cursor Behavior

AspectDetail
LazyServer batches results, client pulls as needed
Default batch101 docs first batch, 16MB subsequent
Idle timeout10 minutes (server)
ResourceHolds locks/snapshots on server

2. Iterating Cursor Results

PatternDescription
for await (const d of cursor)Driver async iteration
cursor.forEach(fn)Imperative
while (cursor.hasNext())mongosh
cursor.tryNext()Non-blocking peek

3. Converting Cursor to Array

MethodWarning
toArray()Loads all results in memory
Use caseSmall bounded results only
Anti-patterntoArray() on unbounded find()

4. Counting Cursor Results

MethodNotes
countDocuments(filter)Accurate; uses aggregation
estimatedDocumentCount()Fast, from metadata; no filter
cursor.count() DEPRECATEDReplaced by countDocuments

5. Setting Cursor Limit

MethodDescription
.limit(n)Cap returned docs
Negative limitSingle batch, then close cursor

6. Skipping Cursor Results

AspectDetail
.skip(n)Skip n docs server-side
PerformanceO(n) cost; avoid large skips for pagination
Better paginationRange query on indexed sort key

Example: Range-based pagination

// Page N uses last _id of page N-1
const page = await db.posts.find({ _id: { $gt: lastId } }).sort({ _id: 1 }).limit(20).toArray();
const lastId = page.at(-1)?._id;

7. Sorting Cursor Results

FormEffect
.sort({field: 1})Ascending
.sort({field: -1})Descending
.sort({a:1, b:-1})Composite
In-memory limit32MB unless backed by index
$meta sortBy text score: .sort({score: {$meta: "textScore"}})

8. Setting Batch Size

MethodEffect
.batchSize(n)Docs per network round-trip
Trade-offSmall = lower memory, more RTT; large = opposite
CapServer limits batches to 16MB regardless

9. Using Tailable Cursors

OptionPurpose
tailableStays open at end of capped collection
awaitDataBlock briefly for new docs
Use caseLog tailing, change-stream-like

10. Using noCursorTimeout Option

AspectDetail
PurposePrevent server-side 10-minute idle close
RiskCursor leaks if client forgets to close
Use caseLong-running ETL / batch jobs

11. Closing Cursors

MethodEffect
cursor.close()Frees server resources immediately
killCursors commandServer-side close by id
Auto-closeWhen exhausted or idle timeout

12. Understanding Cursor Expiration

ConditionResult
10-min idleServer closes; CursorNotFound error
Connection dropCursor invalidated
noCursorTimeoutDisables idle expiration
RecoveryRe-issue query; cannot resume

Was this article helpful?