如何在MongoDB中按数组索引更新指定元素并复制另一个字段的值
如何在MongoDB中按数组索引更新指定元素并复制另一个字段的值
我懂你现在的困扰了——你想把每个文档里的rootName值,只添加到outerItem.innerItems数组索引为1的元素里,而不是给所有数组元素都加上newField。之前用$map的时候没加条件判断,结果所有元素都带上了新字段,后来尝试加条件又写错了语法,对吧?
正确的解决方案
核心思路是:在$map遍历数组时,给每个元素绑定对应的索引,然后通过条件判断只给索引为1的元素合并新字段。这里用$zip把原数组和索引数组配对,能可靠地获取每个元素的位置,避免重复元素导致的索引判断错误。
MongoDB Shell 代码
db.collection.update({}, [ { $set: { "outerItem.innerItems": { $map: { // 将原数组和索引数组配对,每个元素格式为 [原数组元素, 索引值] input: { $zip: { inputs: [ "$outerItem.innerItems", { $range: [0, { $size: "$outerItem.innerItems" }] } ] } }, in: { $cond: [ // 判断当前元素的索引是否为1 { $eq: ["$$this.1", 1] }, // 满足条件:合并原元素和新字段 { $mergeObjects: ["$$this.0", { newField: "$rootName" }] }, // 不满足条件:直接返回原元素 "$$this.0" ] } } } } } ], { multi: true, upsert: false })
Node.js TypeScript 代码
对应到你的TS代码里,可以这样写:
await queryRunner.updateMany('name_items', {}, [ { $set: { "outerItem.innerItems": { $map: { input: { $zip: { inputs: [ "$outerItem.innerItems", { $range: [0, { $size: "$outerItem.innerItems" }] } ] } }, in: { $cond: [ { $eq: ["$$this.1", 1] }, { $mergeObjects: ["$$this.0", { newField: "$rootName" }] }, "$$this.0" ] } } } } } ]);
为什么之前的代码出错?
- 第一次的代码:没有给
$map加条件判断,所以遍历数组时给每个元素都合并了newField,导致所有元素都带上了这个字段。 - 第二次的代码:
$cond的语法错误——$cond需要三个参数(条件、满足时的结果、不满足时的结果),你漏掉了不满足的分支;而且$$item.id是无效的,因为数组元素本身没有id字段,你需要的是数组的索引而不是元素的属性。
额外的健壮性优化
如果你的文档中可能存在innerItems数组长度小于2的情况(也就是索引1不存在),可以加个判断避免出错:
db.collection.update({}, [ { $set: { "outerItem.innerItems": { $cond: [ // 先判断数组长度是否至少为2 { $gte: [{ $size: "$outerItem.innerItems" }, 2] }, // 满足长度要求时执行更新逻辑 { $map: { input: { $zip: { inputs: ["$outerItem.innerItems", { $range: [0, { $size: "$outerItem.innerItems" }] }] } }, in: { $cond: [ { $eq: ["$$this.1", 1] }, { $mergeObjects: ["$$this.0", { newField: "$rootName" }] }, "$$this.0" ] } } }, // 不满足长度要求时保持原数组不变 "$outerItem.innerItems" ] } } } ], { multi: true })
内容来源于stack exchange
相关产品推荐
相关产品推荐

