MongoDB $lookup 和 $map 对象数组

12

我已经尝试了好几天,但是没有成功

我正在使用MongoDB,尝试了很多管道步骤,但是找不到方法。

我有一个玩家集合,每个玩家包含一个items数组。

{
    "_id": ObjectId("5fba17c1c4566e57fafdcd7e"),
    "username": "moshe",
    "items": [
        {
            "_id": ObjectId("5fbb5ac178045a985690b5fd"),
            "equipped": false,
            "itemId": "5fbb5ab778045a985690b5fc"
        }
    ]
}

我有一个物品集合,其中关于每个物品在玩家的items数组中有更多信息。

{
    "_id": ObjectId("5fbb5ab778045a985690b5fc"),
    "name": "Axe",
    "damage": 4,
    "defense": 6
}

我的目标是拥有一个播放器文档,其中包含关于他的items数组内项目的所有信息,因此它将如下所示:

{
    "_id": ObjectId("5fba17c1c4566e57fafdcd7e"),
    "username": "moshe",
    "items": [
        {
            "_id": ObjectId("5fbb5ac178045a985690b5fd"),
            "equipped": false,
            "itemId": "5fbb5ab778045a985690b5fc",
            "name": "Axe",
            "damage": 4,
            "defense": 6
        }
    ]
}
2个回答

15
  • $unwinditems 数组展开
  • $lookup 连接 items 集合,将 itemsId 转换为对象 id,并传递到 let 中,然后传递 items 对象,
    • $match 匹配 itemId 条件
    • $mergeObject 合并 items 对象和 $$ROOT 对象,并使用 $replaceRoot 替换为根对象
  • $group 重新构建 items 数组,按 _id 分组,并获取第一个 username 并构建 items 数组
db.players.aggregate([
  { $unwind: "$items" },
  {
    $lookup: {
      from: "items",
      let: {
        itemId: { $toObjectId: "$items.itemId" },
        items: "$items"
      },
      pipeline: [
        { $match: { $expr: { $eq: ["$_id", "$$itemId" ] } } },
        { $replaceRoot: { newRoot: { $mergeObjects: ["$$items", "$$ROOT"] } } }
      ],
      as: "items"
    }
  },
  {
    $group: {
      _id: "$_id",
      username: { $first: "$username" },
      items: { $push: { $first: "$items" } }
    }
  }
])

Playground


使用$map的第二种选择,无需使用$unwind,

  • $addFields用于将items中的itemId字符串转换为对象类型id,使用$toObjectId$map
  • $lookup连接items集合
  • $project显示所需字段,并使用$map合并items数组和itemsCollection,以迭代项目数组的循环$filter以获取匹配的itemId,并使用$first从返回结果中获取第一个对象,$mergeObject将当前对象与返回的对象合并自身
db.players.aggregate([
  {
    $addFields: {
      items: {
        $map: {
          input: "$items",
          in: {
            $mergeObjects: ["$$this", { itemId: { $toObjectId: "$$this.itemId" } }]
          }
        }
      }
    }
  },
  {
    $lookup: {
      from: "items",
      localField: "items.itemId",
      foreignField: "_id",
      as: "itemsCollection"
    }
  },
  {
    $project: {
      username: 1,
      items: {
        $map: {
          input: "$items",
          as: "i",
          in: {
            $mergeObjects: [
              "$$i",
              {
                $first: {
                  $filter: {
                    input: "$itemsCollection",
                    cond: { $eq: ["$$this._id", "$$i.itemId"] }
                  }
                }
              }
            ]
          }
        }
      }
    }
  }
])

游乐场


7

首先,我强烈建议您将items.itemId存储为ObjectId,而不是字符串。

然后,另一个简单的解决方案可以是:

db.players.aggregate([
  {
    $lookup: {
      from: "items",
      localField: "items.itemId",
      foreignField: "_id",
      as: "itemsDocuments",
    },
  },
  {
    $addFields: {
      items: {
        $map: {
          input: { $zip: { inputs: ["$items", "$itemsDocuments"] } },
          in: { $mergeObjects: "$$this" },
        },
      },
    },
  },
  { $unset: "itemsDocuments" },
])

错误/不完整,因为itemsDocuments是按照items集合中文档的自然顺序排序的,而不是本地items数组的顺序。请参见https://jira.mongodb.org/browse/SERVER-32947。 - Raman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接