数组字段怎么改
MongoDB 文档可以内嵌数组,更新时要用专用数组操作符,而不是把整个字段重新 $set 一遍。整字段覆盖会丢掉并发写入追加的内容,数组操作符则是在单文档内做原子修改:追加、去重添加、按条件删除、截断保留。
基本信息
| 适用版本 | MongoDB 4.x-8.x / mongosh |
|---|---|
| 作用对象 | 单文档内的数组字段 |
| 常用操作符 | $push / $addToSet / $pop / $pull / $pullAll |
| 修饰符 | $each / $slice / $sort(仅 $push 支持前三个) |
| 所需权限 | readWrite |
典型使用场景
- 给文档追加标签、事件记录:$push 原子追加,不会被并发写覆盖
- 维护白名单/黑名单:$addToSet 只在元素不存在时写入,天然去重
- 清理失效元素:$pull 按值或条件移除,如删掉状态为 draft 的标签
- 只保留最近 N 条:$push 配 $slice:-N,把流水类数组限制在固定长度
- 修改数组内某个元素:定位符 $ 或 arrayFilters 精确命中,不必读改写整数组
示例
示例
db.posts.updateOne({ _id: 1 }, { $push: { tags: { $each: ["mongo", "index"], $slice: -5 } } }) # 追加并只保留最近 5 个
db.posts.updateOne({ _id: 1 }, { $addToSet: { tags: { $each: ["mongo", "db"] } } }) # 去重添加,已存在的不写
db.posts.updateOne({ _id: 1 }, { $pull: { tags: "draft" } }) # 移除所有等于 draft 的元素
db.posts.updateOne({ _id: 1 }, { $pop: { tags: 1 } }) # 1 弹尾部,-1 弹头部
db.posts.updateOne({ _id: 1, "comments.id": 9 }, { $set: { "comments.$.text": "ok" } }) # 定位符改单个元素
db.orders.updateOne({ _id: 7 }, { $set: { "items.$[e].qty": 3 } }, { arrayFilters: [ { "e.sku": "A-1" } ] })操作符对照
| 操作符 | 作用 | 注意点 |
|---|---|---|
| $push | 向数组尾部追加元素 | 可组合 $each/$slice/$sort,字段不存在时自动创建数组 |
| $addToSet | 仅当元素不存在时添加 | 只支持 $each,不支持 $slice/$sort,判等是整个元素比较 |
| $pull | 移除所有匹配的元素 | 匹配条件可写成子文档形式,不是只删第一个 |
| $pop | 弹出头部或尾部一个元素 | 1 弹尾部、-1 弹头部,方向容易记反 |
| $pullAll | 按值列表批量移除 | 只匹配完整元素值,支持不了条件表达式 |
数组长期无界增长会持续推高文档体积(单文档上限 16MB)并拖慢每次更新;评论、日志这类流水数据应独立成集合,数组里只保留摘要或最近若干条。
定位符 $ 命中查询条件匹配的第一个元素,适合已知下标的场景;arrayFilters 用 $[标识符] 加过滤数组,可以一次更新多个满足条件的元素(MongoDB 3.6 起支持)。
$slice 为 0 表示清空数组,负值表示从尾部保留 N 个、正值表示从头部保留 N 个;截断方向写错会直接丢掉想要的数据,改前先用 find 验证效果。