跳至主要内容

如何在 MongoDB 中使用 updateMany() 修改多个文档

了解如何使用并优化 MongoDB 中的 updateMany() 操作符,在一次操作中更新多个文档。
· 4分钟 阅读

使用 AI 探索

在使用 MongoDB 时,您可能会遇到需要通过一次操作更新多个文档的情况。虽然可以遍历文档逐个更新,但 updateMany 提供了更快捷、更高效的解决方案。

在这篇简短的教程中,我们将学习如何在 MongoDB 中使用 updateMany 操作符,并优化查询以获得尽可能好的性能。

如果您想用 Python 夯实 MongoDB 的数据处理基础,建议查看课程 Introduction to MongoDB in Python。

通过一个实际示例更新多个 MongoDB 文档

为便于说明,我们来看一个实际场景,您可能希望一次更新多个文档。

以下数据将作为我们的示例基础:

[
    { "name": "Nic Raboy", "title": "Master of Shenanigans", "salary": 50000, "last_raise_date": new Date("2013-06-01") },
    { "name": "Megan Grant", "title": "Word Wrangler", "salary": 125000, "last_raise_date": new Date("2020-12-01") },
    { "name": "Tony Kim", "title": "Alliance Czar", "salary": 80000, "last_raise_date": new Date("2025-02-01") },
];

在上述 JSON 中,我们有一家虚构公司的三位员工。公司希望对一段时间内未调整薪资的员工进行补偿性涨薪。

要更新所有符合条件的员工,我们可以使用 MongoDB 的 updateMany 操作符。为完成任务,我们可能会写出如下语句:

db.employees.updateMany(
    {
        "last_raise_date": {
            "$lte": new Date("2024-01-01")
        }
    },
    {
        "$mul": { "salary": 1.06 },
        "$set": { "last_raise_date": new Date() }
    }
);

那么,上述 updateMany 操作在做什么呢?

先回到基础,updateMany 接受两个参数,以及一个可选的第三个参数。

  • 第一个参数是一个对象,表示我们要用于筛选的数据。
  • 第二个参数是我们希望对筛选结果进行的更改。
  • 可选的第三个参数可以是 { “upsert”: true },当没有匹配项时会插入一条文档。

基于此,我们首先筛选所有 last_raise_date 早于 "2024-01-01" 的文档。回看我们非常基础的数据集,可以知道有三条中的两条匹配:

[
    { "name": "Nic Raboy", "title": "Master of Shenanigans", "salary": 50000, "last_raise_date": new Date("2013-06-01") },
    { "name": "Megan Grant", "title": "Word Wrangler", "salary": 125000, "last_raise_date": new Date("2020-12-01") }
];

根据更改条件参数,针对筛选出的文档会进行两项不同的更改。

"$mul": { "salary": 1.06 },

首先,我们使用 $mul 操作符将某个字段乘以一个数值。在本例中,我们希望为每位符合条件的员工加薪 6%。这意味着我们只需将该字段的当前值乘以 1.06。

为帮助记录此次加薪并防止未来的误加,我们还要将 last_raise_date 更新为当前日期:

"$set": { "last_raise_date": new Date() }

如果我们再次运行 updateMany 操作,将不会发生任何变化,因为筛选条件中的 last_raise_date 将不再匹配我们的三条文档中的任何一条。

以上只是一个示例,您的筛选条件可以复杂得多,同样,您计划进行的更改也可能更复杂,视具体用例而定。您可以在这段视频中进一步了解 MongoDB 的 updateMany。

通过 MongoDB 索引提升更新性能

在本例中,我们只处理了三条小文档。基本上,无论我们对集合执行什么操作,都很快,因为数据量不大。然而,在生产应用中,您可能要处理数百万条文档,或者频繁运行更新操作。

如果我们尝试对数百万条文档使用 updateMany 操作符,除非为筛选条件建立了合适的索引,否则可能会花费较长时间。

要为本例创建索引,可以执行类似如下的语句:

db.employees.createIndex({ "last_raise_date": 1 });

您可以通过执行以下命令验证 updateMany 是否使用了我们的索引:

db.runCommand({
    "explain": {
        "update": "employees",
        "updates": [
            {
                "q": { 
                    "last_raise_date": { 
                        "$lte": new Date("2024-01-01") 
                    }
                },
                "u": {
                    "$mul": { "salary": 1.06 },
                    "$set": { "last_raise_date": new Date() }
                }
            }
        ]
    },
    "verbosity": "allPlansExecution"
});

在结果中,如果 inputStage 为 "IXSCAN",则说明您正在使用索引并最大化 updateMany 的性能。如果 inputStage 为 "COLLSCAN",则说明未使用索引,您可能需要根据筛选条件重新考虑索引的建立方式。

为避免混淆,以上我们使用 runCommand 来分析批量更新操作的执行情况。如果您不需要分析信息,直接使用更简单、更优雅的 updateMany 即可。

总结

如本教程所示,在 MongoDB 中用一次操作更新多个文档并不困难。

借助 updateMany 操作符,我们可以定义用于匹配文档的筛选条件,以及包含要进行更改列表的更改条件。通过确保针对筛选条件建立了合适的索引,还可以进一步优化 updateMany 的性能。

如果您刚接触非关系型数据库, Introduction to NoSQL 课程提供了关于文档存储等概念的有用概览。

FAQs

MongoDB 中的 updateOne 与 updateMany 有何区别?

 updateOne 仅修改第一个匹配的文档,而 updateMany 会更新所有满足筛选条件的文档。请根据期望的变更范围进行选择。

updateMany 能与 upsert 一起使用吗?

可以,添加 { upsert: true } 作为第三个参数会在没有匹配项时插入新文档。不过,它更常与 updateOne 搭配使用。

updateMany 的常见用例有哪些?

它常用于批量更新,例如调整价格、修改用户状态,或清理大型数据集中已过期的字段。

updateMany 是否会返回结果数据?

会返回一个对象,其中包含 matchedCount 和 modifiedCount 等信息,指示匹配和更新的文档数量。

如果 updateMany 的筛选条件没有匹配到文档会怎样?

如果没有文档匹配且未使用 upsert,操作将完成但不会产生任何更改。不会抛出错误。

updateMany 在 MongoDB 中是原子操作吗?

不会,跨多个文档并非原子性。每个单独文档的更新是原子的,但整体不是。

如何测试 updateMany 操作的性能?

使用 explain() 或 runCommand({ explain: ... }) 来查看查询是否使用了索引,或执行了集合扫描。

可以在 updateMany 中链式使用多个更新操作符吗?

可以。MongoDB 支持在一次 updateMany 调用中组合使用 $set、$inc、$mul 等操作符。

索引对 updateMany 操作有何影响?

索引可以显著提升性能,因为它能减少筛选时需要扫描的文档数量。

如何撤销一次误执行的 updateMany 操作?

需要通过备份或日志进行恢复,因为 MongoDB 没有针对 updateMany 的内置回滚功能。请务必先在预发布环境测试,并备份生产数据。

Nic Raboy

作者

Nic Raboy

Nic Raboy 是 MongoDB 的开发者关系主管,带领一支由 Python、Java、C# 与 PHP 开发者组成的团队,创作优质内容,帮助开发者在项目中成功使用 MongoDB。他具备 Golang 与 JavaScript 经验,并常常撰写自己在开发过程中的见闻与实践。

主题