定义
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 | 类型 | 说明 | ||||||
|---|---|---|---|---|---|---|---|---|
文档 | ||||||||
文档或管道 | 要应用的修改。可以是以下之一:
| |||||||
布尔 | ||||||||
布尔 | 可选。 如果设立 | |||||||
文档 | 可选。表达写关注的文档。省略以使用默认的写关注 如果是在事务中运行,则请勿显式设置此操作的写关注。要将写关注与事务一起使用,请参阅事务和写关注。 有关使用 | |||||||
文档 | ||||||||
阵列 | 选修的。大量过滤器文档,用于确定针对大量字段的更新操作要修改哪些大量元素。 在更新文档中,使用 如果更新文档中未包含某一标识符,则无法获得该标识符的数组筛选文档。 有关示例,请参阅数组更新操作。 | |||||||
文档或字符串 | ||||||||
文档 | ||||||||
整型 | 可选。指定更新操作在超时之前运行的时间限制(以毫秒为单位)。 | |||||||
布尔 | 可选。启用 |
返回:
该方法返回包含操作状态的 WriteResult 文档。
访问控制
在使用 authorization 运行的部署中,用户必须具有包含以下特权的访问权限:
内置角色readWrite提供所需的特权。
行为
限制
如果设置multi: true,请仅将update()方法用于幂等操作。
在带有 Upsert 的更新中使用 $expr
在 upsert 标志设置为 true 时,尝试使用 $expr 操作符将产生错误。
分片集合
如需在分片集合中使用 db.collection.update() 和 multi: false,必须在 _id 字段中包含精确匹配,或以单个分片为目标(如包含分片键)。
当 db.collection.update() 执行更新操作(而非文档替换操作)时,db.collection.update() 可以将多个分片作为目标。
分片集合上的替换文档操作
替换文档操作会首先尝试使用查询过滤器定位单个分片。该操作如果无法通过查询过滤器定位单个分片,则会尝试通过替换文档定位。
在早期版本中,该操作会尝试使用替换文档定位。
upsert 分片集合
对于包含 upsert: true 且在分片集合上的 db.collection.update() 操作,您必须在filter 中包含完整的分片键:
用于更新操作。
进行替换文档操作。
但是,分片集合中的文档可能缺少分片键字段。要定位缺失分片键的文档,可将 null 等值匹配 与其他过滤条件(例如针对 _id 字段)结合使用。例如:
{ _id: <value>, <shardkeyfield>: null } // _id of the document missing shard key
分片键修改
您可以更新文档的分片键值,除非分片键字段是不可变的 _id 字段。
要修改现有的分片键值(使用db.collection.update()):
提示
由于缺失的键值是作为 null 相等匹配的一部分返回的,因此为避免更新空值键,请酌情纳入其他查询条件(例如 _id 字段)。
另请参阅针对分片集合的 upsert。
缺少分片键
分片集合中的文档可能缺少分片键字段。要使用 db.collection.update() 来设置文档缺失的分片键,则必须在 mongos 上运行。请勿直接对分片发出此操作。
此外,以下要求也适用:
任务 | 要求 |
|---|---|
若要设为 |
|
要设置为非 |
提示
由于缺失的键值是作为 null 相等匹配的一部分返回的,因此为避免更新空值键,请酌情纳入其他查询条件(例如 _id 字段)。
另请参阅:
事务
db.collection.update() 可以在分布式事务中使用。
重要
在大多数情况下,与单文档写入操作相比,分布式事务会产生更高的性能成本,并且分布式事务的可用性不应取代有效的模式设计。在许多情况下,非规范化数据模型(嵌入式文档和数组)仍然是数据和使用案例的最佳选择。换言之,对于许多场景,适当的数据建模将最大限度地减少对分布式事务的需求。
有关其他事务使用注意事项(如运行时间限制和 oplog 大小限制),另请参阅生产注意事项。
在 ACID 事务中进行更新或插入 (upsert)
如果分布式事务不是跨分片写入事务,则可以在该事务中创建集合和索引。
具有 upsert: true 的 db.collection.update() 可以在现有集合或不存在的集合上运行。如果在不存在的集合上运行,该操作将创建集合。
写关注和事务
如果是在事务中运行,则请勿显式设置此操作的写关注。要将写关注与事务一起使用,请参阅事务和写关注。
Oplog 条目
如果 db.collection.update() 操作成功更新了文档,则该操作会为 oplog(操作日志)添加一个条目。如果操作失败或者未找到要更新的文档,则该操作不会为 oplog 添加条目。
使用虚线 _id 查询进行更新或插入(upsert)
当您使用 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()操作能够更新新插入的文档,必须满足以下所有条件:- 目标集合具有唯一索引,该索引会导致
- 重复键错误。
- 更新操作不是
updateMany或multi是 false.
- 更新操作不是
更新匹配条件为:
单个相等谓词。例如
{ "fieldA" : "valueA" }等式谓词的逻辑 AND。例如
{ "fieldA" : "valueA", "fieldB" : "valueB" }
- 相等谓词中的字段与
- 唯一索引键模式。
- 更新操作不会修改任何字段
- 唯一索引键模式。
- 其他
数组更新操作
在更新文档中,使用 $[<identifier>] 筛选后的位置运算符来定义标识符,然后在数组筛选文档中引用该标识符。如果更新文档中未包含某一标识符,则无法获得该标识符的数组筛选文档。
<identifier> 必须以小写字母开头,且只能包含字母数字字符。
您可以在更新文档中多次包含相同的标识符;但是,对于更新文档中的每个非重复标识符 ($[identifier]),您必须指定准确一个对应的数组过滤器文档。也就是说,不能为同一标识符指定多个数组过滤器文档。但是,您可以在单个过滤文档中对同一标识符指定复合条件
注意
arrayFilters 不适用于使用聚合管道的更新。
WriteResult
成功结果
db.collection.update() 方法返回一个包含操作状态的 WriteResult() 对象。成功后,WriteResult()对象包含符合查询条件的文档数、更新插入的文档数以及修改的文档数。
写关注错误
如果 db.collection.update() 方法遇到写关注(write concern)错误,结果将包括 WriteResult.writeConcernError 字段。
下表解释了 WriteResult.writeConcernError.provenance 的可能值:
来源 | 说明 |
|---|---|
| 应用程序中指定了写关注。 |
| 写入关注源自自定义的默认值。请参阅 |
| 写关注源自副本集的 |
| 在没有所有其他写入关注规范的情况下,写入关注源自服务器。 |
与写关注无关的错误
如果 db.collection.update() 方法遇到非写关注(write concern)错误,结果将包括 WriteResult.writeError 字段。
示例
本页上的示例使用sample_mflix示例数据集中的数据。有关如何将此数据集加载到自管理MongoDB 部署中的详细信息,请参阅加载示例数据集。如果对示例数据库进行了任何修改,则可能需要删除并重新创建数据库才能运行本页上的示例。
使用更新操作符表达式($inc 和 $set)
如果 <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。
删除字段 ($unset)
以下操作使用$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字段将被删除。
更新多个文档($update 使用 multi)
如果 multi 设置为 true,db.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)
指定选项 upsert: true 时:
如果文档符合查询条件,
db.collection.update()将执行更新。如果没有文档符合查询条件,
db.collection.update()将插入单个文档。注意
如果在大致相同的时间内发出多个相同的更新或插入操作,那么与 upsert: true 一起使用的
update()有可能会创建重复的文档。有关详细信息,请参阅具有重复值的更新或插入 。
如果在一个分片集合上指定 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 中。
使用操作符表达式进行更新或插入(upsert) ($set)
如果没有符合查询条件的文档,且 <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 )
提示
有关使用聚合管道进行更新的其他示例,请参阅使用聚合管道进行更新。
将 upsert 与 multi 结合使用(匹配)
以下操作同时指定了 multi 选项和 upsert 选项。如果存在匹配的文档,则该操作将更新所有匹配的文档。如果不存在匹配的文档,则该操作将插入一个新文档。
db.movies.update( { title: { $in: ["The Godfather", "The Matrix"] } }, { $set: { test_upsert_field: true } }, { upsert: true, multi: true } )
由于两部电影都存在于集合中,因此该操作会同时更新两份匹配的文档。
将 upsert 与 multi 结合使用(无匹配)
如果集合中没有匹配的文档,操作结果将是使用 <query> 和 <update> 规范中的字段插入一个文档。例如,考虑以下操作:
db.movies.update( { "title": "Test Movie Unique789" }, { $set: { year: 2024, type: "movie" } }, { upsert: true, multi: true } )
该操作将相应的文档插入到 movies集合中。
使用聚合管道进行更新
db.collection.update() 方法可以接受指定要执行的修改的聚合管道 [ <stage1>, <stage2>, ... ]。该管道可以由以下阶段组成:
$addFields及其别名$set$replaceRoot及其别名$replaceWith
使用聚合分析管道可以进行更具表现力的更新声明,例如基于当前字段值的Express条件更新或使用另一个字段的值更新一个字段。
使用文档中其他字段的值修改字段
以下示例创建了一个将电影标题和年份组合在一起的 displayTitle字段,并通过执行这些操作的聚合管道:
通过连接
title和year字段来创建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 阶段:
为数组更新操作指定 arrayFilters
更新匹配 arrayFilters 条件的元素
要更新符合指定条件的所有数组元素,请使用 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
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 } } )
使用以下项中的变量: let
版本 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 } )