更新内嵌文档与数组元素
本教程共 50 篇 · 第 25 篇 · 更新于 2026-07-30 · 约 8 分钟阅读
25. 更新内嵌文档与数组元素
本节目标:学会定位并修改数组里的某个元素、内嵌文档的某个子字段,掌握位置运算符
$、$[]和带arrayFilters的$[elem]。
前面章节的更新都是「整篇文档层面」的。可真实数据里,数组元素(如订单里的商品行)常常需要单独改某一个。这章专门解决「数组里的第几个、满足条件的那几个」怎么精准下手。
db.orders.insertOne({
_id: 1001,
user_id: 1,
status: "pending",
items: [
{ product: "机械键盘", qty: 1, price: 399 },
{ product: "无线鼠标", qty: 2, price: 129 },
{ product: "USB-C hub", qty: 3, price: 199 }
]
})
用数组下标定点改
最直白的办法:用 "数组.下标.子字段" 直接定位。下标从 0 开始:
// 把订单里第 1 个商品(机械键盘)的数量改成 5
db.orders.updateOne(
{ _id: 1001 },
{ $set: { "items.0.qty": 5 } }
)
这个写法简单,但前提是「你知道要改的是第几个」。通常你并不知道下标,那就得用下面的运算符。
位置运算符 $:改第一个匹配的元素
筛选条件里如果匹配到了数组的某个元素,$ 就代表「那个元素的位置」。更新时用 "items.$.qty" 就能改中它:
// 把订单里「机械键盘」这一行的数量改成 3
db.orders.updateOne(
{ _id: 1001, "items.product": "机械键盘" },
{ $set: { "items.$.qty": 3 } }
)
Note
$只代表第一个匹配到的数组元素。如果有多行都是机械键盘,只有第一行被改。
全位置运算符 $[]:改数组全部元素
想把数组里每一个元素都改一遍,用 $[](注意中括号):
// 订单里所有商品数量统统 +1
db.orders.updateOne(
{ _id: 1001 },
{ $inc: { "items.$[].qty": 1 } }
)
带条件的过滤位置 $[elem] + arrayFilters
$ 只能改第一个,$[] 会改全部。如果想「只改满足条件的那些」,就得用 $[elem](elem 是自定义标识符)配合 arrayFilters 选项。
// 把所有「数量小于 2」的商品行数量改成 10
db.orders.updateOne(
{ _id: 1001 },
{ $set: { "items.$[item].qty": 10 } },
{ arrayFilters: [ { "item.qty": { $lt: 2 } } ] }
)
这里的 item 是占位符,指代数组里「正在被检查」的每个元素。arrayFilters 里的条件决定哪些元素被选中。
Tip标识符可以随便起名,但要以字母开头,不能和字段名冲突。常用
elem。一个更新里可以写多个$[x]、$[y],对应arrayFilters里的多个条件。
组合:内嵌文档子字段也能改
数组元素本身是个内嵌文档,子字段照常点号访问。下面给所有高价商品(单价 ≥ 300)打九折:
db.orders.updateOne(
{ _id: 1001 },
{ $mul: { "items.$[p].price": 0.9 } },
{ arrayFilters: [ { "p.price": { $gte: 300 } } ] }
)
四种定位方式对比
| 写法 | 改哪些 | 何时用 |
|---|---|---|
"items.0.qty" | 固定下标 | 明确知道第几个 |
"items.$.qty" | 首个匹配的 | 按值定位第一个 |
"items.$[].qty" | 全部元素 | 统一改所有 |
"items.$[x].qty" | 满足条件的 | 按条件批量改 |
Warning
$和$[]不能同时出现在同一条更新里。需要「带条件改多个」时必须用$[elem]+arrayFilters。另外$依赖查询筛选命中数组元素,筛选没命中数组时它会失效。
学会了精准改数组元素,下章我们讲怎么把文档删掉,以及怎么删整张集合。