$在父数组中查找对象数组并将结果附加到所述数组的每个项目
$lookup Array of Objects in parent Array and append the results to each item of said Array
考虑以下文档"Backpack",每个slots
是所述背包的一部分,每个插槽都有一个contents
描述各种物品和它们的数量。
{
_id: "backpack",
slots: [
{
slot: "left-pocket",
contents: [
{
item: "pen",
count: 3
},
{
item: "pencil",
count: 2
},
]
},
{
slot: "right-pocket",
contents: [
{
item: "bottle",
count: 1
},
{
item: "eraser",
count: 1
},
]
}
]
}
item
字段是另一个集合中某项的_id
,例如:
{
_id: "pen",
color: "red"
(...)
},
同样适用于钢笔、铅笔、瓶子、橡皮等
我想创建一个 $lookup
以便我可以填写项目的数据,但我没有找到使查找的 as
与项目位于同一位置的方法。即:
db.collection.aggregate({
{
$lookup: {
from: 'items',
localField: 'slots.contents.item',
foreignField: '_id',
as: 'convertedItems', // <=== ISSUE
},
},
})
问题是 as
被命名为 convertedItems
意味着文档在名为 'convertedItems' 的文档的根目录中获取项目数组,如下所示:
{
_id: "backpack",
slots: [ (...) ],
convertedItems: [ (...) ]
}
如何告诉 $lookup
实际使用 localField
作为附加数据的位置?
即使文档变为:
{
_id: "backpack",
slots: [
{
slot: "left-pocket",
contents: [
{
item: "pen", // <== NOTE
count: 3, // <== NOTE
_id: "pen",
color: "red"
(...)
},
{
item: "pencil", // <== NOTE
count: 2, // <== NOTE
_id: "pencil",
color: "blue"
(...)
},
]
},
(...)
注:此时,如果有item的全部数据,item
属性保留无所谓,但count
必须保留。
我无法用 arrayElemAt
手动执行 $addFields
,因为 slots
中的项目数不固定。
额外信息:我使用的是 MongoAtlas Free,所以假设 MongoDB 4.2+(无需为 $lookup 展开数组)。
PS:我现在想到的只是作为根项目离开(例如 "convertedItems")并在接收 API 的代码上循环项目时,我做 Array.find 在 "convertedItems" 上根据使用该项目的 _id。 我会保留这个问题,因为我很好奇如何在 MongoDB 方面
当您使用 $lookup 时,源管道中每个文档的相关集合中只有一个查询,而不是源文档中每个值的查询。
如果您希望单独查找每个项目,则需要展开数组,以便管道中的每个文档都包含一个项目,进行查找,然后分组以重建数组。
db.collection.aggregate([
{$unwind: "$slots"},
{$unwind: "$slots.contents"},
{$lookup: {
from: "items",
localField: "slots.contents.item",
foreignField: "_id",
as: "convertedItems"
}},
{$group: {
_id: "$slots.slot",
root: {$first: "$$ROOT"},
items: {
$push: {
$mergeObjects: [
"$slots.contents",
{$arrayElemAt: ["$convertedItems", 0]}
]
}},
}},
{$addFields: {"root.slots.contents": "$items"}},
{$replaceRoot: {newRoot: "$root"}},
{$group: {
_id: "$_id",
root: {$first: "$$ROOT"},
slots: {$push: "$slots"}
}},
{$addFields: {"root.slots": "$slots"}},
{$replaceRoot: {newRoot: "$root"}},
{$project: { convertedItems: 0}}
])
unwind makes your collection explode, Also you can't specify in place of
'as', So you need to add additional stages like addFields, filters to
get required o/p
正如我所评论的那样,为了将主文档的元素与 $lookup
结果相匹配,您的要求需要做一些工作,也许这可以通过代码轻松完成,但如果必须通过查询,使用这个查询,你将处理与你在集合中的相同的 no.of 文档,而不是放松,因为当你拥有像你现在这样的嵌套数组时,它会爆炸你的文档,一般来说有点复杂如果需要更好的性能,请尝试使用 $match
作为第一阶段来过滤文档。此外,您可以使用 $explain
来了解您的查询性能。
查询:
db.Backpack.aggregate([
/** lookup on items collection & get matched docs to items array */
{
$lookup: {
from: "items",
localField: "slots.contents.item",
foreignField: "_id",
as: "items"
}
},
/** Iterate on slots & contents & internally filter on items array to get matched doc for a content object &
* merge the objects back to respective objects to form the same structure */
{
$project: {
slots: {
$map: {
input: "$slots",
in: {
$mergeObjects: [
"$$this",
{
contents: {
$map: {
input: "$$this.contents",
as: "c",
in: {
$mergeObjects: [
"$$c",
{
$let: {
vars: {
matchedItem: {
$arrayElemAt: [
{
$filter: {
input: "$items",
as: "i",
cond: {
$eq: [
"$$c.item",
"$$i._id"
]
}
}
},
0
]
}
},
in: {
color: "$$matchedItem.color"
}
}
}
]
}
}
}
}
]
}
}
}
}
}
])
考虑以下文档"Backpack",每个slots
是所述背包的一部分,每个插槽都有一个contents
描述各种物品和它们的数量。
{
_id: "backpack",
slots: [
{
slot: "left-pocket",
contents: [
{
item: "pen",
count: 3
},
{
item: "pencil",
count: 2
},
]
},
{
slot: "right-pocket",
contents: [
{
item: "bottle",
count: 1
},
{
item: "eraser",
count: 1
},
]
}
]
}
item
字段是另一个集合中某项的_id
,例如:
{
_id: "pen",
color: "red"
(...)
},
同样适用于钢笔、铅笔、瓶子、橡皮等
我想创建一个 $lookup
以便我可以填写项目的数据,但我没有找到使查找的 as
与项目位于同一位置的方法。即:
db.collection.aggregate({
{
$lookup: {
from: 'items',
localField: 'slots.contents.item',
foreignField: '_id',
as: 'convertedItems', // <=== ISSUE
},
},
})
问题是 as
被命名为 convertedItems
意味着文档在名为 'convertedItems' 的文档的根目录中获取项目数组,如下所示:
{
_id: "backpack",
slots: [ (...) ],
convertedItems: [ (...) ]
}
如何告诉 $lookup
实际使用 localField
作为附加数据的位置?
即使文档变为:
{
_id: "backpack",
slots: [
{
slot: "left-pocket",
contents: [
{
item: "pen", // <== NOTE
count: 3, // <== NOTE
_id: "pen",
color: "red"
(...)
},
{
item: "pencil", // <== NOTE
count: 2, // <== NOTE
_id: "pencil",
color: "blue"
(...)
},
]
},
(...)
注:此时,如果有item的全部数据,item
属性保留无所谓,但count
必须保留。
我无法用 arrayElemAt
手动执行 $addFields
,因为 slots
中的项目数不固定。
额外信息:我使用的是 MongoAtlas Free,所以假设 MongoDB 4.2+(无需为 $lookup 展开数组)。
PS:我现在想到的只是作为根项目离开(例如 "convertedItems")并在接收 API 的代码上循环项目时,我做 Array.find 在 "convertedItems" 上根据使用该项目的 _id。 我会保留这个问题,因为我很好奇如何在 MongoDB 方面
当您使用 $lookup 时,源管道中每个文档的相关集合中只有一个查询,而不是源文档中每个值的查询。
如果您希望单独查找每个项目,则需要展开数组,以便管道中的每个文档都包含一个项目,进行查找,然后分组以重建数组。
db.collection.aggregate([
{$unwind: "$slots"},
{$unwind: "$slots.contents"},
{$lookup: {
from: "items",
localField: "slots.contents.item",
foreignField: "_id",
as: "convertedItems"
}},
{$group: {
_id: "$slots.slot",
root: {$first: "$$ROOT"},
items: {
$push: {
$mergeObjects: [
"$slots.contents",
{$arrayElemAt: ["$convertedItems", 0]}
]
}},
}},
{$addFields: {"root.slots.contents": "$items"}},
{$replaceRoot: {newRoot: "$root"}},
{$group: {
_id: "$_id",
root: {$first: "$$ROOT"},
slots: {$push: "$slots"}
}},
{$addFields: {"root.slots": "$slots"}},
{$replaceRoot: {newRoot: "$root"}},
{$project: { convertedItems: 0}}
])
unwind makes your collection explode, Also you can't specify in place of 'as', So you need to add additional stages like addFields, filters to get required o/p
正如我所评论的那样,为了将主文档的元素与 $lookup
结果相匹配,您的要求需要做一些工作,也许这可以通过代码轻松完成,但如果必须通过查询,使用这个查询,你将处理与你在集合中的相同的 no.of 文档,而不是放松,因为当你拥有像你现在这样的嵌套数组时,它会爆炸你的文档,一般来说有点复杂如果需要更好的性能,请尝试使用 $match
作为第一阶段来过滤文档。此外,您可以使用 $explain
来了解您的查询性能。
查询:
db.Backpack.aggregate([
/** lookup on items collection & get matched docs to items array */
{
$lookup: {
from: "items",
localField: "slots.contents.item",
foreignField: "_id",
as: "items"
}
},
/** Iterate on slots & contents & internally filter on items array to get matched doc for a content object &
* merge the objects back to respective objects to form the same structure */
{
$project: {
slots: {
$map: {
input: "$slots",
in: {
$mergeObjects: [
"$$this",
{
contents: {
$map: {
input: "$$this.contents",
as: "c",
in: {
$mergeObjects: [
"$$c",
{
$let: {
vars: {
matchedItem: {
$arrayElemAt: [
{
$filter: {
input: "$items",
as: "i",
cond: {
$eq: [
"$$c.item",
"$$i._id"
]
}
}
},
0
]
}
},
in: {
color: "$$matchedItem.color"
}
}
}
]
}
}
}
}
]
}
}
}
}
}
])