【问题标题】:MongoDB aggregation $lookup to parent array field with pipeline syntax and _id as stringMongoDB 聚合 $lookup 到具有管道语法和 _id 作为字符串的父数组字段
【发布时间】:2021-08-05 05:25:44
【问题描述】:

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

guilds:

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

characters:

{
  _id: String,
  many_other: 'fields'
}

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

我不使用 Mongo 内置 OjbectIds 作为 _id,我用两个集合中的字符串覆盖它。正如我所听到的,$lookup 与管道中的字符串为_id 有一些不同的行为。所以在回答之前,请确保你知道它。

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

我尝试了什么:

各种查询,例如:

      {
        $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 阶段或类似的东西之后立即排除它。任何关于它的建议都会非常受欢迎。

【问题讨论】:

    标签: javascript mongodb mongoose aggregation-framework


    【解决方案1】:

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

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

    【讨论】:

    • 查询没问题,我也有一个,问题有点大。在 $lookup 阶段之前或之后,要从加入的文档 (characters) 中删除/排除某些字段是什么?例如 (characters.pets)。在这种情况下,我可以在$project 阶段,在$map 中简单地删除它们。我说的对吗?
    • 可以,但最好在添加额外的项目阶段删除,请参阅playground
    【解决方案2】:

    我接受@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"
        },
        
      }
    ])
    

    【讨论】:

      猜你喜欢
      • 2018-08-06
      • 1970-01-01
      • 1970-01-01
      • 2020-08-14
      • 2016-08-16
      • 2018-05-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多