该用 $pull 而不是 $unset 或 $pop 时,是需按条件从数组中移除一个或多个匹配元素;$unset 删除整个字段,$pop 仅删首尾单个元素,而 $pull 支持值、对象或操作符条件匹配并删除所有符合条件的数组元素。

什么时候该用 $pull 而不是 $unset 或 $pop
$pull 专用于从数组中**按条件移除一个或多个匹配元素**,而不是清空整个数组、删除末尾/开头单个元素,也不是把字段设为 null。$unset 会删掉整个字段,$pop 只能删首尾且一次一个——如果你要删掉所有值为 "pending" 的状态项,或者删掉 tags 数组里所有包含 "deprecated" 的字符串,$pull 是唯一合理选择。
$pull 的基本写法和常见错误写法
正确写法是直接在更新操作中使用 { $pull: { "arrayField": <condition> } }</condition>。条件可以是值、对象、表达式,但必须和数组元素类型一致。
- 删掉数组中所有等于指定值的元素:
{ $pull: { tags: "old" } } - 删掉嵌入文档数组中满足字段条件的项:
{ $pull: { comments: { status: "spam" } } } - 用操作符做更灵活匹配:
{ $pull: { scores: { $lt: 60 } } }(删掉所有低于 60 的分数) - ⚠️ 错误:写成
{ $pull: { "tags.0": "old" } }——$pull不支持点号路径定位具体索引,它只作用于整个数组字段 - ⚠️ 错误:对非数组字段用
$pull,MongoDB 会静默忽略,不报错也不生效
批量删多个不同条件的元素要分两次操作
$pull 一次只能指定一个条件表达式,不能像 SQL 的 IN 那样一次删多个离散值(比如同时删 "a" 和 "c")。如果真要这么做,得用 $in 构造条件:
{ $pull: { tags: { $in: ["a", "c"] } } }
但如果要删的条件彼此无关(比如删 status: "draft" 的文档,同时删 score: { $gt: 95 } 的数字),就必须拆成两个独立的 $pull 操作,或改用 find + updateOne 循环处理——MongoDB 不支持单条命令里多个 $pull 针对同一字段。
另外注意:如果数组里有重复元素,$pull 会全部删掉,不是只删第一个。
性能与原子性需要注意的地方
$pull 是原子操作,整个更新要么全成功,要么全失败,这点放心。但性能上要注意两点:
- 如果目标数组极大(比如几十万元素),
$pull会遍历整个数组做匹配,可能阻塞写操作;考虑是否真需要把大量数据堆在单个文档数组里 - 没有对应索引能加速
$pull的匹配过程——MongoDB 对数组字段建索引,只加速查询,不加速$pull本身的遍历 - 如果删完后数组变空,字段不会自动消失;如需清理空数组,得额外加
$unset或应用层判断
真正容易被忽略的是:当数组元素是文档时,$pull 的匹配是**浅层相等**,不是深度相等。比如 { a: 1, b: 2 } 不会匹配 { b: 2, a: 1 }(字段顺序不同),除非你用 $elemMatch 显式声明字段存在性。











