MongoDB 聚合两个集合,返回附加字段作为计数

Posted

技术标签:

【中文标题】MongoDB 聚合两个集合,返回附加字段作为计数【英文标题】:MongoDB aggregate two collections, return additional field as count 【发布时间】:2019-05-16 15:50:51 【问题描述】:

(请参阅下面的编辑)

我正在尝试从同一个 MongoDB 数据库中的两个单独集合中聚合数据。

“accounts”集合包含用户信息(已清理):

 
    _id: ObjectId("5c0d64a4224a2900108c005f"),
    "username" : "mike22", 
    "email" : "mike22@<domain>.com",
    "country" : GB,
    "created" : ISODate("2018-11-26T23:37:49.051Z") 
,
 
    _id: ObjectId("5a0d64a4527h2880108c0445"),
    "username" : "mike23", 
    "email" : "mike23@<domain>.com", 
    "country" : DE,
    "created" : ISODate("2018-11-26T23:37:49.051Z")
,
 
    _id: ObjectId("5a3334a45zzz2884448c0445"),
    "username" : "mike24", 
    "email" : "mike24@<domain>.com", 
    "country" : DE,
    "created" : ISODate("2018-11-26T23:37:49.051Z")

“设备”集合包含所有用户的设备定义。用户可能在此集合中定义了许多设备,并且许多用户设备在此集合中。

此集合中的单个设备定义如下:

 
    "_id" : ObjectId("5c10138c73bbe0001018e415"), 
    "capabilities" : [ 
        "BrightnessController", 
        "PowerController" 
    ], 
    "displayCategories" : [ 
        "LIGHT" 
    ], 
    "friendlyName" : "Test1", 
    "description" : "Test device 1", 
    "reportState" : true, 
    "username" : "mike22", 
    "endpointId" : 11, 
    "__v" : 0 
,
 
    "_id" : ObjectId("5c10138c73bbe0001018e415"), 
    "capabilities" : [ 
        "PowerController" 
    ], 
    "displayCategories" : [ 
        "SWITCH" 
    ], 
    "friendlyName" : "Test2", 
    "description" : "Test device 2", 
    "reportState" : true, 
    "username" : "mike23", 
    "endpointId" : 12, 
    "__v" : 0 
,
 
    "_id" : ObjectId("5c10138c73bbe0001018e415"), 
    "capabilities" : [ 
        "PowerController" 
    ], 
    "displayCategories" : [ 
        "SMARTPLUG" 
    ], 
    "friendlyName" : "Test3", 
    "description" : "Test device 3", 
    "reportState" : true, 
    "username" : "mike22", 
    "endpointId" : 13, 
    "__v" : 0 

我可以使用下面的汇总来显示每位用户的设备数量:

db.accounts.aggregate([
     
        $lookup: 
            from : "devices",
            localField : "username",
            foreignField : "username",
            as : "userdevs"
        ,
    ,
     $unwind:"$userdevs" ,
     $group :  _id : "$username", count :  $sum : 1   
 ])

上述数据/聚合的示例输出:

 "_id" : "mike22", "count" : 2 ,
 "_id" : "mike23", "count" : 1 

(注意没有设备的用户现在丢失/应该为零计数?!)

但是,我想返回每个用户的所有字段加上一个新字段,该字段向我显示他们在“设备”集合中拥有的设备数量。我正在寻找的输出如下:

 
    "_id" : ObjectId("5c0d64a4224a2900108c005f"),
    "username" : "mike22", 
    "email" : "mike22@<domain>.com",
    "country" : GB,
    "created" : ISODate("2018-11-26T23:37:49.051Z"),
    "countDevices": 2
,
 
    "_id" : ObjectId("5a0d64a4527h2880108c0445"),
    "username" : "mike23", 
    "email" : "mike23@<domain>.com", 
    "country" : DE,
    "created" : ISODate("2018-11-26T23:37:49.051Z"),
    "countDevices": 1
,
 
    "_id" : ObjectId("5a0d64a4527h2880108c0445"),
    "username" : "mike24", 
    "email" : "mike24@<domain>.com", 
    "country" : DE,
    "created" : ISODate("2018-11-26T23:37:49.051Z"),
    "countDevices": 0


编辑 16/12: 所以我已经接近了下面的汇总。但零计数用户不见了。

use users
    db.accounts.aggregate([
         
            $lookup: 
                from : "devices",
                localField : "username",
                foreignField : "username",
                as : "userdevs"
            ,
        ,
         $unwind: "$userdevs",
         $group :  _id : 
            _id: "$_id",
            username: "$username",
            email: "$email",
            country: "$country",
            region: "$region",
        ,
        countDevices :  $sum : 1   
     ])

2012 年 16 月第二次编辑:

我在下面找到了所需的聚合:

db.accounts.aggregate([
   "$lookup": 
    "from": "devices",
    "let":  "username": "$username" ,
    "pipeline": [
       "$match": 
        "$expr":  "$eq": [ "$$username", "$username" ] 
      ,
       "$count": "count" 
    ],
    "as": "deviceCount"    
  ,
   "$addFields": 
    "countDevices":  "$sum": "$deviceCount.count" 
  
])

【问题讨论】:

【参考方案1】:

按秒编辑,我使用的聚合如下:

db.accounts.aggregate([
   "$lookup": 
    "from": "devices",
    "let":  "username": "$username" ,
    "pipeline": [
       "$match": 
        "$expr":  "$eq": [ "$$username", "$username" ] 
      ,
       "$count": "count" 
    ],
    "as": "deviceCount"    
  ,
   "$addFields": 
    "countDevices":  "$sum": "$deviceCount.count" 
  
])

【讨论】:

【参考方案2】:

首先,您可以使用如下投影来展平您得到的答案:

 $project : 
        _id : '$_id._id',
        username : '$_id.username',
        email : '$_id.email',
        country : '$_id.country',
        region : '$_id.region',
        countDevices: 1
    

在管道中的 $group 之后添加它,您将获得问题中想要的结果。

关于零计数用户,有一种方法可以在数据库中使用 mongoDB 来处理这个问题,详细解释 here 但我不推荐,最好你处理这种问题客户端。

【讨论】:

感谢您的回答 - 我刚刚使用我在这里找到的汇总示例更新了对该问题的第二次编辑:***.com/questions/44888354/… 这种方法有问题吗? 我不知道聚合管道中的管道!看起来是个不错的方法!

以上是关于MongoDB 聚合两个集合,返回附加字段作为计数的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB在具有附加字段的对象数组上聚合$lookup

计数字段包含具有聚合mongodb的集合中的数据

mongodb聚合两个集合并添加新字段

Mongodb 聚合管道限制 $lookup 字段

如何使用 MongoDB 聚合管道从集合中的两个子文档中返回最小值?

MongoDB(猫鼬)聚合计数集合中特定 ObjectID 的实例