MongoDB $lookup 和 $map 对象数组
MongoDB $lookup and $map array of objects
我试了好几天了,但没成功
我正在使用 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
}
]
}
$unwind
解构items
数组
$lookup
加入items
集合,使用$toObjectId
将其转换为对象id后将itemsId
传递给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" } }
}
}
])
第二个选项使用 $map
,不使用 $unwind
,
$addFields
for items
convert itemId
string to object type id using $toObjectId
and $map
$lookup
加入items
合集
$project
显示必填字段,合并 items
数组和 itemsCollection
使用 $map
迭代项目数组 $filter
的循环以获得匹配 itemId
和 $first
从 return 结果中获取第一个对象,$mergeObject
合并当前对象和 return 从 $first
编辑的对象
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"] }
}
}
}
]
}
}
}
}
}
])
首先,我强烈建议您将 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" },
])
我试了好几天了,但没成功
我正在使用 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
}
]
}
$unwind
解构items
数组$lookup
加入items
集合,使用$toObjectId
将其转换为对象id后将itemsId
传递给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" } }
}
}
])
第二个选项使用 $map
,不使用 $unwind
,
$addFields
foritems
convertitemId
string to object type id using$toObjectId
and$map
$lookup
加入items
合集$project
显示必填字段,合并items
数组和itemsCollection
使用$map
迭代项目数组$filter
的循环以获得匹配itemId
和$first
从 return 结果中获取第一个对象,$mergeObject
合并当前对象和 return 从$first
编辑的对象
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"] }
}
}
}
]
}
}
}
}
}
])
首先,我强烈建议您将 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" },
])