limit(), skip(), and why big skips hurt
limit() and skip() are how everyone first implements pagination, and they work fine until they do not. The interesting part of this lesson is the second half: why page 10,000 costs so much more than page 2.
db.users
.find()
.sort({ createdAt: -1 })
.skip(20)
.limit(10)
Classic pagination:
page = 3
limit = 10
skip = (page - 1) * limit
= 20
⚠️ Senior-level note: Large skip() is problematic
Imagine:
.skip(1000000)
MongoDB still has to walk past a large number of results.
For large datasets, prefer cursor/range-based pagination.
Example:
db.users.find({
createdAt: {
$lt: lastSeenCreatedAt
}
})
.sort({
createdAt: -1
})
.limit(20)
In real applications, you often also use _id as a tie-breaker.
Example conceptually:
createdAt DESC
_id DESC
The way out is to stop counting past documents at all: remember the last value you saw and ask for the next batch with a range condition on an indexed field. That is usually called keyset, or cursor, pagination.