Sort, Limit & Skip
These three cursor methods control the order and quantity of results. They correspond directly to SQL's ORDER BY, LIMIT, and OFFSET.
sort() — Ordering Results
Pass a document with fields mapped to 1 (ascending) or -1 (descending).
sort() examples
JS
// Sort by createdAt descending (newest first)
db.posts.find().sort({ createdAt: -1 })
// Sort by lastName ascending, then firstName ascending
db.users.find().sort({ lastName: 1, firstName: 1 })
// Sort by score descending (highest first)
db.leaderboard.find().sort({ score: -1 })Sorting on Multiple Fields
Multi-field sort
JS
// Sort by status ascending, then by createdAt descending
// JavaScript object key order is preserved in mongosh and all official drivers
db.orders.find().sort({ status: 1, createdAt: -1 })
// In languages without ordered object literals (older Python dicts, etc.),
// use an array of pairs to guarantee sort order:
// [('status', 1), ('createdAt', -1)] <- Python pymongo exampleNote
In mongosh and drivers, sort document key order is respected. Use Map or an array of pairs in languages without ordered object literals.
limit() — Cap the Result Count
limit() examples
JS
// Return only the top 10 products
db.products.find().sort({ rating: -1 }).limit(10)
// Return only the 5 most recent posts
db.posts.find().sort({ createdAt: -1 }).limit(5)
// limit(0) is treated as no limit — returns all documents
db.posts.find().limit(0)skip() — Offset Results
skip() for pagination
JS
const pageSize = 10 // Page 1 — items 1–10 (skip 0) db.products.find().skip(0).limit(pageSize) // Page 2 — items 11–20 (skip 10) db.products.find().skip(10).limit(pageSize) // Page 3 — items 21–30 (skip 20) db.products.find().skip(20).limit(pageSize)
Pagination with skip() and limit()
Page-based pagination
JS
// Generic page-based pagination function (1-indexed pages)
function getPage(page, pageSize) {
const skip = (page - 1) * pageSize
return db.products
.find({ inStock: true })
.sort({ createdAt: -1 })
.skip(skip)
.limit(pageSize)
.toArray()
}
// Usage:
getPage(1, 20) // first 20 results
getPage(2, 20) // results 21-40
getPage(3, 20) // results 41-60Warning
skip() becomes slow on large collections because MongoDB scans and discards skipped documents. Avoid skip() with large offset values (greater than 1000).Cursor-Based Pagination (Seek Method)
For high-performance pagination, use the last seen _id (or a sortable field) as a cursor instead of skip.
Cursor-based pagination
JS
const pageSize = 20
// First page — no cursor needed
const firstPage = db.posts
.find({ published: true })
.sort({ _id: 1 })
.limit(pageSize)
.toArray()
// Get the last _id from the results
const lastId = firstPage[firstPage.length - 1]._id
// Next page — query from where we left off
const nextPage = db.posts
.find({ published: true, _id: { $gt: lastId } })
.sort({ _id: 1 })
.limit(pageSize)
.toArray()Approach | Performance | Supports Jump-to-Page |
|---|---|---|
skip + limit | O(n) — slower as offset grows | Yes |
cursor-based | O(1) — constant time | No (next/prev only) |
Chaining All Three
sort + limit + skip together
JS
// Electronics, sorted by price desc, page 3 (items 21-30)
db.products
.find({ category: 'electronics' })
.sort({ price: -1 })
.skip(20)
.limit(10)countDocuments() for Total Pages
Calculating total pages
JS
const pageSize = 20
const filter = { category: 'electronics', inStock: true }
// Get total count matching the filter
const totalDocuments = await db.products.countDocuments(filter)
// Calculate total pages
const totalPages = Math.ceil(totalDocuments / pageSize)
console.log(`${totalDocuments} products across ${totalPages} pages`)
// Example output:
// 157 products across 8 pagesTip
Always add
sort() before skip() and limit(). Without a sort, result order is not guaranteed and pagination will be inconsistent across requests.Note
The execution order for MongoDB cursor methods is always: sort → skip → limit, regardless of the order you chain them in code.