简体   繁体   English

MongoDB 聚合 $lookup 使用管道语法和 _id 作为字符串的父数组字段

[英]MongoDB aggregation $lookup to parent array field with pipeline syntax and _id as string

I have the following collections in my MongoDB (v.4.4):我的 MongoDB (v.4.4) 中有以下 collections:

guilds : guilds

{
 _id: String,
 members: [{ _id String, rank: number },{ _id String, rank: number }...]
}

and characters :characters

{
  _id: String,
  many_other: 'fields'
}

And I'd like to join with the $lookup pipeline syntax guild.members <= characters by _id field.我想加入$lookup管道语法guild.members <= characters by _id字段。

I don't use Mongo build-in OjbectIds as _id , I overwrite it with strings in both collections.我不使用 Mongo 内置OjbectIds作为_id ,我用 collections 中的字符串覆盖它。 As I heard, there is a bit different behaviour with $lookup with strings as _id in pipelines.正如我所听到的,在管道中使用字符串作为_id$lookup有一些不同的行为。 So make sure, that you knew about it, before answering.因此,在回答之前,请确保您对此有所了解。

The expected result that I want is simple, I'd like to save rank from the original document, and also add any other field from $lookup it:我想要的预期结果很简单,我想从原始文档中保存排名,并从$lookup添加任何其他字段:

{
  _id: String // (guild)
  members: [
    {
      _id: String, // (characters)
      rank: number,
      many_other: '...fields from characters'
    },
    {
      _id: String, // (characters)
      rank: number,
      many_other: '...fields from characters'
    }, ...
]

Mongo Playground example: avaliable Mongo Playground 示例:可用

What have I tried:我尝试了什么:

Various queries, like:各种查询,例如:

      {
        $lookup: {
          from: "characters",
          pipeline: [
            {
              $match: {
                $expr: { $eq: [ { $toString :"$members._id" }, { $toString : "$$character_id" } ] }
              }
            }
          ],
          as: "guild_members"
        },
      }

With converting ID's to string, with let stages variables, and using $map operator.将 ID 转换为字符串,使用let阶段变量,并使用$map运算符。 But I still far away from the required result.但我离要求的结果还很远。

There is also another problem, which is some relevance to the question, but not related to queries itself还有一个问题,和问题有些相关,但和查询本身无关

As you may see, the characters collection has many other fields .如您所见, characters集合还有many other fields Some of them are pretty heavy, (because guilds can have up to 500 members) which making the result document >16 MB (MongoDB threshold limit) which gives an error.其中一些非常重,(因为公会最多可以有 500 个成员)这使得结果文档 >16 MB(MongoDB 阈值限制),这会产生错误。 Since, as far as I know, we could not pick fields from joined documents, it will be much better to exclude it right after the $lookup stage or something like that.因为,据我所知,我们无法从joined的文档中选择字段,所以最好在$lookup阶段或类似的阶段之后立即排除它。 Any advice about it will be pretty welcome.任何关于它的建议都将非常受欢迎。

Lookup with pipeline is not required, when you pass members._id as localField,当您将members._id作为 localField 传递时,不需要使用管道查找,

  • $lookup with characters and pass members._id as localField $lookup与字符和传递members._id作为 localField
  • $map to iterate loop of members array $map迭代members数组的循环
  • $filter to iterate loop of members_guid and get matching member $filter迭代members_guid的循环并获取匹配的成员
  • $arrayElemAt to get first matching element from above filter $arrayElemAt从上面的过滤器中获取第一个匹配元素
  • $mergeObjects to merge current fields with above filtered object of member $mergeObjects将当前字段与上面过滤的成员 object 合并
db.guilds.aggregate([
  {
    $lookup: {
      from: "characters",
      localField: "members._id",
      foreignField: "_id",
      as: "members_guid"
    }
  },
  {
    $project: {
      members: {
        $map: {
          input: "$members",
          as: "m",
          in: {
            $mergeObjects: [
              "$$m",
              {
                $arrayElemAt: [
                  {
                    $filter: {
                      input: "$members_guid",
                      cond: { $eq: ["$$this._id", "$$m._id"] }
                    }
                  },
                  0
                ]
              }
            ]
          }
        }
      }
    }
  }
])

Playground操场

I take the @turivishal answer since the old-style-native aggregation is much easier to understand than pipeline syntax.我接受@turivishal 的回答,因为旧式原生聚合比pipeline语法更容易理解。 But if someone is interested, my old part of aggregation, which I used a while ago.但如果有人感兴趣,我之前使用过的旧聚合部分。

db.guilds.aggregate([
  {
    $lookup: {
      from: "characters",
      let: {
        members: "$members"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $in: [
                "$_id",
                "$$members._id"
              ]
            }
          }
        },
        {
          $addFields: {
            rank: {
              $reduce: {
                input: "$$members",
                initialValue: null,
                in: {
                  $cond: [
                    {
                      $eq: [
                        "$$this._id",
                        "$_id"
                      ]
                    },
                    "$$this.rank",
                    "$$value"
                  ]
                }
              }
            }
          }
        }
      ],
      as: "members"
    },
    
  }
])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM