MongoDb:使用 $lookup 查找深度嵌套的对象

Posted

技术标签:

【中文标题】MongoDb:使用 $lookup 查找深度嵌套的对象【英文标题】:MongoDb: find deeply nested object with $lookup 【发布时间】:2020-07-21 08:47:01 【问题描述】:

我有这样的收藏: 集合名称是 - 帐户。 它有子文档,例如帐户 > 建筑物 > 网关 > 设备。


    "_id" : ObjectId("5e1fe45cd05bfb0cc549297d"),
    "apiCallCount" : 0,
    "email" : "info@data.com",
    "password" : "dummy",
    "userName" : "AAAAA",
    "companyName" : "The AAAAAA",
    "apiKey" : "5e1fe45cd05bfb0cc549297c",
    "solutionType" : "VVVVVV",
    "parentCompany" : "",
    "buildings" : [ 
        
            "_id" : ObjectId("5e1fe5e3d05bfb0cc5494146"),
            "buildingName" : "xxxxxx",
            "address" : "xxx",
            "suite" : "101",
            "floor" : "22",
            "timeZone" : "us/eastern",
            "gateways" : [ 
                
                    "_id" : ObjectId("5e1fe624d05bfb0cc549453a"),
                    "gatewayName" : "CC-GW-THF-001",
                    "gatewayKey" : "gk_5e1fe624d05bfb0cc549453a",
                    "suite" : "area1",
                    "devices" : [ 
                        
                            "_id" : ObjectId("5e1fe751d05bfb0cc549578d"),
                            "serialNumber" : "129300000013",
                            "area" : "area1",
                            "connectionStatus" : 1,
                            "gatewayKey" : "gk_5e1fe624d05bfb0cc549453a",
                            "applicationNumber" : 30,
                            "firmwareVersion" : "1.0",
                            "needsAttention" : false,
                            "verificationCode" : "GAAS",
                            "createdAt" : ISODate("2020-01-16T04:32:17.899Z"),
                            "updatedAt" : ISODate("2020-01-16T08:53:54.460Z")
                        
                    ],
                    "createdAt" : ISODate("2020-01-16T04:27:16.678Z"),
                    "updatedAt" : ISODate("2020-01-16T08:53:54.460Z")
                , 
                
                    "_id" : ObjectId("5e1fe651d05bfb0cc54947f0"),
                    "gatewayName" : "AA-GW-THF-002",
                    "gatewayKey" : "gk_5e1fe651d05bfb0cc54947f0",
                    "suite" : "area2",
                    "devices" : [ 
                        
                            "_id" : ObjectId("5e1fe7a9d05bfb0cc5495cdf"),
                            "serialNumber" : "129300000012",
                            "area" : "area2",
                            "connectionStatus" : 0,
                            "gatewayKey" : "gk_5e1fe651d05bfb0cc54947f0",
                            "applicationNumber" : 30,
                            "firmwareVersion" : "1.0",
                            "needsAttention" : false,
                            "verificationCode" : "VG3K",
                            "createdAt" : ISODate("2020-01-16T04:33:45.698Z"),
                            "updatedAt" : ISODate("2020-01-16T08:54:17.604Z")
                        
                    ],
                    "createdAt" : ISODate("2020-01-16T04:28:01.532Z"),
                    "updatedAt" : ISODate("2020-01-16T08:54:17.604Z")
                , 


            ],
            "createdAt" : ISODate("2020-01-16T04:26:11.941Z"),
            "updatedAt" : ISODate("2020-01-16T08:56:32.657Z")
        
    ],
    "createdAt" : ISODate("2020-01-16T04:19:40.310Z"),
    "updatedAt" : ISODate("2020-04-06T18:18:39.628Z"),
    "__v" : 1,

我有 accountId、buildingId、gatewayId、deviceId。 我正在尝试使用 $lookup 运算符查找匹配的设备对象。

我想我必须先使用 buildingId 找到建筑物对象,然后使用 gatewayId 过滤该建筑物下的网关,然后使用我拥有的 deviceId 找到设备对象。

我基本上需要访问设备对象字段才能在最终输出中进行投影。 难以为使用查找运算符提出正确的管道。

到目前为止我有这个:

    db.getCollection('test').aggregate([

    $lookup: 

        from: 'account',
        let: 
            accountId: "$accountId"
        ,
        pipeline: [

            
                "$match": 
                    "$expr": 
                        "$eq": ["$_id", "$$accountId"]
                    
                
            ,
        ],

        as: "accountDetails"
    
, 
    $unwind: "$accountDetails"
, 
    $lookup: 

        from: 'account',
        let: 
            accountId: "$accountId",
            buildingId: "$buildingId",
            buildings: "$accountDetails"
        ,

        pipeline: [

            
                "$match": 
                    "$expr": 
                        "$eq": ["$buildings._id", "$$buildingId"] // how to dig through nested document to get to devices ? 
                    
                
            ,
        ],

        as: "buildingDetails"
    

          
            $project:  ... ... 
              
        ])

如果我这样做:


    $lookup: 
        from: 'account',
        localField: "accountId",
        foreignField: "_id",
        as: "accountDetails"
    
,

accountDetails 让我可以根据 accountId 访问帐户文档。但我需要到达建筑物 > 网关 > 设备并找到匹配的设备。

更新:

我忘了提,我在这里处理 2 个系列。 感测结果和账目。

主要目的是从sensingresults中聚合数据,同时也从account collection中找到deviceId并返回结果。

这就是为什么需要查找来加入 2 个集合的原因?

更新2:

当前输出:

  
    "accountId": ObjectId("5e1fe45cd05bfb0cc549297d"),
    "avgZoneCountNumber": 0,
    "avgZoneCountNumberInstant": 0,
    "buildingId": ObjectId("5e1fe5e3d05bfb0cc5494146"),
    "companyName": "The AAAAAA",
    "createdAt": ISODate("1970-01-01T00:00:00Z"),
    "dateHour": "2020-03-19T18",
    "deviceId": ObjectId("5e1fe81ed05bfb0cc5496406"),
    "gatewayId": ObjectId("5e1fe6a6d05bfb0cc5494d25"),
    "minuteBucket": 1
  

预期结果:


    "accountId": ObjectId("5e1fe45cd05bfb0cc549297d"),
    "avgZoneCountNumber": 0,
    "avgZoneCountNumberInstant": 0,
    "buildingId": ObjectId("5e1fe5e3d05bfb0cc5494146"),
    "createdAt": ISODate("1970-01-01T00:00:00Z"),
    "dateHour": "2020-03-19T18",
    "deviceId": ObjectId("5e1fe81ed05bfb0cc5496406"),
    "gatewayId": ObjectId("5e1fe6a6d05bfb0cc5494d25"),
    "minuteBucket": 1,
    "serialNumber: 1, // this value should come from device object 
    "area": 1  // this value should come from device object 
  

【问题讨论】:

请正确格式化代码块 我格式化并更新了问题 【参考方案1】:

您可以使用$filter、$arrayElemAt 和$let 找到嵌套的device

device: 
    $let: 
        vars: 
            building:  
                $arrayElemAt: [  $filter:  input: "$company_name.buildings", cond:  $eq: [ "$$this._id", "$buildingId" ]  , 0 ] 
                
        ,
        in: 
            $let: 
                vars: 
                    gateway: 
                        $arrayElemAt: [  $filter:  input: "$$building.gateways", cond:  $eq: [ "$$this._id", "$gatewayId" ]  , 0 ] 
                    
                ,
                in:  $arrayElemAt: [  $filter:  input: "$$gateway.devices", cond:  $eq: [ "$$this._id", "$deviceId" ]  , 0 ] 
            
        
    

Full Solution

【讨论】:

我忘了说,我在这里处理 2 个系列。感测结果和账目。主要目的是聚合传感结果中的数据,也可以从账户集合中找到deviceId并返回结果。这就是为什么需要查找来加入 2 个集合的原因? mongoplayground.net/p/VV_9sBp57M4 我添加了我的原始聚合,请注意第一次查找只是从帐户中获取 companyName。我需要挖掘帐户并使用我拥有的那些 id 获取设备对象。 @newdeveloper 你也可以显示数据吗? Mongo Playground 可以处理多个集合,点击“docs”有一个例子 我创建了 2 个集合 - mongoplayground.net/p/iV7mdIL_-qv 它抛出了一个错误,但我认为它只是没有得到集合名称,你会知道我认为 @newdeveloper 谢谢,这有帮助,看看我修改后的答案

以上是关于MongoDb:使用 $lookup 查找深度嵌套的对象的主要内容,如果未能解决你的问题,请参考以下文章

$lookup 与深度嵌套的对象

MongoDB 聚合 - 我如何“$lookup”嵌套文档“_id”?

mongodb $查找带有投影的数组中的嵌套对象

具有 3 个级别的 MongoDB 嵌套查找

mongoDB对嵌套对象数组的聚合查找

MongoDB $lookup 在 2 级嵌套文档上不使用 $unwind