Limit, Skip and Sort
Master cursor methods in MongoDB — limit(), skip(), and sort(). Learn how to paginate results, order data by fields, combine methods for efficient pagination, and understand cursor-based pagination for production apps.
When dealing with large collections, returning all documents at once is impractical. MongoDB provides cursor methods — limit(), skip(), and sort() — to control how many documents are returned, in what order, and from which offset. These are essential for building pagination in any application.
limit() — Restrict Number of Results
// Return only the first 5 users
db.users.find().limit(5)
// Combine with a filter
db.posts.find({ published: true }).limit(10)
// limit(0) means no limit (returns all documents)
db.users.find().limit(0)
skip() — Skip Documents
// Skip the first 10 documents
db.users.find().skip(10)
// Skip 5 and return the next 5
db.users.find().skip(5).limit(5)
sort() — Order Results
// Sort ascending (1 = A-Z, oldest first, lowest first)
db.users.find().sort({ name: 1 })
// Sort descending (-1 = Z-A, newest first, highest first)
db.users.find().sort({ createdAt: -1 })
// Sort by multiple fields
db.users.find().sort({ role: 1, name: 1 })
// First sort by role (A-Z), then by name within each role
// Sort by nested field
db.orders.find().sort({ "customer.name": 1 })
Combining Methods — Pagination
The most common use case is building pagination:
// Page 1 — first 10 results, newest first
db.posts.find({ published: true })
.sort({ createdAt: -1 })
.skip(0)
.limit(10)
// Page 2 — results 11-20
db.posts.find({ published: true })
.sort({ createdAt: -1 })
.skip(10)
.limit(10)
// Page 3 — results 21-30
db.posts.find({ published: true })
.sort({ createdAt: -1 })
.skip(20)
.limit(10)
// Generic pagination formula:
// skip = (pageNumber - 1) * pageSize
// limit = pageSize
Pagination Helper Function
// In your application code:
function getPage(collection, filter, page, pageSize, sortField) {
const skip = (page - 1) * pageSize;
return db[collection]
.find(filter)
.sort({ [sortField]: -1 })
.skip(skip)
.limit(pageSize);
}
// Usage:
getPage("posts", { published: true }, 3, 10, "createdAt")
// Gets page 3 (posts 21-30), sorted by newest
Counting Total Pages
// Get total count for pagination metadata
const totalDocs = db.posts.countDocuments({ published: true });
const pageSize = 10;
const totalPages = Math.ceil(totalDocs / pageSize);
// Return to frontend:
// {
// data: [...results],
// pagination: {
// currentPage: 3,
// pageSize: 10,
// totalDocs: 150,
// totalPages: 15
// }
// }
Method Execution Order
Regardless of the order you chain them, MongoDB always executes in this order:
- sort() — Orders the results first
- skip() — Then skips documents
- limit() — Then limits the count
// These produce the SAME result:
db.users.find().sort({ name: 1 }).skip(5).limit(10)
db.users.find().limit(10).sort({ name: 1 }).skip(5)
db.users.find().skip(5).limit(10).sort({ name: 1 })
Performance: The skip() Problem
Warning: skip() gets slower as the offset increases. For page 1000 with 10 items per page, MongoDB scans and discards 9,990 documents before returning 10. For large datasets, use cursor-based pagination instead:
Cursor-Based Pagination (Better for Large Datasets)
// Instead of skip(), use the last document's _id or timestamp
// First page
db.posts.find({ published: true })
.sort({ createdAt: -1, _id: -1 })
.limit(10)
// Next page — use the last document's createdAt and _id
db.posts.find({
published: true,
$or: [
{ createdAt: { $lt: lastCreatedAt } },
{
createdAt: lastCreatedAt,
_id: { $lt: lastId }
}
]
})
.sort({ createdAt: -1, _id: -1 })
.limit(10)
This is how production apps like Twitter, Facebook, and Instagram implement "infinite scroll" — it's consistently fast regardless of how deep you paginate.
Natural Sort Order
// $natural sort — returns documents in insertion order
db.logs.find().sort({ $natural: 1 }) // Oldest first (insertion order)
db.logs.find().sort({ $natural: -1 }) // Newest first (reverse)
Practical Examples
// Top 5 most viewed posts
db.posts.find({ published: true })
.sort({ views: -1 })
.limit(5)
.project({ title: 1, views: 1, _id: 0 })
// Latest 3 orders for a customer
db.orders.find({ "customer.email": "john@example.com" })
.sort({ createdAt: -1 })
.limit(3)
// Products sorted by price (cheapest first), page 2
db.products.find({ inStock: true })
.sort({ price: 1 })
.skip(20)
.limit(20)
What's Next
You can now paginate, sort, and efficiently retrieve subsets of data. In the next episode, we'll dive into indexing — the most important performance optimization in MongoDB, which makes sort() and find() operations dramatically faster.