Phân trang cơ bản trong MongoDB sử dụng cặp skip() và limit().
Công thức
skip((page - 1) * pageSize)limit(pageSize)
Ví dụ minh họa
Tạo collection paginationDemo 5 document:
> db.paginationDemo.insertOne({ "CustomerName": "Chris", "CustomerAge": 23 });
> db.paginationDemo.insertOne({ "CustomerName": "Robert", "CustomerAge": 26 });
> db.paginationDemo.insertOne({ "CustomerName": "David", "CustomerAge": 24 });
> db.paginationDemo.insertOne({ "CustomerName": "Carol", "CustomerAge": 28 });
> db.paginationDemo.insertOne({ "CustomerName": "Bob", "CustomerAge": 29 });
Hiển thị tất cả:
> db.paginationDemo.find().pretty();
Trang 1: PageSize = 3
> db.paginationDemo.find().skip(0).limit(3);
Kết quả: 3 document đầu (Chris, Robert, David).
{ "_id" : ..., "CustomerName" : "Chris", "CustomerAge" : 23 }
{ "_id" : ..., "CustomerName" : "Robert", "CustomerAge" : 26 }
{ "_id" : ..., "CustomerName" : "David", "CustomerAge" : 24 }
Trang 2: PageSize = 3
> db.paginationDemo.find().skip(3).limit(3);
Kết quả: 2 document còn lại (Carol, Bob).
Code mẫu áp dụng (JavaScript/Node.js)
async function getPage(page, pageSize) {
const skip = (page - 1) * pageSize;
const data = await db.collection.find().skip(skip).limit(pageSize).toArray();
const total = await db.collection.countDocuments();
return { data, total, page, pageSize, totalPages: Math.ceil(total / pageSize) };
}
Vấn đề hiệu suất với skip() lớn
skip() phải quét qua N document đầu tiên -> **chậm dần khi trang sâu** (deep pagination).
Giải pháp tối ưu: Keyset Pagination (Cursor-based)
Dùng field sort unique (thường là _id hoặc createdAt) làm "con trỏ":
// Trang đầu
db.coll.find().sort({ _id: 1 }).limit(3);
// Trang sau: dùng _id của document cuối trang trước
db.coll.find({ _id: { $gt: lastId } }).sort({ _id: 1 }).limit(3);
- Hiệu suất O(1) không phụ thuộc vào số trang.
- Yêu cầu field sort có index và unique (hoặc compound unique).
- Không hỗ trợ "nhảy đến trang N" tùy ý (chỉ next/prev).
Tóm tắt khi nào dùng cách nào
| Phương pháp | Ưu điểm | Nhược điểm | Phù hợp |
|---|---|---|---|
skip/limit | Đơn giản, nhảy trang tùy ý | Chậm khi trang sâu | Admin UI, dữ liệu ít (<10k docs) |
Keyset ($gt/_id) | Nhanh, ổn định | Không nhảy trang tùy ý | API mobile, feed vô hạn, big data |