排序、分页与聚合
排序
在Mongoose中,排序是通过sort()
方法实现的。这个方法接收一个对象作为参数,指定排序的字段和顺序。1表示升序,-1表示降序。例如,按照创建时间降序排列:
const users = await User.find().sort({ createdAt: -1 });
多字段排序也很常见。比如先按年龄升序,再按姓名降序:
const users = await User.find()
.sort({ age: 1, name: -1 });
对于字符串排序,需要注意大小写敏感问题。可以使用collation
选项:
const users = await User.find()
.sort({ name: 1 })
.collation({ locale: 'en', strength: 2 });
分页
分页通常结合skip()
和limit()
方法实现。skip()
指定跳过的文档数,limit()
限制返回的文档数。例如获取第二页的数据,每页10条:
const page = 2;
const perPage = 10;
const users = await User.find()
.skip((page - 1) * perPage)
.limit(perPage);
对于大数据集,skip()
性能较差。替代方案是使用基于游标的分页:
// 第一页
const firstPage = await User.find().sort({ _id: 1 }).limit(10);
// 获取最后一篇文档的ID
const lastId = firstPage[firstPage.length - 1]._id;
// 第二页
const secondPage = await User.find({ _id: { $gt: lastId } })
.sort({ _id: 1 })
.limit(10);
聚合
Mongoose的aggregate()
方法提供了强大的数据聚合功能。基本聚合管道包含多个阶段:
const result = await User.aggregate([
{ $match: { age: { $gte: 18 } } }, // 筛选阶段
{ $group: { _id: "$city", count: { $sum: 1 } } }, // 分组阶段
{ $sort: { count: -1 } } // 排序阶段
]);
常见聚合操作包括:
$group
: 分组统计$project
: 字段投影$unwind
: 展开数组$lookup
: 关联查询
例如统计每个城市的平均年龄:
const stats = await User.aggregate([
{ $group: {
_id: "$city",
avgAge: { $avg: "$age" },
minAge: { $min: "$age" },
maxAge: { $max: "$age" }
}},
{ $sort: { avgAge: -1 } }
]);
组合应用
排序、分页和聚合经常组合使用。例如实现一个分页的聚合查询:
const page = 1;
const perPage = 5;
const result = await Order.aggregate([
{ $match: { status: "completed" } },
{ $group: {
_id: "$product",
totalSales: { $sum: "$amount" },
count: { $sum: 1 }
}},
{ $sort: { totalSales: -1 } },
{ $skip: (page - 1) * perPage },
{ $limit: perPage }
]);
性能优化
处理大数据集时需要注意性能:
- 为常用查询字段创建索引
UserSchema.index({ age: 1, name: 1 });
- 避免不必要的字段返回
User.find().select('name age -_id');
- 使用
explain()
分析查询
const explanation = await User.find().sort({ age: 1 }).explain();
实际案例
电商平台商品列表的实现:
async function getProducts(category, page = 1, sortBy = 'popular') {
const perPage = 12;
let sortOption = {};
switch(sortBy) {
case 'price_asc': sortOption = { price: 1 }; break;
case 'price_desc': sortOption = { price: -1 }; break;
case 'newest': sortOption = { createdAt: -1 }; break;
default: sortOption = { salesCount: -1 };
}
const [products, total] = await Promise.all([
Product.find({ category })
.sort(sortOption)
.skip((page - 1) * perPage)
.limit(perPage),
Product.countDocuments({ category })
]);
return {
products,
totalPages: Math.ceil(total / perPage),
currentPage: page
};
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:链式查询与查询优化