MongoDB Cursor Management: Pagination
MongoDB Cursor Management: Pagination
MongoDB Cursor Management: Pagination
Tip: Cursor-Based Pagination
$posts = $collection->find(
['_id' => ['$lt' => new MongoDB\BSON\ObjectId($lastId)]],
['sort' => ['_id' => -1], 'limit' => 10]
);
Much faster than skip/limit for deep pages.
Gotcha: skip() Performance
$collection->find([], ['skip' => 10000, 'limit' => 10]);
MongoDB still scans all 10,000 skipped documents. Very slow.
Tip: Range Query Pagination
$collection->find(
['created_at' => ['$lt' => $lastDate]],
['sort' => ['created_at' => -1], 'limit' => 10]
);
Uses index on created_at for fast pagination.
Gotcha: Consistent Sort Order
Pagination requires a stable sort order. Add _id as a tiebreaker:
['sort' => ['created_at' => -1, '_id' => -1]]
Tip: Count Without Loading
$collection->countDocuments(['published' => true]);
Faster than find()->toArray() and count().
Gotcha: Estimated Count
$collection->estimatedDocumentCount();
Uses collection metadata — very fast but approximate.
Tip: Embed or Reference? The 80/20 Rule
If you always access data together, embed it. If you access it independently, reference it. The 16MB document size limit is the hard boundary — stay under 1MB for most documents.
Tip: Index Your Query Patterns, Not All Fields
Creating indexes on every field wastes RAM. Use explain() to find in-memory sorts and collection scans. Index only what your actual queries filter on.
Gotcha: No Transaction Rollback for Index Builds
Building an index on a large collection can take hours. If it fails midway, the partial index is silently discarded. Plan index builds during maintenance windows.
Senior Insight
MongoDB cursors are server-side constructs that batch query results. The default batch size is 101 documents or 16MB (whichever is smaller). For pagination, using skip() and limit() becomes expensive at high offsets because MongoDB must scan all skipped documents. I use range-based pagination with _id or indexed timestamp fields for efficient pagination: find({_id: {$gt: lastSeenId}}).limit(20). This approach performs consistently regardless of page depth.
Source: MongoDB Developer Center (https://www.mongodb.com/developer/), MongoDB Engineering Blog (https://www.mongodb.com/blog/channel/engineering-blog), Studio 3T Blog (https://studio3t.com/blog/)