繁体   English   中英

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

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

我的 MongoDB (v.4.4) 中有以下 collections:

guilds

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

characters

{
  _id: String,
  many_other: 'fields'
}

我想加入$lookup管道语法guild.members <= characters by _id字段。

我不使用 Mongo 内置OjbectIds作为_id ,我用 collections 中的字符串覆盖它。 正如我所听到的,在管道中使用字符串作为_id$lookup有一些不同的行为。 因此,在回答之前,请确保您对此有所了解。

我想要的预期结果很简单,我想从原始文档中保存排名,并从$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 示例:可用

我尝试了什么:

各种查询,例如:

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

将 ID 转换为字符串,使用let阶段变量,并使用$map运算符。 但我离要求的结果还很远。

还有一个问题,和问题有些相关,但和查询本身无关

如您所见, characters集合还有many other fields 其中一些非常重,(因为公会最多可以有 500 个成员)这使得结果文档 >16 MB(MongoDB 阈值限制),这会产生错误。 因为,据我所知,我们无法从joined的文档中选择字段,所以最好在$lookup阶段或类似的阶段之后立即排除它。 任何关于它的建议都将非常受欢迎。

当您将members._id作为 localField 传递时,不需要使用管道查找,

  • $lookup与字符和传递members._id作为 localField
  • $map迭代members数组的循环
  • $filter迭代members_guid的循环并获取匹配的成员
  • $arrayElemAt从上面的过滤器中获取第一个匹配元素
  • $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
                ]
              }
            ]
          }
        }
      }
    }
  }
])

操场

我接受@turivishal 的回答,因为旧式原生聚合比pipeline语法更容易理解。 但如果有人感兴趣,我之前使用过的旧聚合部分。

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