带驱动程序的 MongoDB
定义
兼容性
此方法可用于以下环境中托管的部署:
- MongoDB Atlas: The fully managed service for MongoDB deployments in the cloud
注意
This command is supported in all MongoDB Atlas clusters. For information on Atlas support for all commands, see Unsupported Commands.
MongoDB Enterprise:基于订阅、自我管理的 MongoDB 版本
MongoDB Community:源代码可用、免费使用且可自行管理的 MongoDB 版本
语法
updateOne()方法采用以下语法:
db.collection.updateOne( <filter>, <update>, { upsert: <boolean>, writeConcern: <document>, collation: <document>, arrayFilters: [ <filterdocument1>, ... ], hint: <document|string>, let: <document>, sort: <document>, maxTimeMS: <int>, bypassDocumentValidation: <boolean> } )
参数
db.collection.updateOne() 方法采用以下参数:
Parameter | 类型 | 说明 | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
文档 | ||||||||||||||||||||
文档或管道 | 要应用的修改。可以是以下之一:
要使用替换文档进行更新,请参阅 | |||||||||||||||||||
| 布尔 | Optional. When
为避免多次更新或插入,请确保 默认值为 | ||||||||||||||||||
| 文档 | |||||||||||||||||||
| 文档 | 可选。 可选。指定用于操作的排序规则。 排序规则允许用户为字符串比较指定特定于语言的规则,例如字母大小写和重音符号规则。 排序规则选项的语法如下: 指定排序规则时, 如果未指定排序规则,但集合具有默认排序规则(请参阅 如果没有为收集或操作指定排序规则,MongoDB 将使用先前版本中使用的简单二进制比较来进行字符串比较。 您不能为一个操作指定多个排序规则。例如,您不能为每个字段指定不同的排序规则,或者如果执行带排序的查找,则不能使用一种排序规则进行查找而另一种排序规则进行排序。 | ||||||||||||||||||
| 阵列 | 选修的。大量过滤器文档,用于确定针对大量字段的更新操作要修改哪些大量元素。 在更新文档中,使用
您可以在更新文档中多次包含相同的标识符;但对于更新文档中的每个不同标识符 ( 但是,您可以在单个过滤器文档中的同一标识符上指定复合条件,例如以下示例: For examples, see Specify | ||||||||||||||||||
文档或字符串 | Optional. A document or string that specifies the index to use to support the query predicate. 该选项可以采用索引规范文档或索引名称字符串。 如果指定不存在的索引,则操作出错。 For an example, see Specify | |||||||||||||||||||
| 文档 | 可选。 指定包含变量列表的文档。这样可以将变量与查询文本分开,从而提高命令的可读性。 文档语法为: 变量设置为表达式返回的值,并且之后不能再进行更改。 要访问命令中的变量值,请使用双美元符号前缀 ( 要使用变量筛选结果,您必须在 有关使用 | ||||||||||||||||||
| 文档 | 可选。 如果查询选择了多个文档,则确定该操作更新了哪个文档。 如果 sort 参数不是文档,则操作错误。 MongoDB 不按特定顺序将文档存储在集合中。对包含重复值的字段进行排序时,可能会以任何顺序返回包含这些值的文档。
如果排序条件中指定的字段在两个文档中都不存在,那么它们排序所依据的值是相同的。这两个文档可以以任何顺序返回。 如果需要一致的排序顺序,请在排序中至少纳入一个包含唯一值的字段。最简单方法是在排序查询中纳入 有关更多信息,请参阅排序一致性。 8.0版本新增。 | ||||||||||||||||||
整型 | 可选。指定更新操作在超时之前运行的时间限制(以毫秒为单位)。 | |||||||||||||||||||
布尔 | 可选。启用 |
返回:
该方法返回包含以下内容的文档:
matchedCount包含匹配文档的数量modifiedCount包含已修改文档的数量upsertedId,包含用于已更新或插入的文档的_idupsertedCount包含更新或插入文档的数量如果操作使用写关注来运行,则布尔值
acknowledged为true;如果已禁用写关注,则为false
访问控制
在使用 authorization 运行的部署中,用户必须具有包含以下特权的访问权限:
内置角色readWrite提供所需的特权。
行为
更新单个文档
db.collection.updateOne() finds the first document that matches the filter and applies the specified update modifications.
使用更新操作符表达式文档进行更新
For the update specifications, the db.collection.updateOne() method can accept a document that only contains update operator expressions.
例如:
db.collection.updateOne( <query>, { $set: { status: "D" }, $inc: { quantity: 2 } }, ... )
使用聚合管道进行更新
db.collection.updateOne() 方法可以接受指定要执行的修改的聚合管道 [ <stage1>, <stage2>, ... ]。该管道可以由以下阶段组成:
$addFields及其别名$set$replaceRoot及其别名$replaceWith
使用聚合分析管道可以进行更具表现力的更新声明,例如基于当前字段值的Express条件更新或使用另一个字段的值更新一个字段。
例如:
db.collection.updateOne( <query>, [ { $set: { status: "Modified", comments: [ "$misc1", "$misc2" ] } }, { $unset: [ "misc1", "misc2" ] } ] ... )
For examples, see Update with Aggregation Pipeline.
更新插入
从MongoDB 8.0开始,如果您在分分片的集合上指定
upsert: true,则无需在过滤中包含完整的分分片键。If
upsert: trueand no documents match thefilter,db.collection.updateOne()creates a new document based on thefiltercriteria andupdatemodifications. See Update with Upsert.For additional
db.collection.updateOne()behavior on a sharded collection, see Sharded Collections.
固定大小集合
如果更新操作更改了文档大小,则操作将失败。
分片集合
upsert 分片集合
要在分片集合上使用 db.collection.updateOne():
从MongoDB 8.0开始,如果您在分分片的集合上指定
upsert: true,则无需在过滤中包含完整的分分片键。If you don't specify
upsert: true, you must include an exact match on the_idfield or target a single shard (such as by including the shard key in the filter).
但是,分片集合中的文档可能缺少分片键字段。要定位缺失分片键的文档,可将 null 等值匹配与其他过滤条件(例如针对 _id 字段)结合使用。例如:
{ _id: <value>, <shardkeyfield>: null } // _id of the document missing shard key
分片键修改
您可以更新文档的分片键值,除非分片键字段是不可变的 _id 字段。
警告
分片集合中的文档可能缺少分片键字段。采取预防措施,避免在更改文档的分片键值时意外删除分片键。
要使用 db.collection.updateOne() 修改现有的分片键值:
See also upsert on a Sharded Collection.
缺少分片键
从版本 7.1 开始,您不需要在查询规范中提供分片键 或
_id字段。
另请参阅:
可解释性
updateOne() 与db.collection.explain() 不兼容。
事务
db.collection.updateOne() 可用于分布式事务。
重要
在大多数情况下,与单文档写入操作相比,分布式事务会产生更高的性能成本,并且分布式事务的可用性不应取代有效的模式设计。在许多情况下,非规范化数据模型(嵌入式文档和数组)仍然是数据和使用案例的最佳选择。换言之,对于许多场景,适当的数据建模将最大限度地减少对分布式事务的需求。
有关其他事务使用注意事项(如运行时间限制和 oplog 大小限制),另请参阅生产注意事项。
在 ACID 事务中进行更新或插入 (upsert)
如果分布式事务不是跨分片写入事务,则可以在该事务中创建集合和索引。
db.collection.updateOne() 可在现有集合或不存在集合上运行 upsert: true。如果在不存在的集合上运行,该操作将创建集合。
写关注和事务
如果是在事务中运行,则请勿显式设置此操作的写关注。要将写关注与事务一起使用,请参阅事务和写关注。
Oplog 条目
如果 db.collection.updateOne() 操作成功更新了文档,则该操作会为 oplog(操作日志)添加一个条目。如果操作失败或者未找到要更新的文档,则该操作不会为 oplog 添加条目。
示例
使用更新操作符表达式进行更新
restaurant 集合包含以下文档:
db.restaurant.insertMany( [ { _id: 1, name: "Central Perk Cafe", Borough: "Manhattan" }, { _id: 2, name: "Rock A Feller Bar and Grill", Borough: "Queens", violations: 2 }, { _id: 3, name: "Empire State Pub", Borough: "Brooklyn", violations: 0 } ] )
如下操作将在 name 为 Central Perk Cafe 的文档上设置 violations 字段:
try { db.restaurant.updateOne( { "name" : "Central Perk Cafe" }, { $set: { "violations" : 3 } } ); } catch (e) { print(e); }
该操作返回:
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
如果未找到匹配项,那么操作将返回:
{ "acknowledged" : true, "matchedCount" : 0, "modifiedCount" : 0 }
Setting upsert: true would insert the document if no match was found. See Update with Upsert
使用聚合管道进行更新
db.collection.updateOne() can use an aggregation pipeline to express a more complex update, such as a conditional update based on current field values. For the supported pipeline stages, see Update with an Aggregation Pipeline.
示例 1
以下示例使用聚合分析管道使用文档中其他字段的值来修改字段。
使用以下文档创建 students 集合:
db.students.insertMany( [ { _id: 1, student: "Skye", points: 75, commentsSemester1: "great at math", commentsSemester2: "loses temper", lastUpdate: ISODate("2019-01-01T00:00:00Z") }, { _id: 2, student: "Elizabeth", points: 60, commentsSemester1: "well behaved", commentsSemester2: "needs improvement", lastUpdate: ISODate("2019-01-01T00:00:00Z") } ] )
假设希望像第二份文档一样,将第一份文档中的 commentsSemester1 和 commentsSemester2 字段聚集到一个 comments 字段中,而不单独使用这两个字段。以下更新操作使用聚合管道:
添加新的
comments字段并设置lastUpdate字段。删除集合中所有文档的
commentsSemester1和commentsSemester2字段。
确保更新命令中的筛选器定针对唯一文档。以下代码中的字段 id 便是此类筛选器的示例:
db.students.updateOne( { _id: 1 }, [ { $set: { status: "Modified", comments: [ "$commentsSemester1", "$commentsSemester2" ], lastUpdate: "$$NOW" } }, { $unset: [ "commentsSemester1", "commentsSemester2" ] } ] )
- 第一个阶段:
$set阶段:创建一个新的数组字段
comments,其元素是commentsSemester1和commentsSemester2字段的当前内容,并且
- 第二阶段
$unset阶段删除commentsSemester1和commentsSemester2字段。
执行该命令之后,集合中将包含以下文档:
{ _id: 2, student: "Elizabeth", status: "Modified", points: 60, lastUpdate: ISODate("2020-01-23T05:11:45.784Z"), comments: [ "well behaved", "needs improvement" ] } { _id: 1, student: 'Skye', points: 75, commentsSemester1: 'great at math', commentsSemester2: 'loses temper', lastUpdate: ISODate("2019-01-01T00:00:00.000Z") }
示例 2
聚合管道允许更新根据当前字段值执行条件更新,并使用当前字段值计算单独的字段值。
例如,使用以下文档创建 students3 集合:
db.students3.insertMany( [ { _id: 1, tests: [ 95, 92, 90 ], average: 92, grade: "A", lastUpdate: ISODate("2020-01-23T05:18:40.013Z") }, { _id: 2, tests: [ 94, 88, 90 ], average: 91, grade: "A", lastUpdate: ISODate("2020-01-23T05:18:40.013Z") }, { _id: 3, tests: [ 70, 75, 82 ], lastUpdate: ISODate("2019-01-01T00:00:00Z") } ] )
第三个文档 _id: 3 缺少 average 和 grade 字段。使用聚合管道,可以根据计算出的平均成绩和字母等级来更新文档。
db.students3.updateOne( { _id: 3 }, [ { $set: { average: { $trunc: [ { $avg: "$tests" }, 0 ] }, lastUpdate: "$$NOW" } }, { $set: { grade: { $switch: { branches: [ { case: { $gte: [ "$average", 90 ] }, then: "A" }, { case: { $gte: [ "$average", 80 ] }, then: "B" }, { case: { $gte: [ "$average", 70 ] }, then: "C" }, { case: { $gte: [ "$average", 60 ] }, then: "D" } ], default: "F" } } } } ] )
执行该命令之后,集合中将包含以下文档:
{ _id: 1, tests: [ 95, 92, 90 ], average: 92, grade: "A", lastUpdate: ISODate("2020-01-23T05:18:40.013Z") } { _id: 2, tests: [ 94, 88, 90 ], average: 91, grade: "A", lastUpdate: ISODate("2020-01-23T05:18:40.013Z") } { _id: 3, tests: [ 70, 75, 82 ], lastUpdate: ISODate("2020-01-24T17:33:30.674Z"), average: 75, grade: "C" }
使用更新或插入进行更新
restaurant 集合包含以下文档:
db.restaurant.insertMany( [ { _id: 1, name: "Central Perk Cafe", Borough: "Manhattan", violations: 3 }, { _id: 2, name: "Rock A Feller Bar and Grill", Borough: "Queens", violations: 2 }, { _id: 3, name: "Empire State Pub", Borough: "Brooklyn", violations: "0" } ] )
以下操作尝试使用 name : "Pizza Rat's Pizzaria" 更新文档,同时 upsert: true:
try { db.restaurant.updateOne( { "name" : "Pizza Rat's Pizzaria" }, { $set: {"_id" : 4, "violations" : 7, "borough" : "Manhattan" } }, { upsert: true } ); } catch (e) { print(e); }
因为 upsert:true,所以该文档是基于 filter 和 update 标准 inserted 的。该操作返回:
{ "acknowledged" : true, "matchedCount" : 0, "modifiedCount" : 0, "upsertedId" : 4, "upsertedCount": 1 }
该集合现在包含以下文档:
{ _id: 1, name: "Central Perk Cafe", Borough: "Manhattan", violations: 3 }, { _id: 2, name: "Rock A Feller Bar and Grill", Borough: "Queens", violations: 2 }, { _id: 3, name: "Empire State Pub", Borough: "Brooklyn", violations: 4 }, { _id: 4, name: "Pizza Rat's Pizzaria", Borough: "Manhattan", violations: 7 }
name 字段使用 filter 条件填充,而 update 操作符用于创建文档的其余部分。
以下操作会使用大于 10 的 violations 更新第一个文档:
try { db.restaurant.updateOne( { "violations" : { $gt: 10} }, { $set: { "Closed" : true } }, { upsert: true } ); } catch (e) { print(e); }
该操作返回:
{ "acknowledged" : true, "matchedCount" : 0, "modifiedCount" : 0, "upsertedId" : ObjectId("56310c3c0c5cbb6031cafaea") }
该集合现在包含以下文档:
{ _id: 1, name: "Central Perk Cafe", Borough: "Manhattan", violations: 3 }, { _id: 2, name: "Rock A Feller Bar and Grill", Borough: "Queens", violations: 2 }, { _id: 3, name: "Empire State Pub", Borough: "Brooklyn", violations: 4 }, { _id: 4, name: "Pizza Rat's Pizzaria", Borough: "Manhattan", violations: 7 } { _id: ObjectId("56310c3c0c5cbb6031cafaea"), Closed: true }
由于没有文档与过滤器匹配,并且 upsert 是 true,因此 updateOne 仅使用生成的 _id 和 update 标准插入文档。
使用写关注更新
给定一个三节点副本集,以下操作指定 w 为 majority,指定 wtimeout 为 100:
try { db.restaurant.updateOne( { "name" : "Pizza Rat's Pizzaria" }, { $inc: { "violations" : 3}, $set: { "Closed" : true } }, { w: "majority", wtimeout: 100 } ); } catch (e) { print(e); }
如果主节点和至少一个从节点在 100 毫秒内确认每个写入操作,则返回:
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
如果确认花费的时间超过wtimeout限制,则会引发以下异常:
WriteConcernError({ "code" : 64, "errmsg" : "waiting for replication timed out", "errInfo" : { "wtimeout" : true, "writeConcern" : { "w" : "majority", "wtimeout" : 100, "provenance" : "getLastErrorDefaults" } } })
下表解释了 errInfo.writeConcern.provenance 的可能值:
来源 | 说明 |
|---|---|
| 应用程序中指定了写关注。 |
| 写入关注源自自定义的默认值。请参阅 |
| 写关注源自副本集的 |
| 在没有所有其他写入关注规范的情况下,写入关注源自服务器。 |
使用排序更新
8.0版本新增。
以下示例将停用评分最低的活跃用户:
db.people.updateOne( { state: "active" }, { $set: { state: "inactive" } }, { sort: { rating: 1 } } )
指定排序规则。
排序规则允许用户为字符串比较指定特定于语言的规则,例如字母大小写和重音符号规则。
集合 myColl 包含以下文档:
db.myColl.insertMany( [ { _id: 1, category: "café", status: "A" }, { _id: 2, category: "cafe", status: "a" }, { _id: 3, category: "cafE", status: "a" } ] )
以下操作包括排序规则选项:
db.myColl.updateOne( { category: "cafe" }, { $set: { status: "Updated" } }, { collation: { locale: "fr", strength: 1 } } );
为数组更新操作指定 arrayFilters
更新数组字段时,您可以指定 arrayFilters 确定要更新哪些数组元素。
更新元素匹配 arrayFilters 条件
使用以下文档创建集合 students:
db.students.insertMany( [ { _id: 1, grades: [ 95, 92, 90 ] }, { _id: 2, grades: [ 98, 100, 102 ] }, { _id: 3, grades: [ 95, 110, 100 ] } ] )
要修改 grades 数组中大于或等于 100 的所有元素,请在 db.collection.updateOne 方法中使用带有 arrayFilters 选项的过滤器位置操作符 $[<identifier>]:
db.students.updateOne( { grades: { $gte: 100 } }, { $set: { "grades.$[element]" : 100 } }, { arrayFilters: [ { "element": { $gte: 100 } } ] } )
该操作更新单个文档的 grades 字段,操作后,该集合包含以下文档:
{ _id: 1, grades: [ 95, 92, 90 ] } { _id: 2, grades: [ 98, 100, 100 ] } { _id: 3, grades: [ 95, 110, 100 ] }
更新文档数组的特定元素
使用以下文档创建集合 students2:
db.students2.insertMany( [ { _id: 1, grades: [ { grade: 80, mean: 75, std: 6 }, { grade: 85, mean: 90, std: 4 }, { grade: 85, mean: 85, std: 6 } ] }, { _id: 2, grades: [ { grade: 90, mean: 75, std: 6 }, { grade: 87, mean: 90, std: 3 }, { grade: 85, mean: 85, std: 4 } ] } ] )
要修改 grades 数组中等级大于或等于 85 的所有元素的 mean 字段的值,请将 过滤器 位置操作符 $[<identifier>] 与 db.collection.updateOne 方法中的 arrayFilters 结合使用:
db.students2.updateOne( { }, { $set: { "grades.$[elem].mean" : 100 } }, { arrayFilters: [ { "elem.grade": { $gte: 85 } } ] } )
该操作更新单个文档的数组,操作后,该集合包含以下文档:
{ _id: 1, grades: [ { grade: 80, mean: 75, std: 6 }, { grade: 85, mean: 100, std: 4 }, { grade: 85, mean: 100, std: 6 } ] } { _id: 2, grades: [ { grade: 90, mean: 75, std: 6 }, { grade: 87, mean: 90, std: 3 }, { grade: 85, mean: 85, std: 4 } ] }
为更新操作指定 hint
使用以下文档创建示例 students 集合:
db.students.insertMany( [ { _id: 1, student: "Richard", grade: "F", points: 0, comments1: null, comments2: null }, { _id: 2, student: "Jane", grade: "A", points: 60, comments1: "well behaved", comments2: "fantastic student" }, { _id: 3, student: "Ronan", grade: "F", points: 0, comments1: null, comments2: null }, { _id: 4, student: "Noah", grade: "D", points: 20, comments1: "needs improvement", comments2: null }, { _id: 5, student: "Adam", grade: "F", points: 0, comments1: null, comments2: null }, { _id: 6, student: "Henry", grade: "A", points: 86, comments1: "fantastic student", comments2: "well behaved" } ] )
在集合上创建以下索引:
db.students.createIndex( { grade: 1 } ) db.students.createIndex( { points: 1 } )
以下更新操作明确提示使用索引 { grade: 1 }:
注意
如果指定不存在的索引,则操作出错。
db.students.updateOne( { "points": { $lte: 20 }, "grade": "F" }, { $set: { "comments1": "failed class" } }, { hint: { grade: 1 } } )
更新命令返回以下内容:
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
注意
即使 3 个文档符合更新条件,updateOne 仅修改其查找到的第一个文档。因此,即使学生 Richard、Ronan 和 Adam 都符合条件,但只更新 Richard。
要查看使用的索引,请对操作运行 explain:
db.students.explain().update( { "points": { $lte: 20 }, "grade": "F" }, { $set: { "comments1": "failed class" } }, { multi: true, hint: { grade: 1 } } )
分片集群中的写关注错误
在版本8.1.2中进行了更改。
当 db.collection.updateOne() 在分片集群中的mongos上执行时,即使出现一个或多个其他错误,响应中也始终会报告 writeConcernError。在以前的版本中,其他错误有时会导致 db.collection.updateOne() 不报告写关注(write concern)错误。
示例,如果文档验证失败,触发 DocumentValidationFailed 错误,并且还发生写关注(write concern)错误,则 DocumentValidationFailed 错误和 writeConcernError 都会在响应的顶级字段中返回。
用户角色和文档更新
从 MongoDB 7.0 开始,您可以使用新的 USER_ROLES 系统变量来返回用户角色。
本部分中的示例展示了包含医疗信息的集合中的字段更新。该示例从 USER_ROLES 系统变量中读取当前用户角色,并且仅在用户具有特定角色时才执行更新。
要使用系统变量,请将$$添加到变量名称的开头。将USER_ROLES系统变量指定为$$USER_ROLES。
该示例创建这些用户:
James角色为Billing。Michelle角色为Provider。
执行以下步骤以创建角色、用户和集合:
创建角色
创建名为 Billing 和Provider 的角色,并赋予所需权限和资源。
运行:
db.createRole( { role: "Billing", privileges: [ { resource: { db: "test", collection: "medicalView" }, actions: [ "find" ] } ], roles: [ ] } ) db.createRole( { role: "Provider", privileges: [ { resource: { db: "test", collection: "medicalView" }, actions: [ "find" ] } ], roles: [ ] } )
以角色为 Provider 的 Michelle 身份登录,然后执行更新:
前面的示例使用 $setIntersection 返回文档,其中,"Provider" 字符串与 $$USER_ROLES.role 中的用户角色之间的交集不为空。Michelle 的角色为 Provider,因此,将执行更新。
接下来,以不具有 Provider 角色的 James 身份登录,并尝试执行相同更新:
前面的示例没有更新任何文档。
提示
要更新多个文档,请参阅 db.collection.updateMany()。