MongoDB聚合:计算不同字段的数量

47

我正在尝试编写一个聚合,以识别使用多个付款来源的账户。典型的数据如下。

{
 account:"abc",
 vendor:"amazon",
}
 ...
{
 account:"abc",
 vendor:"overstock",
}

现在,我想制作一个类似于这个的账户列表

{
 account:"abc",
 vendorCount:2
}

我该如何使用Mongo的聚合框架来编写这个查询

8个回答

79
我使用 $addToSet 和 $unwind 运算符解决了这个问题。 Mongodb聚合统计数组/集合的大小
db.collection.aggregate([
{
    $group: { _id: { account: '$account' }, vendors: { $addToSet: '$vendor'} }
},
{
    $unwind:"$vendors"
},
{
    $group: { _id: "$_id", vendorCount: { $sum:1} }
}
]);

希望能对某人有所帮助


2
这可能适用于集合的亲和性足够小的情况,但对于大数据场景来说,这是行不通的(想象一下如果您有数十万个唯一供应商)。 - Dmitry B.
4
这个答案解决了大数据场景:https://dev59.com/wWAf5IYBdhLWcg3wVxcp#24770233 - anushr
考虑到我们可以通过 results.get("vendors").size(); 计算出供应商数量,是否真的有必要再次迭代 $vendors 呢? - Jerry Chin
3
@JerryChin可以在管道中使用 $size 运算符。原文链接:https://dev59.com/qGMl5IYBdhLWcg3wTlnY#49210341 - Hett
当您有其他字段不想与$unwind和$group混淆时,这种方法会导致问题。 - Anuj Gupta
显示剩余2条评论

27

我认为最好按照以下方式执行查询,这样就可以避免展开操作

db.t2.insert({_id:1,account:"abc",vendor:"amazon"});
db.t2.insert({_id:2,account:"abc",vendor:"overstock"});


db.t2.aggregate([
{ $group : { _id : { "account" : "$account", "vendor" : "$vendor" }, number : { $sum : 1 } } },
{ $group : { _id : "$_id.account", number : { $sum : 1 } } }
]);

这将显示您期望的结果。

{ "_id" : "abc", "number" : 2 }

这假设每个账户至少有一个供应商。 - Asya Kamsky

23

您可以使用集合

db.test.aggregate([
    {$group: { 
      _id: "$account", 
      uniqueVendors: {$addToSet: "$vendor"}
    }},
    {$project: {
      _id: 1, 
      vendorsCount: {$size: "$uniqueVendors"}
    }}
]);

1
请注意,这仅适用于所有供应商都适合一个文档的情况,该文档限制为16MB。对于大多数情况来说可能还好,但如果有数百万个供应商和/或供应商ID很长(GUID字符串? :-/),那么我想双重分组是正确的方法。 - Qtax

11

我不明白为什么有人需要两次使用$group

db.collection.aggregate([ { $group: { "_id": "$account", "number": { $sum: 1 } } } ])

我想是因为他们想要重命名键并重新格式化。但这确实更好,更有效。 - c24b
5
这是“不同计数”吗? - cupen
1
查询语句应该是“select group_id, count(*) from table_name group by group_id”,而不是“select count(distinct group_id)) from table_name”。 - lispc
7
这个答案是错误的,因为它假设没有账户会有相同的供应商两次(即它假设每个账户的文档数量与不同供应商的数量相同)。这是完全错误的。 - Asya Kamsky
此答案返回具有相同账户的所有文档数。例如:account:“abc”,account:“abc”,account:“abc”,account:“bbb”-> abc:3,bbb:1。 - dang

5

这种方法不使用$unwind和其他额外操作。此外,如果在聚合中添加了新内容,这不会影响任何内容。接受的答案存在缺陷。如果在$group中有其他累积字段,则会在接受的答案的$unwind阶段引起问题。

db.collection.aggregate([{
    "$group": {
        "_id": "$account",
        "vendors": {"$addToSet": "$vendor"}
    }
},
{
    "$addFields": {
        "vendorCount": {
            "$size": "$vendors"
        }
    }
}])

3
这个答案与@Hett在18个月前发布的答案完全相同。 - Asya Kamsky

-1

为了识别使用多个付款来源的账户:

  1. 使用分组来计算来自多个账户记录的数据,并按帐户进行分组以进行计数
  2. 使用匹配案例来过滤仅具有多种付款方式的帐户
  db.payment_collection.aggregate([ { $group: {"_id":"$account" ,
 "number":{$sum:1}} }, {
                     "$match": {
                         "number": { "$gt": 1 }
                      }
                 } ])

这将完美地运行,


-3
db.UserModule.aggregate(
{ $group : { _id : { "companyauthemail" : "$companyauthemail", "email" : "$email" }, number : { $sum : 1 } } },
{ $group : { _id : "$_id.companyauthemail", number : { $sum : 1 } } }
);

2
虽然这段代码片段可能是解决方案,但包括解释真的有助于提高您的帖子质量。请记住,您正在回答未来读者的问题,而这些人可能不知道您的代码建议原因。 - Narendra Jadhav
此外,它基本上与现有答案相同。 - Asya Kamsky

-5

一个例子

db.collection.distinct("example.item").forEach( function(docs) {
    print(docs + "==>>" + db.collection.count({"example.item":docs}))
});

1
你应该提供一个描述来解释为什么这个解决方案适用于这个问题。同时,让示例代码使用与实际问题相同的数据和变量上下文也非常有帮助。在StackOverflow上,这个答案将被认为是“低质量”的;低质量的答案往往会吸引负评,并可能导致你被禁止回答更多的问题。 - Michael Gaskill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接