2016-03-15 74 views
10

我需要从数据库中检索整个单个对象层次结构作为JSON。实际上,有关任何其他解决方案来达到这一结果的建议都会得到高度评价。我决定使用MongoDB及其$查找支持。3个级别的MongoDB嵌套查找

所以我有三个类别:

{ "_id" : "2", "name" : "party2" } 
{ "_id" : "5", "name" : "party5" } 
{ "_id" : "4", "name" : "party4" } 
{ "_id" : "1", "name" : "party1" } 
{ "_id" : "3", "name" : "party3" }  

地址

{ "_id" : "a3", "street" : "Address3", "party_id" : "2" } 
{ "_id" : "a6", "street" : "Address6", "party_id" : "5" } 
{ "_id" : "a1", "street" : "Address1", "party_id" : "1" } 
{ "_id" : "a5", "street" : "Address5", "party_id" : "5" } 
{ "_id" : "a2", "street" : "Address2", "party_id" : "1" } 
{ "_id" : "a4", "street" : "Address4", "party_id" : "3" } 

addressComment

{ "_id" : "ac2", "address_id" : "a1", "comment" : "Comment2" } 
{ "_id" : "ac1", "address_id" : "a1", "comment" : "Comment1" } 
{ "_id" : "ac5", "address_id" : "a5", "comment" : "Comment6" } 
{ "_id" : "ac4", "address_id" : "a3", "comment" : "Comment4" } 
{ "_id" : "ac3", "address_id" : "a2", "comment" : "Comment3" } 

我需要检索所有相关地址和地址注释作为记录的一部分。我的聚合:

db.party.aggregate([{ 
    $lookup: { 
     from: "address", 
     localField: "_id", 
     foreignField: "party_id", 
     as: "address" 
    } 
}, 
{ 
    $unwind: "$address" 
}, 
{ 
    $lookup: { 
     from: "addressComment", 
     localField: "address._id", 
     foreignField: "address_id", 
     as: "address.addressComment" 
    } 
}]) 

结果很奇怪。有些记录是可以的。但与_id 4的党失踪了(没有地址)。结果集中也有两个Party _id 1(但具有不同的地址):

{ 
    "_id": "1", 
    "name": "party1", 
    "address": { 
     "_id": "2", 
     "street": "Address2", 
     "party_id": "1", 
     "addressComment": [{ 
      "_id": "3", 
      "address_id": "2", 
      "comment": "Comment3" 
     }] 
    } 
}{ 
    "_id": "1", 
    "name": "party1", 
    "address": { 
     "_id": "1", 
     "street": "Address1", 
     "party_id": "1", 
     "addressComment": [{ 
      "_id": "1", 
      "address_id": "1", 
      "comment": "Comment1" 
     }, 
     { 
      "_id": "2", 
      "address_id": "1", 
      "comment": "Comment2" 
     }] 
    } 
}{ 
    "_id": "3", 
    "name": "party3", 
    "address": { 
     "_id": "4", 
     "street": "Address4", 
     "party_id": "3", 
     "addressComment": [] 
    } 
}{ 
    "_id": "5", 
    "name": "party5", 
    "address": { 
     "_id": "5", 
     "street": "Address5", 
     "party_id": "5", 
     "addressComment": [{ 
      "_id": "5", 
      "address_id": "5", 
      "comment": "Comment5" 
     }] 
    } 
}{ 
    "_id": "2", 
    "name": "party2", 
    "address": { 
     "_id": "3", 
     "street": "Address3", 
     "party_id": "2", 
     "addressComment": [{ 
      "_id": "4", 
      "address_id": "3", 
      "comment": "Comment4" 
     }] 
    } 
} 

请帮我这个。我对MongoDB相当陌生,但我觉得它可以做我需要的。

回答

19

你的'麻烦'的原因是第二个聚合阶段 - { $unwind: "$address" }。它删除_id: 4(因为它的地址数组为空)的一方的记录,并为参与方_id: 1_id: 5(因为它们每个都有两个地址)产生两条记录。

  • 为了防止无地址删除各方应$unwind阶段的preserveNullAndEmptyArrays选项设置为true

  • 为防止双方因其不同的地址重复工作,您应该将$group聚合阶段添加到您的管道中。此外,使用$project阶段与$filter运算符排除输出中的空地址记录。

db.party.aggregate([{ 
    $lookup: { 
    from: "address", 
    localField: "_id", 
    foreignField: "party_id", 
    as: "address" 
    } 
}, { 
    $unwind: { 
    path: "$address", 
    preserveNullAndEmptyArrays: true 
    } 
}, { 
    $lookup: { 
    from: "addressComment", 
    localField: "address._id", 
    foreignField: "address_id", 
    as: "address.addressComment", 
    } 
}, { 
    $group: { 
    _id : "$_id", 
    name: { $first: "$name" }, 
    address: { $push: "$address" } 
    } 
}, { 
    $project: { 
    _id: 1, 
    name: 1, 
    address: { 
     $filter: { input: "$address", as: "a", cond: { $ifNull: ["$$a._id", false] } } 
    } 
    } 
}]); 
+0

坦克你沙德!没有与记录4虽然一个小问题: '{ \t “_id”: “4”, \t “名”: “的派对”, \t “地址”:[{ \t \t “addressComment”:[] \t}] }' 正如你所看到的 - 地址应该是空的,但它是一个空的记录,而不是......如果addressComment为空,我们可以跳过地址吗?在其他情况下,这个地址将被视为一个记录。 – Yuriy

+1

实际上,我看到提供的解决方案按照预期的方式工作,根据$ unwind操作(自3.2开始)的新“preserveNullAndEmptyArrays”字段的描述。现在我们可以跳过“$ project”步骤并指定这个“$ unwind”而不是简单的:'$ unwind:{path:“$ address”,preserveNullAndEmptyArrays:true}'。我会接受你的回答,谢谢你的快速和明确的回应! – Yuriy

+0

@Shad我有类似的问题。在这里,OP的代码在'party'集合中只有一个名为'name'的属性,因此您使用'$ first'来获取'$ group'。假设我有10多个属性,那么是否有任何方法可以自动获取所有属性而不单独提及每个属性? – Xyroid