使用聚合框架使用 MongoDB 进行组计数

Posted

技术标签:

【中文标题】使用聚合框架使用 MongoDB 进行组计数【英文标题】:Group count with MongoDB using aggregation framework 【发布时间】:2012-10-25 18:10:07 【问题描述】:

假设我的 MongoDB 架构如下所示:

car_id: "...", owner_id: "..."

这是一个多对多的关系。例如,数据可能如下所示:

+-----+----------+--------+
| _id | owner_id | car_id |
+-----+----------+--------+
|   1 |        1 |      1 |
|   2 |        1 |      2 |
|   3 |        1 |      3 |
|   4 |        2 |      1 |
|   5 |        2 |      2 |
|   6 |        3 |      4 |
|   7 |        3 |      5 |
|   8 |        3 |      6 |
|   9 |        3 |      7 |
|  10 |        1 |      1 | <-- not unique
+-----+----------+--------+

我想获取每个车主拥有的汽车数量。在 SQL 中,这可能看起来像:

SELECT owner_id, COUNT(*) AS cars_owned
FROM (SELECT owner_id FROM car_owners GROUP BY owner_id, car_id) AS t
GROUP BY owner_id;

在这种情况下,结果如下所示:

+----------+------------+
| owner_id | cars_owned |
+----------+------------+
|        1 |          3 |
|        2 |          2 |
|        3 |          4 |
+----------+------------+

我怎样才能通过聚合框架使用 MongoDB 完成同样的事情?

【问题讨论】:

@JohnnyHK,我认为我列出我尝试完成此任务的 10 种不同方法不会帮助您或其他任何人更有效地回答这个问题,因为它们不起作用。我已经做了一些工作来准确解释我正在尝试做什么,以及我可能在 SQL 中采用的方法。我查看了上下 MongoDB 文档,但我的管道聚合方法都没有奏效,可能是因为我对使用聚合框架还是新手。 在您的架构中,每个文档是否只有一辆汽车(由 id 表示)?在这种情况下,要查找所有者拥有的汽车数量,您不只是在查找集合中有多少文档具有该 owner_id 吗?在这种情况下,您可以执行类似 db.foo.find( owner_id : [owner id here] ).count() 的操作来获取集合中具有该 owner_id 的文档数。 您现有的代码不起作用完全没问题,但是通过发布它我们可以看到您采取的方向以及您可能缺少的概念。 @Louisa,这是一个多对多的关系。可以有很多车和很多车主。 可以有多个文档具有相同的owner_id/car_id 对吗?例如owner_id = 1car_id = 1? 的两个文档? 【参考方案1】:

$group 类似于 SQL Group by command。在下面的示例中,我们将根据公司成立的年份汇总公司。并计算每家公司的平均员工人数。


db.companies.aggregate([
    $group: 
      _id: 
        founded_year: "$founded_year"
      ,
      average_number_of_employees: 
        $avg: "$number_of_employees"
      
    
  , 
    $sort: 
      average_number_of_employees: -1
    
  
])

这个聚合管道有 2 个阶段

    $group $sort

现在,$group 阶段的基础是我们指定为文档一部分的_id 字段。这就是$group 运算符本身的值,它使用对arrogation 框架语法的非常严格的解释。 _id 是我们如何定义、如何控制、如何调整小组赛阶段用来组织它看到的文档的方式。

以下查询使用$sum 运算符查找人员与公司的关系:


db.companies.aggregate([
  $match: 
    "relationships.person": 
      $ne: null
    
  
, 
  $project: 
    relationships: 1,
    _id: 0
  
, 
  $unwind: "$relationships"
, 
  $group: 
    _id: "$relationships.person",
    count: 
      $sum: 1
    
  
, 
  $sort: 
    count: -1
  
])

【讨论】:

这些截图怎么了?【参考方案2】:

为了适应潜在的重复,您需要使用两个$group 操作:

db.test.aggregate([
     $group: 
        _id:  owner_id: '$owner_id', car_id: '$car_id' 
    ,
     $group: 
        _id: '$_id.owner_id',
        cars_owned:  $sum: 1 
    ,
     $project: 
        _id: 0,
        owner_id: '$_id',
        cars_owned: 1
    ]
    , function(err, result)
        console.log(result);
    
);

给出格式如下的结果:

[  cars_owned: 2, owner_id: 10 ,
   cars_owned: 1, owner_id: 11  ]

【讨论】:

很好的答案。我真的很亲近。我有 2 个流水线组,但我向 $sum 运算符提供了一个字段名称而不是 1。这解决了它。谢谢! 如何将输出限制为仅拥有多于一辆车 (cars_owned > 1) 的人? @IngviGautsson $sort : "cars_owned" : -1, $limit : 10$project 之后和] 之前添加 天哪,sql这么简单,把sql带回来!

以上是关于使用聚合框架使用 MongoDB 进行组计数的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB: 聚合框架

MongoDB $reduce(aggregation) 组与数组中嵌套文档的总和并按组计数

MongoDB - 聚合查询

使用多个字段在 MongoDB 聚合框架中按相关性排序

使用多个字段在 MongoDB 聚合框架中按相关性排序

Mongodb平均聚合查询没有组