阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 聚合操作(count、distinct)

聚合操作(count、distinct)

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

聚合操作中的count

count是MongoDB中最基础的聚合操作之一,用于统计集合中符合条件的文档数量。这个操作在数据分析和报表生成时特别有用。基本语法如下:

db.collection.count(query)

其中query参数是可选的,用于指定筛选条件。例如统计users集合中所有文档数量:

db.users.count()

如果要统计年龄大于30的用户数量:

db.users.count({age: {$gt: 30}})

在MongoDB 4.0之后,count()方法被标记为已废弃,推荐使用countDocuments()或estimatedDocumentCount()替代:

// 精确计数,支持查询条件
db.users.countDocuments({age: {$gt: 30}})

// 快速估计数量,但不支持查询条件
db.users.estimatedDocumentCount()

distinct操作

distinct操作用于获取集合中某个字段的所有不同值。这在需要获取分类列表或枚举值时非常实用。基本语法:

db.collection.distinct(field, query)

例如获取users集合中所有不同的城市:

db.users.distinct("address.city")

也可以添加查询条件,比如获取年龄大于30的用户所在的不同城市:

db.users.distinct("address.city", {age: {$gt: 30}})

distinct操作返回的是一个数组,包含所有不重复的值。对于大型集合,这个操作可能会消耗较多资源。

聚合管道中的count

在更复杂的聚合场景中,可以在聚合管道中使用$count阶段。这种方式可以与其他聚合阶段配合使用:

db.users.aggregate([
  {$match: {age: {$gt: 30}}},
  {$count: "over30Count"}
])

这个例子会返回类似这样的结果:

{"over30Count": 42}

$count阶段必须放在管道的最后,因为它会消耗所有输入文档并输出单个文档。

聚合管道中的distinct

虽然distinct可以作为独立操作使用,但在聚合管道中实现类似功能通常使用$group阶段:

db.users.aggregate([
  {$group: {_id: "$address.city"}}
])

这种方式比distinct更灵活,可以与其他聚合阶段组合。例如统计每个城市的用户数量:

db.users.aggregate([
  {$group: {
    _id: "$address.city",
    userCount: {$sum: 1}
  }}
])

性能考虑

count和distinct操作的性能受多种因素影响:

  1. 索引使用:确保查询字段有适当索引
  2. 集合大小:大型集合可能需要更长时间
  3. 分片集群:跨分片操作会有额外开销

对于countDocuments(),MongoDB会执行完整的查询计划,而estimatedDocumentCount()使用集合元数据,速度更快但不精确。

distinct操作在内存中构建所有唯一值,对于高基数字段可能消耗大量内存。替代方案是使用聚合管道的$group阶段配合allowDiskUse选项。

实际应用示例

假设有一个电商订单集合orders,包含字段:orderDate、customerId、products(数组)、totalAmount等。

统计2023年的订单数量:

db.orders.countDocuments({
  orderDate: {
    $gte: ISODate("2023-01-01"),
    $lt: ISODate("2024-01-01")
  }
})

获取所有购买过特定类别商品的客户ID:

db.orders.distinct("customerId", {
  "products.category": "电子产品"
})

使用聚合管道统计每月订单数量和不同客户数:

db.orders.aggregate([
  {$match: {orderDate: {$gte: ISODate("2023-01-01")}}},
  {$project: {
    month: {$month: "$orderDate"},
    customerId: 1
  }},
  {$group: {
    _id: "$month",
    orderCount: {$sum: 1},
    uniqueCustomers: {$addToSet: "$customerId"}
  }},
  {$project: {
    month: "$_id",
    orderCount: 1,
    customerCount: {$size: "$uniqueCustomers"}
  }}
])

高级用法

对于更复杂的需求,可以结合多个聚合阶段:

  1. 先$match筛选文档
  2. 然后$unwind展开数组
  3. 接着$group分组计算
  4. 最后$sort排序结果

例如统计每个产品类别的销售额和订单数:

db.orders.aggregate([
  {$unwind: "$products"},
  {$group: {
    _id: "$products.category",
    totalSales: {$sum: "$products.price"},
    orderCount: {$sum: 1}
  }},
  {$sort: {totalSales: -1}}
])

限制与替代方案

count和distinct有一些限制:

  1. 结果集大小限制(16MB)
  2. 内存使用限制
  3. 分片集群中的性能问题

替代方案包括:

  1. 使用聚合管道的$facet阶段并行执行多个操作
  2. 使用$sample进行抽样统计
  3. 使用Map-Reduce处理超大数据集

例如使用$facet同时计算多个统计量:

db.orders.aggregate([
  {$match: {status: "completed"}},
  {$facet: {
    "totalOrders": [{$count: "count"}],
    "byMonth": [
      {$group: {
        _id: {$month: "$orderDate"},
        count: {$sum: 1}
      }}
    ],
    "topCustomers": [
      {$group: {
        _id: "$customerId",
        orderCount: {$sum: 1}
      }},
      {$sort: {orderCount: -1}},
      {$limit: 5}
    ]
  }}
])

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

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