2016-03-15 76 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