MongoDBSort, Limit & Skip

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 example
Note
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-60
Warning
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 pages
Tip
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.