2017-06-12 23 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),尽管最近编辑过,但这个问题有它自己的现代处理。 –