代码之家  ›  专栏  ›  技术社区  ›  AlexZeDim

MongoDB aggregation$lookup to parent array field,管道语法,\u id作为字符串

  •  1
  • AlexZeDim  · 技术社区  · 3 年前

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

    guilds :

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

    characters :

    {
      _id: String,
      many_other: 'fields'
    }
    

    我想加入 $lookup 管道语法 guild.members <= characters _id 现场。

    OjbectIds _身份证 ,我用两个集合中的字符串覆盖它。正如我所听说的,我们的行为有些不同 以字符串作为 _身份证 在管道中。所以在回答之前,确保你知道。

    我想要的结果很简单,我想保存原始文档中的rank,并从中添加任何其他字段 $查找

    {
      _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 接线员。但我离要求的结果还很远。

    如你所见 人物 many other fields . 其中一些相当重(因为公会最多可以有500个成员),这使得结果文档>16 MB(MongoDB阈值限制),这会导致错误。因为,据我所知,我们不能从 joined 文档之后,最好立即将其排除在外 $查找 舞台之类的。任何关于这方面的建议都是非常受欢迎的。

    2 回复  |  直到 3 年前
        1
  •  2
  •   turivishal    3 年前

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

    • $lookup 带字符和密码 作为本地域
    • $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

        2
  •  0
  •   AlexZeDim    3 年前

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