阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 排序、分页与聚合

排序、分页与聚合

作者:陈川 阅读数:4324人阅读 分类: MongoDB

排序

在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 }
]);

性能优化

处理大数据集时需要注意性能:

  1. 为常用查询字段创建索引
UserSchema.index({ age: 1, name: 1 });
  1. 避免不必要的字段返回
User.find().select('name age -_id');
  1. 使用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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌