将$ lookup结果合并到现有数组

时间:2018-11-01 19:57:42

标签: mongodb mongodb-query aggregation-framework

我是mongo新手,需要您的帮助。

我收藏了 studijneProgramy 。这是示例文档:

{
    "_id" : "dGFY",
    "garranti" : [
        {
            "typ" : {
                "sk" : "garant",
                "en" : "Chairman of study board"
            },
            "id" : "1025769"
        },
        {
            "typ" : {
                "sk" : "predseda odborovej komisie",
                "en" : "Chairman of study board"
            },
            "id" : "1025769"
        }
    ]
}

下一步,我收集了 osoby
示例文件:

{
    "_id" : "1025769",
    "plneMeno" : "prof. RNDr. Peter Moczo, DrSc.",
    "priezvisko" : "Moczo",
    "meno" : "Peter",
    "jeGarantProgramu" : "dGFY/x"
}

我需要的是将 osoby 中的文档添加到数组 garranti 中的相应文档中(其中studijneProgramy.garanti.id == osoby._id)。 所以这是我想要的结果:

{
    "_id" : "dGFY",
    "garranti" : [
        {
            "typ" : {
                "sk" : "garant",
                "en" : "Chairman of study board"
            },
            "id" : "1025769"
            "garant":{
                "_id" : "1025769",
                "plneMeno" : "prof. RNDr. Peter Moczo, DrSc.",
                "priezvisko" : "Moczo",
                "meno" : "Peter",
                "jeGarantProgramu" : "dGFY/x"
            }
        },
        {
            "typ" : {
                "sk" : "predseda odborovej komisie",
                "en" : "Chairman of study board"
            },
            "id" : "1025769"
            "garant":{
                "_id" : "1025769",
                "plneMeno" : "prof. RNDr. Peter Moczo, DrSc.",
                "priezvisko" : "Moczo",
                "meno" : "Peter",
                "jeGarantProgramu" : "dGFY/x"
            }
        }
    ]
}

我尝试了这种汇总,但是它替换了 garranti 的内容。

db.studijneProgramy.aggregate([
{
    $lookup:
    {
        from:"osoby", 
        localField:"garranti.id",
        foreignField:"_id", 
        as:"garranti.garant"
    }
 }
]
).pretty()

任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:2)

MongoDB $lookup不会使用“ lookup”集合中的匹配项来“更新”现有数组中的元素。它将输出与给定条件匹配的“数组”,即与您所拥有的值的“现有数组”或奇异值匹配的结果。

要通过“服务器” $lookup操作“合并”条目,您必须继续使用以下选项之一,以便以所需的形式返回。

$ unwind首先将数组

最简单的形式是简单地更改文档的结构,以使源中的每个数组成员首先是它的自己的文档,然后再实际尝试“关联”相关信息:< / p>

db.studijneProgramy.aggregate([
  { "$unwind": "$garranti" },
  { "$lookup": {
    "from": "osoby",
    "as": "garranti.garrant",
    "localField": "garranti.id",
    "foreignField": "_id"
  }},
  { "$unwind": "$garranti.garrant" },
  { "$group": {
    "_id": "$_id",
    "garranti": { "$push": "$garranti" }
  }}
])

由于原始数组材料现在是单个文档,因此每个仅从合并的集合中接收匹配项的“数组”。这将再次$unwind,最后使用$group,以便$push成为具有“ joined”条目的最终数组形式。

关联“数组”

在支持它的版本中,有些幻想是使用$indexOfArray$arrayElemAt的功能,以便将$lookup的输出数组“匹配”到其中的现有数组条目。文件:

db.studijneProgramy.aggregate([
  { "$lookup": {
    "from": "osoby",
    "as": "related",
    "localField": "garranti.id",
    "foreignField": "_id"
  }},
  { "$project": {
    "garranti": {
      "$map": {
        "input": "$garranti",
        "in": {
          "typ": "$$this.typ",
          "id": "$$this.id",
          "garrant": {
            "$arrayElemAt": [
              "$related",
              { "$indexOfArray": [ "$related._id", "$$this.id" ] }
            ]
          }
        }
      }
    }
  }}
])

因此,查找返回“匹配数组”(related),然后您“查找”这些匹配项,并通过$map将它们转置为原始文档数组。当然,这需要额外的$project阶段或类似步骤才能重塑文档结果,因为您不能像前面提到的那样“定位” $lookup输出中现有数组的每个元素。

这实际上是某些服务器(例如“猫鼬”)为“在客户端上进行联合仿真”所做的工作在“服务器”上的直接关联。实际上,“外来”条目被“映射”到现有阵列上。

子管道处理

使用“子管道”处理对MongoDB 3.6及更高版本的Uncorrelated subquery进行处理的另一种选择是有点幻想和漫不经心。在这里,我们基本上在$lookup的“子管道”中进行操作,而不是在随后的聚合阶段进行处理:

db.studijneProgramy.aggregate([
  { "$lookup": {
    "from": "osoby",
    "as": "garranti",
    "let": { "garranti": "$garranti" },
    "pipeline": [
      { "$match": {
        "$expr": { "$in": [ "$_id", "$$garranti.id" ] } 
      }},
      { "$addFields": {
        "docs": {
          "$filter": {
            "input": "$$garranti",
            "cond": {
              "$eq": [ "$$this.id", "$_id" ]
            }
          }
        }
      }},
      { "$unwind": "$docs" },
      { "$replaceRoot": {
        "newRoot": {
          "$mergeObjects": [
            "$docs",
            { "garrant": {
              "$arrayToObject": {
                "$filter": { 
                  "input": { "$objectToArray": "$$ROOT" },
                  "cond": { "$ne": [ "$$this.k", "docs"] }
                }
              }
            }}
          ]
        }
      }}
    ]
  }}
])

这种操作使操作“顺其自然”,并将“源文档”中的“匹配数组元素”有效地放置到每个匹配的外部元素数组中。

然后,该处理有效地在过滤的源列表上使用$unwind,然后合并来自外部集合的内容,因此现在看来$lookup“输出数组”实际上是来自“本地”的数据数组”现在与“外国内容”“合并”。

确实,这只是上面相同的$map流程的更高级的调用,但是在之前对条目进行“关联”,结果与原始父文档合并,从而覆盖了原始array属性


我认为某个地方可以使用JIRA,但是我感觉在所有此类报告中都标有“按设计工作” ,因此不太可能对其进行更改目前确实如此。

因此,您对“ join”的误解将“自动”与数组项“合并”。不会。

如果您想实际“合并数组输出”,那么上述方法就是“服务器”方法。

答案 1 :(得分:0)

除了dege指出的潜在重复项之外,如果数据量不大,则可以按以下方式使用JS。我相信使用聚合和$ lookup会更快,但这更干净。

db.studijneProgramy.find().forEach(stu=>{
    stu.garranti=stu.garranti.map(gar=>{
        gar.garant=db.osoby.find({_id:gar.id})[0];
        return gar;
    });
    db.studijneProgramy.save(stu);
});