MongoDB需要$嵌套foreach循环中的数组

时间:2017-05-25 03:48:35

标签: mongodb mongodb-query aggregation-framework

我有3个集合:父母,子女和带有数据子集的链接,如:

父母:

{ "_id": 1, "PID" : 1, "Pname" : "Joe", "Sal" : 20000 },
{ "_id": 2, "PID" : 2, "Pname" : "Jim", "Sal" : 14100 },
{ "_id": 3, "PID" : 3, "Pname" : "Bob", "Sal" : 13500 },
{ "_id": 4, "PID" : 4, "Pname" : "Amy", "Sal" : 12000 },
{ "_id": 5, "PID" : 5, "Pname" : "George", "Sal" : 10000 }

儿童:

{ "_id" : 1, "CID" : 1, "Cname" : "Ronney", "Age" : 10 },
{ "_id" : 2, "CID" : 2, "Cname" : "Mo", "Age" : 11 },,
{ "_id" : 3, "CID" : 3, "Cname" : "Adam", "Age" : 13 },
{ "_id" : 4, "CID" : 4, "Cname" : "Eve", "Age" : 21 },
{ "_id" : 5, "CID" : 5, "Cname" : "Johny", "Age" : 19 },
{ "_id" : 6, "CID" : 6, "Cname" : "Sammy", "Age" : 25 },
{ "_id" : 7, "CID" : 7, "Cname" : "Sammy", "Age" : 23 }

链接:

{ "_id" : 1, "PID" : 1, "CID" : 1 },
{ "_id" : 2, "PID" : 1, "CID" : 3 },
{ "_id" : 3, "PID" : 2, "CID" : 5 },
{ "_id" : 4, "PID" : 2, "CID" : 7 },
{ "_id" : 5, "PID" : 2, "CID" : 2 },
{ "_id" : 6, "PID" : 4, "CID" : 4 },
{ "_id" : 7, "PID" : 5, "CID" : 6 }

我需要使用将父ID绑定到子ID的链接集合将$推送到父集合中。因此,例如父1将具有:

{ "_id" :1, "PID" : 1, "Pname" : "Joe", "Sal" : 20000, “Children” : [“Ronny”, ”Adam”]} }

我认为我可以使用嵌套的foreach循环来实现这一目标,但我对如何使用感到困惑。

非常感谢任何帮助!

1 个答案:

答案 0 :(得分:0)

你可以放弃使用"嵌套循环"任何支持$lookup聚合管道操作的MongoDB版本。这将允许"加入"来自多个来源的数据到一个结果集。

鉴于收藏品#34;父母","儿童"和"链接",您希望执行两个$lookup操作,然后执行$unwind语句,以获取来自"链接"的相关数据。首先,然后将其加入"孩子"。

为方便起见,您可以$group将子名称转换为每个父项的数组:

db.parents.aggregate([
  { "$lookup": {
    "from": "links",
    "localField": "PID",
    "foreignField": "PID",
    "as": "children"
  }},
  { "$unwind": "$children" },
  { "$lookup": {
    "from": "children",
    "localField": "children.CID",
    "foreignField": "CID",
    "as": "children"
  }},
  { "$unwind": "$children" },
  { "$group": {
    "_id": "$_id",
    "children": { "$push": "$children.Cname" }
  }}
])

在您的数据示例中,输出如下:

{ "_id" : 4, "children" : [ "Eve" ] }
{ "_id" : 5, "children" : [ "Sammy" ] }
{ "_id" : 2, "children" : [ "Johny", "Sammy", "Mo" ] }
{ "_id" : 1, "children" : [ "Ronney", "Adam" ] }

现在,您希望使用该数据输出作为循环和更新相应的"父级"的基础。文档。考虑到实际数据可能并且可能会更大:

let ops = [];

db.parents.aggregate([
  { "$lookup": {
    "from": "links",
    "localField": "PID",
    "foreignField": "PID",
    "as": "children"
  }},
  { "$unwind": "$children" },
  { "$lookup": {
    "from": "children",
    "localField": "children.CID",
    "foreignField": "CID",
    "as": "children"
  }},
  { "$unwind": "$children" },
  { "$group": {
    "_id": "$_id",
    "children": { "$push": "$children.Cname" }
  }}
]).forEach(doc => {
  ops = [
    ...ops,
    {
      "updateOne": {
        "filter": { "_id": doc._id },
        "update": {
          "$push": { "Children": { "$each": doc.children } }
        }
      }
    }
  ];

  if ( ops.length >= 500 ) {
    db.parents.bulkWrite(ops);
    ops = [];
  }
});

if ( ops.length != 0 ) { 
  db.parents.bulkWrite(ops);
  ops = [];
}

然后将您的数据修改为:

{ "_id" : 1, "PID" : 1, "Pname" : "Joe", "Sal" : 20000, "Children" : [ "Ronney", "Adam" ] }
{ "_id" : 2, "PID" : 2, "Pname" : "Jim", "Sal" : 14100, "Children" : [ "Johny", "Sammy", "Mo" ] }
{ "_id" : 3, "PID" : 3, "Pname" : "Bob", "Sal" : 13500 }
{ "_id" : 4, "PID" : 4, "Pname" : "Amy", "Sal" : 12000, "Children" : [ "Eve" ] }
{ "_id" : 5, "PID" : 5, "Pname" : "George", "Sal" : 10000, "Children" : [ "Sammy" ] }

使用$push运算符的替代方法是使用$set代替一次写入整个数组。但通常需追加$push$addToSet来解释"可能性"一个数组可能已经存在于该位置,目的是"添加"而不是"覆盖。