Learn the "why" behind slow queries and how to fix them in our 2-Part Webinar.
Register now >
Docs 菜单
Docs 主页
/ /

db.集合。 更新()(mongosh方法)

重要

已弃用的 mongosh 方法

此方法在 mongosh 中已弃用。有关替代方法,请参阅与旧版 mongo Shell 的兼容性更改

db.collection.update(query, update, options)

修改集合中的一个或多个现有文档。该方法可以修改现有文档的特定字段,也可以完全替换现有文档,具体取决于更新参数

默认情况下,db.collection.update() 方法会更新单个文档。请包括选项 multi: true ,以更新所有符合查询条件的文档。

此方法可用于以下环境中托管的部署:

  • MongoDB Atlas:用于云中 MongoDB 部署的完全托管服务

注意

所有 MongoDB Atlas 集群都支持此命令。有关 Atlas 对所有命令的支持的信息,请参阅不支持的命令

  • MongoDB Enterprise:基于订阅、自我管理的 MongoDB 版本

  • MongoDB Community:源代码可用、免费使用且可自行管理的 MongoDB 版本

5.0 版本中的更改

db.collection.update() 方法采用以下形式:

db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string>,
let: <document>,
maxTimeMS: <int>,
bypassDocumentValidation: <boolean>
}
)

db.collection.update() 方法使用以下参数:

Parameter
类型
说明

文档

更新的选择条件。可以使用与 find() 方法中相同的查询选择器

当您使用 执行update() upsert: true且该查询与任何现有文档都不匹配时,如果该查询使用点表示法在 字段上指定了条件, MongoDB将拒绝插入新文档。_id

文档或管道

要应用的修改。可以是以下之一:

仅包含 <field1>: <value1> 对。

仅包含以下聚合阶段:

有关详细信息和示例,请参阅oplog条目。

布尔

可选。 当true update()时, 将:

  • 如果没有文档与 query 匹配,则创建一个新文档。有关详细信息,请参阅更新或插入行为

  • 更新与 query 匹配的单份文档。

如果 upsertmulti 均为 true,并且没有文档与查询匹配,则更新操作仅插入单个文档。

要避免多次更新或插入,请确保 query字段具有唯一索引。有关示例,请参阅使用重复值进行更新或插入。

默认值为 false;即,在未找到匹配项时插入新文档。

布尔

可选。 如果设立true,则更新符合 query 条件的多个文档。 如果设立false,则更新一个文档。 默认值为 false有关更多信息,请参阅更新多个文档示例。

文档

可选。表达写关注的文档。省略以使用默认的写关注 w: "majority"

如果是在事务中运行,则请勿显式设置此操作的写关注。要将写关注与事务一起使用,请参阅事务和写关注。

有关使用writeConcern 的示例,请参阅覆盖默认写关注。

文档

可选。

排序规则允许用户为字符串比较指定特定于语言的规则,例如字母大小写和重音符号规则。

有关使用collation 的示例,请参阅指定排序规则。

阵列

选修的。大量过滤器文档,用于确定针对大量字段的更新操作要修改哪些大量元素。

在更新文档中,使用$[<identifier>] 定义一个标识符,以仅更新与arrayFilters 中相应过滤文档匹配的大量元素。

如果更新文档中未包含某一标识符,则无法获得该标识符的数组筛选文档。

有关示例,请参阅数组更新操作。

文档或字符串

可选。 指定用于支持查询谓词的索引的文档或字符串。

该选项可以采用索引规范文档或索引名称字符串。

如果指定不存在的索引,则操作出错。

有关示例,请参阅为更新操作指定 hint

文档

可选。

指定包含变量列表的文档。这样可以将变量与查询文本分开,从而提高命令的可读性。

文档语法为:

{
<variable_name_1>: <expression_1>,
...,
<variable_name_n>: <expression_n>
}

变量设置为表达式返回的值,并且之后不能再进行更改。

要访问命令中的变量值,请使用双美元符号前缀 ($$) 以及 $$<variable_name> 形式的变量名称。例如:$$targetTotal

要使用变量筛选结果,您必须在 $expr 操作符中访问该变量。

有关使用let 和变量的完整示例,请参阅在 let中使用变量。

整型

可选。指定更新操作在超时之前运行的时间限制(以毫秒为单位)。

布尔

可选。启用 insert 可在此操作过程中绕过模式验证。这样就可以插入不符合验证要求的文档。

该方法返回包含操作状态的 WriteResult 文档。

在使用 authorization 运行的部署中,用户必须具有包含以下特权的访问权限:

  • update 针对指定集合的操作。

  • find 针对指定集合的操作。

  • insert 如果操作导致更新插入,则对指定收集执行操作。

内置角色readWrite提供所需的特权。

如果设置multi: true,请仅将update()方法用于幂等操作。

在 upsert 标志设置为 true 时,尝试使用 $expr 操作符将产生错误。

如需在分片集合中使用 db.collection.update()multi: false,必须在 _id 字段中包含精确匹配,或以单个分片为目标(如包含分片键)。

db.collection.update() 执行更新操作(而非文档替换操作)时,db.collection.update() 可以将多个分片作为目标。

提示

替换文档操作会首先尝试使用查询过滤器定位单个分片。该操作如果无法通过查询过滤器定位单个分片,则会尝试通过替换文档定位。

在早期版本中,该操作会尝试使用替换文档定位。

对于包含 upsert: true 且在分片集合上的 db.collection.update() 操作,您必须在filter 中包含完整的分片键:

  • 用于更新操作。

  • 进行替换文档操作。

但是,分片集合中的文档可能缺少分片键字段。要定位缺失分片键的文档,可将 null 等值匹配 其他过滤条件(例如针对 _id 字段)结合使用。例如:

{ _id: <value>, <shardkeyfield>: null } // _id of the document missing shard key

您可以更新文档的分片键值,除非分片键字段是不可变的 _id 字段。

要修改现有的分片键值(使用db.collection.update()):

  • 必须mongos 上运行。请勿直接对分片发出此操作。

  • 必须事务中运行,或者以可重试写入的形式运行。

  • 必须指定 multi: false

  • 必须在完整分片键上包含相等查询筛选条件

提示

由于缺失的键值是作为 null 相等匹配的一部分返回的,因此为避免更新空值键,请酌情纳入其他查询条件(例如 _id 字段)。

另请参阅针对分片集合的 upsert

分片集合中的文档可能缺少分片键字段。要使用 db.collection.update() 来设置文档缺失的分片键,则必须mongos 上运行。请直接对分片发出此操作。

此外,以下要求也适用:

任务
要求

若要设为 null

  • 可以指定 multi: true

  • 如果 upsert: true,则需使用针对完整分片键的相等筛选器。

要设置为非 null

  • 必须事务中或作为可重试写入执行。

  • 必须指定 multi: false

  • 如果满足以下任一条件,则需使用针对完整分片键的相等筛选器:

    • upsert: true,或

    • 如果使用替换文档,并且新的分片键值属于另一个分片。

提示

由于缺失的键值是作为 null 相等匹配的一部分返回的,因此为避免更新空值键,请酌情纳入其他查询条件(例如 _id 字段)。

另请参阅:

db.collection.update() 可以在分布式事务中使用。

重要

在大多数情况下,与单文档写入操作相比,分布式事务会产生更高的性能成本,并且分布式事务的可用性不应取代有效的模式设计。在许多情况下,非规范化数据模型(嵌入式文档和数组)仍然是数据和使用案例的最佳选择。换言之,对于许多场景,适当的数据建模将最大限度地减少对分布式事务的需求。

有关其他事务使用注意事项(如运行时间限制和 oplog 大小限制),另请参阅生产注意事项

如果分布式事务不是跨分片写入事务,则可以在该事务中创建集合和索引。

具有 upsert: truedb.collection.update() 可以在现有集合或不存在的集合上运行。如果在不存在的集合上运行,该操作将创建集合。

如果是在事务中运行,则请勿显式设置此操作的写关注。要将写关注与事务一起使用,请参阅事务和写关注。

如果 db.collection.update() 操作成功更新了文档,则该操作会为 oplog(操作日志)添加一个条目。如果操作失败或者未找到要更新的文档,则该操作不会为 oplog 添加条目。

当您使用 upsert: true 执行 update() 并且查询与任何现有文档都不匹配时,如果查询使用点表示法_id 字段上指定条件,则 MongoDB 将拒绝插入新文档。

此限制确保 _id 文档中嵌入的字段顺序定义明确,并且不受查询中指定顺序的约束。

如果尝试以这种方式插入文档, MongoDB会引发错误。

除非有唯一索引来防止重复,否则 upsert 可能会创建重复的文档。

如果所有 db.collection.update() 操作在任何客户端成功插入数据之前完成查询阶段,并且 name字段上没有唯一索引,则每个 db.collection.update() 操作都可能导致插入,从而使用 name: Andy 创建多个文档。

name字段上的唯一索引可确保只创建一个文档。使用唯一索引后,多个 db.collection.update() 操作现在会表现出以下行为:

  • 仅有一个 db.collection.update() 操作将成功插入一个新的
    文档。
  • 其他 db.collection.update() 操作要么更新新插入的

    文档,否则会因唯一键冲突而失败。

    为了使其他 db.collection.update() 操作能够更新新插入的文档,必须满足以下所有条件:

    • 目标集合具有唯一索引,该索引会导致
      重复键错误。
    • 更新操作不是 updateManymulti
      false.
    • 更新匹配条件为:

      • 单个相等谓词。例如 { "fieldA" : "valueA" }

      • 等式谓词的逻辑 AND。例如 { "fieldA" : "valueA", "fieldB" : "valueB" }

    • 相等谓词中的字段与
      唯一索引键模式。
    • 更新操作不会修改任何字段
      唯一索引键模式。

在更新文档中,使用 $[<identifier>] 筛选后的位置运算符来定义标识符,然后在数组筛选文档中引用该标识符。如果更新文档中未包含某一标识符,则无法获得该标识符的数组筛选文档。

<identifier> 必须以小写字母开头,且只能包含字母数字字符。

您可以在更新文档中多次包含相同的标识符;但是,对于更新文档中的每个非重复标识符 ($[identifier]),您必须指定准确一个对应的数组过滤器文档。也就是说,不能为同一标识符指定多个数组过滤器文档。但是,您可以在单个过滤文档中对同一标识符指定复合条件

注意

arrayFilters 不适用于使用聚合管道的更新。

db.collection.update() 方法返回一个包含操作状态的 WriteResult() 对象。成功后,WriteResult()对象包含符合查询条件的文档数、更新插入的文档数以及修改的文档数。

如果 db.collection.update() 方法遇到写关注(write concern)错误,结果将包括 WriteResult.writeConcernError 字段。

下表解释了 WriteResult.writeConcernError.provenance 的可能值:

来源
说明

clientSupplied

应用程序中指定了写关注。

customDefault

写入关注源自自定义的默认值。请参阅 setDefaultRWConcern

getLastErrorDefaults

写关注源自副本集的 settings.getLastErrorDefaults 字段。

implicitDefault

在没有所有其他写入关注规范的情况下,写入关注源自服务器。

如果 db.collection.update() 方法遇到非写关注(write concern)错误,结果将包括 WriteResult.writeError 字段。

本页上的示例使用sample_mflix示例数据集中的数据。有关如何将此数据集加载到自管理MongoDB 部署中的详细信息,请参阅加载示例数据集。如果对示例数据库进行了任何修改,则可能需要删除并重新创建数据库才能运行本页上的示例。

如果 <update> 文档包含更新操作符修饰符,例如使用 $set 修饰符,则:

  • <update> 文档只能包含更新操作符表达式。

  • db.collection.update() 方法只更新文档中的相应字段。

    • 要将嵌入式文档或数组作为整体进行更新,请指定该字段的替换值。

    • 要更新嵌入式文档或数组中的特定字段,请使用点符号来指定字段。

db.movies.update(
{ title: "The Godfather" },
{
$inc: { "tomatoes.viewer.numReviews": 1 },
$set: {
"tomatoes.viewer.meter": 99
}
}
)
/* Corresponds to the following SQL statement:
* UPDATE movies
* SET tomatoes_viewer_numReviews = tomatoes_viewer_numReviews + 1,
* tomatoes_viewer_meter = 99
* WHERE title = "The Godfather"
*/

在此操作中:

  • { title: "The Godfather" }<query> 参数指定要更新的文档

  • $inc 操作符递增 tomatoes.viewer 嵌入式文档中的 numReviews 字段

  • $set 操作符更新 tomatoes.viewer 嵌入式文档中的 meter 字段。

如果 query 参数匹配到多个文档,此操作将只更新一个匹配文档。要更新多个文档,必须将 multi 选项设置为 true

以下操作使用 $push 更新操作符将值附加到 genres 数组。

db.movies.update(
{ title: "The Matrix" },
{
$push: { genres: "Thriller" }
}
)

更新后,genres数组将包含新值。

提示

以下操作使用$unset操作符从“教父”文档中删除metacritic字段。

db.movies.update(
{ title: "The Godfather" },
{ $unset: { metacritic: "" } }
)
/* $unset is similar (but not identical) to the following SQL
command which removes the ``metacritic`` field from the ``movies``
table
* UPDATE movies
* SET metacritic = NULL
* WHERE title = "The Godfather"
*/

更新后,metacritic字段将被删除。

如果 multi 设置为 truedb.collection.update() 方法会更新所有符合 <query> 标准的文档。multi 更新操作可与其他读/写操作交错进行。

以下操作将标题为“教父”或“黑客帝国”的文档的 test_field字段设置为 true

db.movies.update(
{ title: { $in: ["The Godfather", "The Matrix"] } },
{ $set: { test_field: true } },
{ multi: true }
)
/* Corresponds to the following SQL statement:
* UPDATE movies
* SET test_field = true
* WHERE title IN ('The Godfather', 'The Matrix')
*/

该操作会更新两个匹配的文档。

在执行替换时,不能指定 multi: true,并且 更新文档包含 field:value 表达式。

提示

指定选项 upsert: true 时:

如果在一个分片集合上指定 upsert: true,则必须在 filter 中包含完整的分片键。有关分片集合的其他 db.collection.update() 行为,请参阅分片集合

以下选项卡展示了使用 upsert 修饰符与 update() 的各种用途。

如果没有符合查询条件的文档,且 <update> 参数是替换文档(即仅包含字段和值对),则更新将插入一个包含替换文档的字段和值的新文档。

  • 如果您在查询参数或替换文档中指定 _id 字段,则 MongoDB 将在插入的文档中使用该 _id 字段。

  • 如果您未在查询参数或替换文档中指定 _id 字段,则 MongoDB 将使用随机生成的 ObjectId 值来生成 _id 字段。

    您不能在查询参数和替换文档中指定不同的 _id 字段值。如果这样做,操作将出错。

例如,如下更新会将更新或插入选项设置为 true

db.movies.update(
{ title: "Test Movie 12345" },
{
$set: {
title: "Test Movie 12345",
year: 2024,
genres: [ "Documentary" ],
rated: "NR"
}
},
{ upsert: true }
)

如果没有与 <query> 参数匹配的文档,则更新操作将插入一个包含替换文档的文档。由于在替换文档或查询文档中未指定 _id字段,因此该操作会为新文档的 _id字段创建一个新的唯一 ObjectId。您可以看到 upsert 反映在操作的 WriteResult 中。

如果没有符合查询条件的文档,且 <update> 参数是带有更新操作符表达式的文档,则该操作将根据 <query> 参数中的等式子句创建基础文档,并应用 <update> 参数中的表达式。

来自<query>比较操作将不包含在新文档中。 如果新文档不包含_id字段, MongoDB将添加带有ObjectId值的_id字段。

例如,如下更新会将更新或插入选项设置为 true

db.movies.update(
{ title: "Test Movie 67890" }, // Query parameter
{ // Update document
$set: { rated: "PG" },
$setOnInsert: { year: 2024, type: "movie" }
},
{ upsert: true } // Options
)

如果没有符合查询条件的文档,则该操作将插入相应的文档。

如果 <update> 参数是聚合管道,更新会根据 <query> 参数中的相等子句创建一个基础文档,然后将管道应用于该文档,创建要插入的文档。如果新文档不包含 _id 字段,MongoDB 将添加具有 ObjectId 值的 _id 字段。

示例,以下聚合管道会在 movies集合中插入一个新文档,因为不存在与查询筛选条件匹配的现有文档:

db.movies.update(
{ title: "Test Movie ABC123" }, // Query parameter
[ // Aggregation pipeline
{ $set: {
year: 2024,
type: "movie",
rated: "NR",
lastModified: "$$NOW"
} }
],
{ upsert: true } // Options
)

提示

有关使用聚合管道进行更新的其他示例,请参阅使用聚合管道进行更新。

以下操作同时指定了 multi 选项和 upsert 选项。如果存在匹配的文档,则该操作将更新所有匹配的文档。如果不存在匹配的文档,则该操作将插入一个新文档。

db.movies.update(
{ title: { $in: ["The Godfather", "The Matrix"] } },
{ $set: { test_upsert_field: true } },
{ upsert: true, multi: true }
)

由于两部电影都存在于集合中,因此该操作会同时更新两份匹配的文档。

如果集合中没有匹配的文档,操作结果将是使用 <query><update> 规范中的字段插入一个文档。例如,考虑以下操作:

db.movies.update(
{ "title": "Test Movie Unique789" },
{ $set: { year: 2024, type: "movie" } },
{ upsert: true, multi: true }
)

该操作将相应的文档插入到 movies集合中。

db.collection.update() 方法可以接受指定要执行的修改的聚合管道 [ <stage1>, <stage2>, ... ]。该管道可以由以下阶段组成:

使用聚合分析管道可以进行更具表现力的更新声明,例如基于当前字段值的Express条件更新或使用另一个字段的值更新一个字段。

以下示例创建了一个将电影标题和年份组合在一起的 displayTitle字段,并通过执行这些操作的聚合管道:

  • 通过连接 titleyear 字段来创建 displayTitle字段

  • 设置 lastModified 时间戳

db.movies.update(
{ title: "The Godfather" },
[
{ $set: {
displayTitle: { $concat: [ "$title", " (", { $toString: "$year" }, ")" ] },
lastModified: "$$NOW"
}
}
]
)

以下示例更新了 2015 上映的电影,以计算 IMDB 和 Tomatoes 评分的组合评分,并根据该分数指定一个等级。

db.movies.update(
{
year: 2015,
"imdb.rating": { $type: "number" },
"tomatoes.viewer.rating": { $type: "number" }
},
[
{ $set: {
combinedScore: {
$round: [
{ $avg: [
{ $multiply: [ "$imdb.rating", 10 ] },
{ $multiply: [ "$tomatoes.viewer.rating", 10 ] }
] },
1
]
},
lastUpdate: "$$NOW"
}
},
{ $set: {
grade: {
$switch: {
branches: [
{ case: { $gte: [ "$combinedScore", 80 ] }, then: "A" },
{ case: { $gte: [ "$combinedScore", 70 ] }, then: "B" },
{ case: { $gte: [ "$combinedScore", 60 ] }, then: "C" },
{ case: { $gte: [ "$combinedScore", 50 ] }, then: "D" }
],
default: "F"
}
}
}
}
],
{ multi: true }
)

注意

管道中使用的 $set 指的是聚合阶段 $set,而不是更新操作符 $set

$set 阶段:

  • 通过计算 IMDB 评分(按 10 缩放)和 Tomatoes 收视率(按 10 缩放)的平均值计算新字段combinedScore,然后四舍五入到小数点后一位。有关更多信息,请参阅 $avg$multiply$round

  • 将字段 lastUpdate 设置为聚合变量 NOW 的值。

  • 根据 combinedScore 使用 $switch 操作符分配字母等级。

要更新符合指定条件的所有数组元素,请使用 arrayFilters 参数。

以下示例更新其 languages数组中包含 "English" 的所有电影。该操作会将 "English" 替换为 "EN"

db.movies.update(
{ languages: "English" },
{ $set: { "languages.$[element]" : "EN" } },
{
arrayFilters: [ { "element": "English" } ],
multi: true
}
)

您还可以将 arrayFilters 参数与筛选后的位置操作符一起使用,以更新与模式匹配的特定数组元素。

以下示例使用现有集合中的“教父”电影,该集合具有 writers数组。该操作仅更新名称通过附加后缀包含“剧本”的作者。

db.movies.update(
{ title: "The Godfather" },
{ $set: { "writers.$[elem]" : { $concat: [ "$elem", " - UPDATED" ] } } },
{
arrayFilters: [ { "elem": { $regex: /screenplay/ } } ]
}
)

该操作以“教父”文档为目标,仅更新与过滤器条件匹配的数组元素。操作完成后,撰写剧本的编剧会在后面加上“- UPDATED”。

hint 选项允许您指定MongoDB应使用哪个索引进行更新操作。当更新多个文档并且您希望确保使用特定索引来提高性能时,这非常有用。此示例使用 sample_mflix数据库中的现有 movies集合。

首先,在 year字段上创建索引:

db.movies.createIndex( { year: 1 } )

以下更新操作明确提示使用 { year: 1 }索引来更新2010-2015 之间的所有电影:

db.movies.update(
{ year: { $gte: 2010, $lte: 2015 } }, // Query parameter
{ $set: { decade: "2010s" } }, // Update document
{ multi: true, hint: { year: 1 } } // Options
)

注意

如果指定不存在的索引,则操作出错。

要查看使用的索引,请对操作运行 explain

db.movies.explain().update(
{ title: "The Godfather", year: { $gte: 1970 } },
{ $set: { test_hint_field: true } },
{ hint: { year: 1 } }
)

db.collection.explain().update() 不修改文档。

版本 5.0 中的新增功能

要定义可在命令中其他位置访问的变量,请使用 let 选项。

注意

要使用变量筛选结果,您必须在 $expr 操作符中访问该变量。

示例:

  • let 选项中定义两个变量:targetTitle(设立为 "The Matrix")和 newTitle(设立为 "The Matrix Reloaded")

  • 在查询筛选条件中使用 $expr 将文档的 title字段与 $$targetTitle 变量进行比较

  • 使用带有 $set 的聚合管道,将 title字段更新为 $$newTitle 的值

db.movies.update(
{ $expr: { $eq: [ "$title", "$$targetTitle" ] } },
[ { $set: { sequel: "$$sequelTitle" } } ],
{ let : { targetTitle: "The Matrix", sequelTitle: "The Matrix Reloaded" } }
)

以下对副本集的操作指定了写关注 w: 2,其 wtimeout 为 5000 毫秒。该操作要么在写操作传播到主节点和一个辅助节点后返回,要么在 5 秒后超时。

db.movies.update(
{ num_mflix_comments: { $lte: 10 } },
{ $set: { featured: true } },
{
multi: true,
writeConcern: { w: 2, j: true, wtimeout: 5000 }
}
)

在主节点 (primary node in the replica set)和至少一个从节点(secondary node from replica set)(如 w: 2 指定)确认写入后,操作成功完成。

在版本8.1.2中进行了更改

db.collection.update() 在分片集群中的 mongos 上执行时,即使出现一个或多个其他错误,响应中也始终会报告 writeConcernError。在以前的版本中,其他错误有时会导致 db.collection.update() 不报告写关注(write concern)错误。

示例,如果文档验证失败,触发 DocumentValidationFailed 错误,并且还发生写关注(write concern)错误,则 DocumentValidationFailed 错误和 writeConcernError 都会在响应的顶级字段中返回。

指定用于操作的排序规则

排序规则允许用户为字符串比较指定特定于语言的规则,例如字母大小写和重音符号规则。

排序规则选项的语法如下:

collation: {
locale: <string>,
caseLevel: <boolean>,
caseFirst: <string>,
strength: <int>,
numericOrdering: <boolean>,
alternate: <string>,
maxVariable: <string>,
backwards: <boolean>
}

指定排序规则时,locale 字段为必填字段;所有其他排序规则字段均为可选字段。有关字段的说明,请参阅排序规则文档

如果未指定排序规则,但集合具有默认排序规则(请参阅 db.createCollection()),则操作将使用为集合指定的排序规则。

如果没有为收集或操作指定排序规则,MongoDB 将使用先前版本中使用的简单二进制比较来进行字符串比较。

您不能为一个操作指定多个排序规则。例如,您不能为每个字段指定不同的排序规则,或者如果执行带排序的查找,则不能使用一种排序规则进行查找而另一种排序规则进行排序。

此操作会更新标题以 night 开头的所有电影,并使用 strength: 1 进行不区分大小写的比较:

db.movies.update(
{ title: /^night/i },
{ $set: { updated: true } },
{
collation: { locale: "en", strength: 1 },
multi: true
}
)

后退

db.collection.unhideIndex

在此页面上