Lấy N bản ghi mới nhất với sort() và limit()
Kết hợp sort() (sắp xếp) và limit() (giới hạn) để phân trang hoặc lấy top N bản ghi.
1. Dữ liệu mẫu (thứ tự insert = thứ tự thời gian)
> db.demo454.insertOne({ ClientName: "Chris" });
> db.demo454.insertOne({ ClientName: "John" });
> db.demo454.insertOne({ ClientName: "Bob" });
> db.demo454.insertOne({ ClientName: "David" });
> db.demo454.insertOne({ ClientName: "Mike" });
2. Lấy 3 bản ghi mới nhất (theo _id giảm dần)
> db.demo454.find().sort({ _id: -1 }).limit(3);
Kết quả: Mike, David, Bob (3 bản ghi insert cuối)
3. Ví dụ trong bài: Sắp xếp theo ClientName giảm dần
> db.demo454.find().sort({ ClientName: -1 }).limit(3);
{ "_id": ObjectId("5e7cceafdbcb9adb296c95c4"), "ClientName": "Mike" }
{ "_id": ObjectId("5e7cce95dbcb9adb296c95c1"), "ClientName": "John" }
{ "_id": ObjectId("5e7ccea6dbcb9adb296c95c3"), "ClientName": "David" }
Mẫu phân trang chuẩn (Pagination)
// Trang 1 (skip 0, limit 10)
db.collection.find().sort({ _id: -1 }).skip(0).limit(10);
// Trang 2 (skip 10, limit 10)
db.collection.find().sort({ _id: -1 }).skip(10).limit(10);
// Trang N
db.collection.find().sort({ _id: -1 }).skip((page-1)*pageSize).limit(pageSize);
Lưu ý hiệu suất quan trọng
| Vấn đề | Giải pháp |
|---|---|
| skip() lớn → chậm (quét qua nhiều doc) | Dùng keyset pagination (cursor-based): filter _id < lastSeenId |
| sort không dùng index | Tạo index khớp sort: db.coll.createIndex({ ClientName: -1 }) |
| limit lớn | Giới hạn max limit (vd: 100), dùng cursor streaming |
Keyset Pagination (Cursor-based) - Hiệu quả hơn
// Lấy trang đầu
const page1 = await db.find().sort({ _id: -1 }).limit(10).toArray();
const lastId = page1[page1.length - 1]._id;
// Trang tiếp theo
const page2 = await db.find({ _id: { $lt: lastId } }).sort({ _id: -1 }).limit(10).toArray();
Cách này không dùng skip(), tận dụng index trên _id, hiệu suất không đổi theo số trang.