Mongodb 更新管道更新嵌套数组中元素的字段
Mongodb update pipeline update a field of a element inside a nested array
我有一个包含此类对象的集合。
{
materials: {
"m1": {
inventory: [
{
price: 100,
amount: 65
}
]
}
}
}
如您所见,库存是层次结构深处的一个数组。我想更新它的 amount
字段。
我从客户端收到 material ID ("m1"
) 和库存索引 (0
)。
我必须使用更新管道,因为我正在设置和取消设置此文档中的一些其他字段。
这是我试过的:
await products.findOneAndUpdate(filters, [
{
$set: {
"materials.m1.inventory.0.amount": 100,
},
},
]);
但它在第 0 个元素内创建了一个名为 0 的新字段,并在该对象内设置了 amount
。所以生成的文档看起来像这样。
{
materials: {
"m1": {
inventory: [
{
0: {
amount: 100
}
price: 100,
amount: 65
}
]
}
}
}
而我想要的是:
{
materials: {
"m1": {
inventory: [
{
price: 100,
amount: 100
}
]
}
}
}
我确定要更新数组中哪个元素的唯一方法是它的索引。
我正在使用 nodejs mongodb 驱动程序。如何为此更新管道编写 $set
阶段?
我认为没有其他直接使用聚合管道更新特定索引位置元素的方法,
$range
生成从 0 到 materials.m1.inventory
array 长度的数组
$map
迭代上述范围数组的循环
$cond
检查当前元素是否为 0 然后转到更新部分,否则 return 来自 materials.m1.inventory
的元素,输入索引
$arrayElemAt
从 materials.m1.inventory
获取特定元素
$mergeObjects
将当前对象与更新后的 amount
合并 属性
await products.findOneAndUpdate(filters, [
{
$set: {
"materials.m1.inventory": {
$map: {
input: {
$range: [0, { $size: "$materials.m1.inventory" }]
},
in: {
$cond: [
{ $eq: ["$$this", 0] }, // 0 position
{
$mergeObjects: [
{ $arrayElemAt: ["$materials.m1.inventory", "$$this"] },
{ amount: 100 } // input amount
]
},
{ $arrayElemAt: ["$materials.m1.inventory", "$$this"] }
]
}
}
}
}
}
]);
findOneAndUpdate
使用聚合管道的问题是您只能使用 $set
、$unset
和 $replaceWith
(及其别名)。似乎 $set
或 $addFields
无法覆盖数组内的对象,因此您可以简单地根据需要的更改重新创建整个数组。您基本上被迫像这样构建查询:
db.collection.update(filters,
[
{
"$addFields": {
"materials.m1.inventory": {
...
}
}
}
])
这是我的实现,但可以有更直接的方法(参见替换单对象数组的编辑):
db.collection.update(filters,
[
{
"$addFields": {
"materials.m1.inventory": {
$concatArrays: [
[
{
price: {
$getField: {
field: "price",
input: {
$first: "$materials.m1.inventory"
}
}
},
amount: 100
}
],
{
$slice: [
"$materials.m1.inventory",
1,
{
$size: "$materials.m1.inventory"
}
]
}
]
}
}
}
])
我正在使用 $concatArrays
来 concat:
- 具有单个对象的数组,该对象位于索引 0 但
amount
字段设置为 100
inventory
数组的切片,从索引 1 开始。
显然,如果索引不为 0,则必须采用第一个切片 inventory[0:index]
、单个对象数组(参见第 1 点)和切片 inventory[index + 1:]
。
Here 演示。
编辑:
@turivishal 提出的 $mergeObjects
部分绝对比我的单对象数组好:无论你有 2 个还是 20 个字段,代码都是一样的。
我有一个包含此类对象的集合。
{
materials: {
"m1": {
inventory: [
{
price: 100,
amount: 65
}
]
}
}
}
如您所见,库存是层次结构深处的一个数组。我想更新它的 amount
字段。
我从客户端收到 material ID ("m1"
) 和库存索引 (0
)。
我必须使用更新管道,因为我正在设置和取消设置此文档中的一些其他字段。
这是我试过的:
await products.findOneAndUpdate(filters, [
{
$set: {
"materials.m1.inventory.0.amount": 100,
},
},
]);
但它在第 0 个元素内创建了一个名为 0 的新字段,并在该对象内设置了 amount
。所以生成的文档看起来像这样。
{
materials: {
"m1": {
inventory: [
{
0: {
amount: 100
}
price: 100,
amount: 65
}
]
}
}
}
而我想要的是:
{
materials: {
"m1": {
inventory: [
{
price: 100,
amount: 100
}
]
}
}
}
我确定要更新数组中哪个元素的唯一方法是它的索引。
我正在使用 nodejs mongodb 驱动程序。如何为此更新管道编写 $set
阶段?
我认为没有其他直接使用聚合管道更新特定索引位置元素的方法,
$range
生成从 0 到materials.m1.inventory
array 长度的数组
$map
迭代上述范围数组的循环$cond
检查当前元素是否为 0 然后转到更新部分,否则 return 来自materials.m1.inventory
的元素,输入索引$arrayElemAt
从materials.m1.inventory
获取特定元素
$mergeObjects
将当前对象与更新后的amount
合并 属性
await products.findOneAndUpdate(filters, [
{
$set: {
"materials.m1.inventory": {
$map: {
input: {
$range: [0, { $size: "$materials.m1.inventory" }]
},
in: {
$cond: [
{ $eq: ["$$this", 0] }, // 0 position
{
$mergeObjects: [
{ $arrayElemAt: ["$materials.m1.inventory", "$$this"] },
{ amount: 100 } // input amount
]
},
{ $arrayElemAt: ["$materials.m1.inventory", "$$this"] }
]
}
}
}
}
}
]);
findOneAndUpdate
使用聚合管道的问题是您只能使用 $set
、$unset
和 $replaceWith
(及其别名)。似乎 $set
或 $addFields
无法覆盖数组内的对象,因此您可以简单地根据需要的更改重新创建整个数组。您基本上被迫像这样构建查询:
db.collection.update(filters,
[
{
"$addFields": {
"materials.m1.inventory": {
...
}
}
}
])
这是我的实现,但可以有更直接的方法(参见替换单对象数组的编辑):
db.collection.update(filters,
[
{
"$addFields": {
"materials.m1.inventory": {
$concatArrays: [
[
{
price: {
$getField: {
field: "price",
input: {
$first: "$materials.m1.inventory"
}
}
},
amount: 100
}
],
{
$slice: [
"$materials.m1.inventory",
1,
{
$size: "$materials.m1.inventory"
}
]
}
]
}
}
}
])
我正在使用 $concatArrays
来 concat:
- 具有单个对象的数组,该对象位于索引 0 但
amount
字段设置为 100 inventory
数组的切片,从索引 1 开始。
显然,如果索引不为 0,则必须采用第一个切片 inventory[0:index]
、单个对象数组(参见第 1 点)和切片 inventory[index + 1:]
。
Here 演示。
编辑:
@turivishal 提出的 $mergeObjects
部分绝对比我的单对象数组好:无论你有 2 个还是 20 个字段,代码都是一样的。