2017-06-12 53 views
0

我是一個MongoDb新手。我越來越好,但還沒有專家。我試圖以一種合理的方式設置我的收藏。我想保留一些鏈接到只是_ids數組中的外部文檔以及具有_id的對象數組。

我創建了一個JSON文檔與我想的筆記充分說明什麼,我試圖做...

// (item) Character Inventory/Items collection 
[ 
    { 
     "_id": "1234", 
     "name": "Sword", 
     "descr": "Long sword, well worn, light rust", 
     "encumber": 2, 
     "del": false 
    }, 
    { 
     "_id": "1271", 
     "name": "Pouch", 
     "descr": "Small leather waist pouch, suitable for coins", 
     "encumber": 0, 
     "del": false 
    } 
] 

// (charnpcclass) Character Classes collection 
[ 
    { "_id": "2", "name": "Thief", "del": false }, 
    { "_id": "3", "name": "Cleric", "del": false } 
] 

// (charnpcalign) Character Alignments collection 
[ 
    { "_id": "3", "name": "Lawful Good", "del": false }, 
    { "_id": "4", "name": "Neutral", "del": false } 
] 

// (character) Characters collection 
[ 
    { 
     "_id": "3345", 
     "name": "Offut 'Dead Dog' Dubro", 
     "description": "Halfling, scruffy, looks homeless", 
     "align": ObjectId("4"), 
     "classes": [ 
      ObjectId("2"), 
      ObjectId("3") 
     ], 
     "carrying": [ 
      { "itemId": ObjectId("1271"), "qty":1, "where": "Sheath inside vest", "visible": false } 
      { "itemId": ObjectId("1234"), "qty":1, "where": "Sword scabbard at waist", "visible": true } 
     ], 
     "del": false 
    } 
] 

// ------------------------------------------------------------ 
// This is my MongoDb aggregation in the REST api routes 

var linkedModels = [ 
    { 
     "$match": { "del": false } 
    }, { 
     "$lookup": { 
      from: "charnpcclass", 
      localField: "classes", 
      foreignField: "_id", 
      as: "linked_classes" 
     } 
    }, { 
     "$lookup": { 
      from: "charnpcalign", 
      localField: "alignId", 
      foreignField: "_id", 
      as: "linked_align" 
     } 
    }, { 
     "$lookup": { 
      from: "item", 
      localField: "carrying.itemId", 
      foreignField: "_id", 
      as: "linked_carrying" 
     } 
    } 
]; 
db.collection('character').aggregate(linkedModels).toArray(function (err, docs) { 
    res.json(201, docs); 
    next(); 
}); 


// Query for Character, return items carrying with data from items collection 

// ------------------------------------------------------------ 
// WHAT I *WANT* IN RESPONSE... 
{ 
    "id": "3345", 
    "name": "Offut 'Dead Dog' Dubro", 
    "description": "Halfling, scruffy, looks homeless", 
    "align": "4", 
    "classes": [ 
     "2", 
     "3" 
    ], 
    "carrying": [ 
     { "itemId": "1271", "qty":1, "where": "Sheath inside vest", "visible": false } 
     { "itemId": "1234", "qty":1, "where": "Sword scabbard at waist", "visible": true } 
    ], 
    "linked_align": [ 
     { "_id": "4", "name": "Neutral" }, 
    ], 
    "linked_classes": [ 
     { "_id": "2", "name": "Thief" }, 
     { "_id": "3", "name": "Cleric" } 
    ], 
    "linked_carrying": [ 
     { "_id": "1271", "name": "Dagger", "encumber": 0 }, 
     { "_id": "1234", "name": "Sword", "encumber": 2 } 
    ] 
} 

// ------------------------------------------------------------ 
// WHAT I ACTUALLY GET IN RESPONSE 
{ 
    "id": "3345", 
    "name": "Offut 'Dead Dog' Dubro", 
    "description": "Halfling, scruffy, looks homeless", 
    "align": "4", 
    "classes": [ 
     "2", 
     "3" 
    ], 
    "carrying": [ 
     { "itemId": "1271", "qty":1, "where": "Sheath inside vest", "visible": false } 
     { "itemId": "1234", "qty":1, "where": "Sword scabbard at waist", "visible": true } 
    ], 
    "linked_align": [ 
     { "_id": "4", "name": "Neutral" }, 
    ], 
    "linked_classes": [], 
    "linked_carrying": [] 
} 

,我希望你注意到的正上方,在JSON響應例的下面的問題。我的鏈接陣列是空的,我不知道如何解決這個問題。

我將不勝感激您的專家MongoDB的查詢建議:-)

+0

如果你是從一個新的位置「建模」,最好的做法是將外鍵包含在「子」內而不是「父子」內的「子列」。問題在於,對於數組中的對象結構,您不能直接使用'$ lookup'並要求數組上的'$ unwind'來處理(這可能會改變)。如果未正確處理,使用'$ unwind'多個數組可能會導致[笛卡爾積](https://en.wikipedia.org/wiki/Cartesian_product)。由於您不是創建大型數組,因此還需要將外鍵保留在子級中。 –

+0

@NeilLunn如果你的父母恰好與一個孩子有聯繫,這是有道理的。你可以看到我在「對齊」中這樣做。然而,這並不能滿足像我的示例JSON中所示的「類」和/或「攜帶」之類的1:N關係的需要。 – Locohost

+0

我想你是誤會。父'''_id「:」123「}'孩子'{」_id「:1,」parent「:」123「},{」_id「:2,」parent「:」123「}'操作符允許通過使用外鍵來檢索多個或奇異的孩子。在這種情況下,來自孩子的「父母」。這消除了存儲在父數組內的需要。因此'{「$ lookup」:{「from」:「child」,「localField」:「_id」,「foreignField」:「parent」,「as」:「children」}}'。這取決於案件。通過內嵌的文檔和引用,就可以使用'$ unwind'。我會分開rels –

回答

3

你必須$unwind扁平化標量和子文件外國_ids,並在管道的末尾添加$group階段找回原來的結構。

$first蓄電池保持領域和$push$arrayElemAt積累數組值調整爲$unwind

var linkedModels = [ 
    { 
     "$match": { "del": false } 
    }, 
    { 
     "$lookup": { 
      from: "charnpcalign", 
      localField: "align", 
      foreignField: "_id", 
      as: "linked_align" 
     } 
    }, 
    { 
     "$unwind":"$classes" 
    }, 
    { 
     "$lookup": { 
      from: "charnpcclass", 
      localField: "classes", 
      foreignField: "_id", 
      as: "linked_classes" 
     } 
    }, 
    { 
     "$group": { 
      "_id": "$_id", 
      "name": {"$first":"$name"}, 
      "align": {"$first":"$align"}, 
      "classes":{"$push":"$classes"}, 
      "carrying":{"$first":"$carrying"}, 
      "linked_align":{"$first":"$linked_align"}, 
      "linked_classes":{"$push":{"$arrayElemAt":["$linked_classes",0]}} 
     } 
    }, 
    { 
     "$unwind":"$carrying" 
    }, 
    { 
     "$lookup": { 
      from: "item", 
      localField: "carrying.itemId", 
      foreignField: "_id", 
      as: "linked_carrying" 
     } 
    }, 
    { 
     "$group": { 
      "_id": "$_id", 
      "name": {"$first":"$name"}, 
      "align": {"$first":"$align"}, 
      "classes":{"$first":"$classes"}, 
      "linked_align":{"$first":"$linked_align"}, 
      "carrying":{"$push":"$carrying"}, 
      "linked_carrying":{"$push":{"$arrayElemAt":["$linked_carrying",0]}} 
     } 
    } 
] 

你不會在3.4版本需要$unwind標量陣列(classes)上,你可以分別替換{"classes":{"$push":"$classes"}} & {"linked_classes":{"$push":{$arrayElemAt:["$linked_classes",0]}}}{"classes":{"$first":"$classes"}} & {"linked_classes":{"$first":"$linked_classes"}}

+0

如果使用'$ unwind',那麼你「應該」分別對每個'$ unwind'和後續的$ $ lookup進行配對,當然'$ group'會回到數組形式。如果你不這樣做,那麼你最終會得到一個[笛卡爾產品](https://en.wikipedia.org/wiki/Cartesian_product),因爲第一個數組的內容將被重複用於第二個陣列「未纏繞」。理想情況下,模型根本不應該使用數組,而是將外鍵引用給子內的主人。這也比較好。 –

+0

@NeilLunn感謝您的好評,並重新安排了管道,因爲您已經注意到了。 – Veeram

+0

你不太明白。除非你在每一對操作之後「回到陣列」,那麼「笛卡爾積」就是結果。您可能需要閱讀鏈接才能理解這一點。只要運營商已經存在,這就是一個'放鬆'問題。 Heres實際上是一個演示,我的意思是[彙總兩個數組的$ sum值](https://stackoverflow.com/a/28827709/2313887),儘管最近編輯過,但這個問題有它自己的現代處理。 –