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

Luk*_*nka 6 mongodb mongodb-query aggregation-framework

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

我有收藏studyProgramy。这是示例文档:

{
    "_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"
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

接下来我收集osoby
示例文档:

{
    "_id" : "1025769",
    "plneMeno" : "prof. RNDr. Peter Moczo, DrSc.",
    "priezvisko" : "Moczo",
    "meno" : "Peter",
    "jeGarantProgramu" : "dGFY/x"
}
Run Code Online (Sandbox Code Playgroud)

我需要的是从增加documets 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"
            }
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

我尝试了这种聚合,但它取代了garranti 的内容。

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

任何帮助将不胜感激!

Nei*_*unn 18

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

为了将条目与“服务器”$lookup操作“合并”,您必须继续使用以下选项之一,以便以您想要的形式返回。

$unwind 首先数组

最简单的形式是简单地更改文档的结构,以便在您实际尝试“结合”相关信息之前,源中的每个数组成员首先是它自己的文档

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" }
  }}
])
Run Code Online (Sandbox Code Playgroud)

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

关联“数组”

在支持它是要使用的功能的版本的位爱好者$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" ] }
            ]
          }
        }
      }
    }
  }}
])
Run Code Online (Sandbox Code Playgroud)

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

这实际上是某些库(例如“mongoose”)为“在客户端上加入仿真”所做的“服务器”上的直接关联。实际上,“外部”条目被“映射”到现有数组上。

子流水线处理

使用MongoDB 3.6 及更高版本提供的Uncorrelated 子查询的“子管道”处理的另一种选择,有点花哨和冗长。这里我们基本上是在“子管道”中进行操作,$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"] }
                }
              }
            }}
          ]
        }
      }}
    ]
  }}
])
Run Code Online (Sandbox Code Playgroud)

这种将操作“放在头上”并有效地将“源文档”中的“匹配数组元素”作为数组放入每个匹配的外部元素中。

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

实际上,这只是对上述相同$map过程的更高级调用,但是在结果与覆盖原始数组属性的原始父文档合并之前对条目进行“关联” 。


我认为在某处有一个 JIRA,但我有一种感觉,“按设计工作”已标记在所有此类报告中,因此它不太可能从目前的情况改变。

所以你的误解是“加入”会“自动”与数组条目“合并”。它不是。

如果你想真正“合并数组输出”,那么上面的方法就是“服务器”方法。