findAndModify
定义
findAndModify
The
findAndModify
command updates and returns a single document. By default, the returned document does not include the modifications made on the update. To return the document with the modifications made on the update, use thenew
option.提示
在
mongosh
中,该命令也可通过db.collection.findAndModify()
辅助方法运行。辅助方法对
mongosh
用户来说很方便,但它们返回的信息级别可能与数据库命令不同。如果不追求方便或需要额外的返回字段,请使用数据库命令。
兼容性
此命令可用于以下环境中托管的部署:
MongoDB Atlas:用于云中 MongoDB 部署的完全托管服务
注意
所有 MongoDB Atlas 集群都支持此命令。有关 Atlas 对所有命令的支持的信息,请参阅不支持的命令。
MongoDB Enterprise:基于订阅、自我管理的 MongoDB 版本
MongoDB Community:源代码可用、免费使用且可自行管理的 MongoDB 版本
5.0 版本中的更改。
语法
该命令具有以下语法:
db.runCommand( { findAndModify: <collection-name>, query: <document>, sort: <document>, remove: <boolean>, update: <document or aggregation pipeline>, new: <boolean>, fields: <document>, upsert: <boolean>, bypassDocumentValidation: <boolean>, writeConcern: <document>, maxTimeMS: <integer>, collation: <document>, arrayFilters: <array>, hint: <document|string>, comment: <any>, let: <document> // Added in MongoDB 5.0 } )
命令字段
该命令接受以下字段:
字段 | 类型 | 说明 | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 文档 | Optional. The selection criteria for the modification. The 如果未指定,则默认为空文档。 If the query argument is not a document, the operation errors. | ||||||||||||||||||
| 文档 | Optional. Determines which document the operation updates if the query
selects multiple documents. 如果 sort 参数不是文档,则操作错误。 MongoDB 不按特定顺序将文档存储在集合中。对包含重复值的字段进行排序时,可能会以任何顺序返回包含这些值的文档。 如果需要一致的排序顺序,请在排序中至少纳入一个包含唯一值的字段。最简单方法是在排序查询中纳入 有关更多信息,请参阅排序一致性。 | ||||||||||||||||||
| 布尔 | Must specify either the | ||||||||||||||||||
| 文档或数组 | Must specify either the
| ||||||||||||||||||
| 布尔 | Optional. When | ||||||||||||||||||
| 文档 | Optional. A subset of fields to return. The If the fields argument is not a document, the operation errors. | ||||||||||||||||||
| 布尔 | Optional. Used in conjunction with the When
要避免多次更新或插入,请确保 默认值为 | ||||||||||||||||||
| 布尔 | 可选。启用 | ||||||||||||||||||
| 文档 | |||||||||||||||||||
| non-negative integer | 可选。 指定时间限制(以毫秒为单位)。如果您未指定 MongoDB 使用与 | ||||||||||||||||||
| 字符串 | The collection against which to run the command. | ||||||||||||||||||
| 文档 | 可选。 指定用于操作的排序规则。 排序规则允许用户为字符串比较指定特定于语言的规则,例如字母大小写和重音符号规则。 排序规则选项的语法如下:
指定排序规则时, 如果未指定排序规则,但集合具有默认排序规则(请参阅 如果没有为收集或操作指定排序规则,MongoDB 将使用先前版本中使用的简单二进制比较来进行字符串比较。 您不能为一个操作指定多个排序规则。例如,您不能为每个字段指定不同的排序规则,或者如果执行带排序的查找,则不能使用一种排序规则进行查找而另一种排序规则进行排序。 | ||||||||||||||||||
| 阵列 | 选修的。大量过滤器文档,用于确定针对大量字段的更新操作要修改哪些大量元素。 在更新文档中,使用
您可以在更新文档中多次包含相同的标识符;但对于更新文档中的每个不同标识符 (
但是,您可以在单个过滤器文档中的同一标识符上指定复合条件,例如以下示例:
有关示例,请参阅使用
| ||||||||||||||||||
| 文档或字符串 | Optional. A document or string that specifies the
索引(index) to use to support the 该选项可以采用索引规范文档或索引名称字符串。 如果指定不存在的索引,则操作出错。 For an example, see Specify | ||||||||||||||||||
| any | 可选。用户提供的待附加到该命令的注释。设置后,该注释将与该命令的记录一起出现在以下位置:
注释可以是任何有效的 BSON 类型(字符串、整型、对象、数组等)。 | ||||||||||||||||||
文档 |
输出
The findAndModify
command returns a document with the
following fields:
字段 | 类型 | 说明 |
---|---|---|
| 文档 | Contains the command's returned value. See |
| 文档 | Contains information about updated documents. See
|
| 数字 | Contains the command's execution status. |
lastErrorObject
The lastErrorObject
embedded document contains the following fields:
字段 | 类型 | 说明 |
---|---|---|
| 整型 | Contains the number of documents that matched the update predicate or the number of documents that the command inserted or deleted. |
| 布尔 | Contains
|
| 文档 | Contains the ObjectId of the inserted document if an |
value
For remove
operations, value
contains the removed document if
the query matches a document. If the query does not match a document to
remove, value
contains null
.
For update
operations, the value
embedded document contains the
following:
If the
new
parameter is not set or isfalse
:the pre-modification document if the query matches a document;
otherwise,
null
.
If
new
istrue
:the updated document if the query returns a match;
the inserted document if
upsert: true
and no document matches the query;otherwise,
null
.
行为
使用唯一索引进行 upsert
除非有唯一索引来防止重复,否则 upsert 可能会创建重复的文档。
以某一情况为例,其中不存在名为 Andy
的文档,且多个客户端大致会在同一时间发出以下命令:
db.runCommand( { findAndModify: "people", query: { name: "Andy" }, update: { $inc: { score: 1 } }, upsert: true } )
如果所有 findAndModify
操作在任何客户端成功插入数据之前完成了查询阶段,且 name
字段上没有唯一索引,则每个 findAndModify
操作都可能导致插入,从而使用 name: Andy
创建多个文档。
name
字段上的唯一索引可确保仅创建一个文档。通过此唯一索引,多个 findAndModify
操作此时便有如下行为:
只需执行一次
findAndModify
操作就能成功插入一个新文档。其他
findAndModify
操作要么更新新插入的文档,要么由于唯一键冲突而失败。为了让其他
findAndModify
操作更新新插入的文档,必须满足以下所有条件:目标collection具有会导致重复键错误的唯一索引。
更新操作不是
updateMany
或multi
是false
。更新匹配条件为:
单个相等谓词。例如
{ "fieldA" : "valueA" }
等式谓词的逻辑 AND。例如
{ "fieldA" : "valueA", "fieldB" : "valueB" }
相等谓词中的字段与唯一索引键模式中的字段匹配。
更新操作不会修改唯一索引键模式中的任何字段。
下表显示了upsert
操作的示例,当发生键冲突时,这些操作要么导致更新,要么失败。
唯一索引键模式 | 更新操作 | 结果 | ||||||
---|---|---|---|---|---|---|---|---|
|
| 匹配文档的 | ||||||
|
| 操作失败是因为它修改了唯一索引键模式 ( | ||||||
|
| 操作失败,因为等值谓词字段( |
分片集合
要在分片集合上使用 findAndModify
:
If you only target one shard, you can use a partial shard key in the
query
field or,You can provide an equality condition on a full shard key in the
query
field.从版本 7.1 开始,您不需要在查询规范中提供分片键 或
_id
字段。
分片集合中的文档可能缺少分片键字段。要定位缺失分片键的文档,可将 null
等值匹配与其他过滤条件(例如针对 _id
字段)结合使用。例如:
{ _id: <value>, <shardkeyfield>: null } // _id of the document missing shard key
分片键修改
您可以更新文档的分片键值,除非分片键字段是不可变的 _id
字段。
警告
分片集合中的文档可能缺少分片键字段。采取预防措施,避免在更改文档的分片键值时意外删除分片键。
To update the existing shard key value with
findAndModify
:
缺少分片键
Documents in a sharded collection can be
missing the shard key fields. To use
findAndModify
to set the document's missing shard
key:
提示
由于缺失的键值是作为 null 相等匹配的一部分返回的,因此为避免更新空值键,请酌情纳入其他查询条件(例如 _id
字段)。
另请参阅:
模式验证
findAndModify
命令新增了对 bypassDocumentValidation
选项的支持,从而允许您在附带验证规则的集合中插入或更新文档时绕过模式验证。
Comparisons with the update
Method
When updating a document, findAndModify
and the
updateOne()
method operate differently:
If multiple documents match the update criteria, for
findAndModify
, you can specify asort
to provide some measure of control on which document to update.updateOne()
updates the first document that matches.By default,
findAndModify
returns an object that contains the pre-modified version of the document, as well as the status of the operation. To obtain the updated document, use thenew
option.The
updateOne()
method returns aWriteResult()
object that contains the status of the operation.To return the updated document, use the
find()
method. However, other updates may have modified the document between your update and the document retrieval. Also, if the update modified only a single document but multiple documents matched, you will need to use additional logic to identify the updated document.
When modifying a single document, both findAndModify
and the
updateOne()
method atomically update the
document. See 原子性和事务 for more
details about interactions and order of operations of these methods.
另请参阅:
事务
findAndModify
可以在分布式事务中使用。
重要
在大多数情况下,与单文档写入操作相比,分布式事务会产生更高的性能成本,并且分布式事务的可用性不应取代有效的模式设计。在许多情况下,非规范化数据模型(嵌入式文档和数组)仍然是数据和使用案例的最佳选择。换言之,对于许多场景,适当的数据建模将最大限度地减少对分布式事务的需求。
有关其他事务使用注意事项(如运行时间限制和 oplog 大小限制),另请参阅生产注意事项。
在 ACID 事务中进行更新或插入 (upsert)
如果分布式事务不是跨分片写入事务,则可以在该事务中创建集合和索引。
具有 upsert: true
的
findAndModify
可以在现有集合或不存在的集合上运行。如果在不存在的集合上运行,该操作将创建集合。
另请参阅:
写关注和事务
如果是在事务中运行,则请勿显式设置此操作的写关注。要将写关注与事务一起使用,请参阅事务和写关注。
示例
Update and Return
The following command updates an existing document in the people
collection where the document matches the query
criteria:
db.runCommand( { findAndModify: "people", query: { name: "Tom", state: "active", rating: { $gt: 10 } }, sort: { rating: 1 }, update: { $inc: { score: 1 } } } )
This command performs the following actions:
The
query
finds a document in thepeople
collection where thename
field has the valueTom
, thestate
field has the valueactive
and therating
field has a valuegreater than
10.The
sort
orders the results of the query in ascending order. If multiple documents meet thequery
condition, the command will select for modification the first document as ordered by thissort
.The
update
increments
the value of thescore
field by 1.此命令将返回一个包含以下字段的文档:
The
lastErrorObject
field that contains the details of the command, including the fieldupdatedExisting
which istrue
, andThe
value
field that contains the original (i.e. pre-modification) document selected for this update:{ "lastErrorObject" : { "connectionId" : 1, "updatedExisting" : true, "n" : 1, "syncMillis" : 0, "writtenTo" : null, "err" : null, "ok" : 1 }, value" : { "_id" : ObjectId("54f62d2885e4be1f982b9c9c"), "name" : "Tom", "state" : "active", "rating" : 100, "score" : 5 }, "ok" : 1 }
To return the updated document in the value
field, add the
new:true
option to the command.
If no document match the query
condition, the command
returns a document that contains null
in the value
field:
{ "value" : null, "ok" : 1 }
mongosh
and many drivers
provide a findAndModify()
helper method.
Using the shell helper, this previous operation can take the
following form:
db.people.findAndModify( { query: { name: "Tom", state: "active", rating: { $gt: 10 } }, sort: { rating: 1 }, update: { $inc: { score: 1 } } } );
However, the findAndModify()
shell helper
method returns only the unmodified document, or if new
is
true
, the updated document.
{ "_id" : ObjectId("54f62d2885e4be1f982b9c9c"), "name" : "Tom", "state" : "active", "rating" : 100, "score" : 5 }
upsert: true
The following findAndModify
command includes the upsert:
true
option for the update
operation to either update a matching
document or, if no matching document exists, create a new document:
db.runCommand( { findAndModify: "people", query: { name: "Gus", state: "active", rating: 100 }, sort: { rating: 1 }, update: { $inc: { score: 1 } }, upsert: true } )
If the command finds a matching document, the command performs an update.
If the command does not find a matching document, the update
with upsert: true operation results in an insertion
and returns a document with the following fields:
The
lastErrorObject
field that contains the details of the command, including the fieldupserted
that contains the_id
value of the newly inserted document, andThe
value
field containingnull
.
{ "value" : null, "lastErrorObject" : { "updatedExisting" : false, "n" : 1, "upserted" : ObjectId("54f62c8bc85d4472eadea26f") }, "ok" : 1 }
Return New Document
The following findAndModify
command includes both
upsert: true
option and the new:true
option. The command either
updates a matching document and returns the updated document or, if no
matching document exists, inserts a document and returns the newly
inserted document in the value
field.
In the following example, no document in the people
collection
matches the query
condition:
db.runCommand( { findAndModify: "people", query: { name: "Pascal", state: "active", rating: 25 }, sort: { rating: 1 }, update: { $inc: { score: 1 } }, upsert: true, new: true } )
The command returns the newly inserted document in the value
field:
{ "lastErrorObject" : { "connectionId" : 1, "updatedExisting" : false, "upserted" : ObjectId("54f62bbfc85d4472eadea26d"), "n" : 1, "syncMillis" : 0, "writtenTo" : null, "err" : null, "ok" : 1 }, "value" : { "_id" : ObjectId("54f62bbfc85d4472eadea26d"), "name" : "Pascal", "rating" : 25, "state" : "active", "score" : 1 }, "ok" : 1 }
Sort and Remove
By including a sort
specification on the rating
field, the
following example removes from the people
collection a single
document with the state
value of active
and the lowest
rating
among the matching documents:
db.runCommand( { findAndModify: "people", query: { state: "active" }, sort: { rating: 1 }, remove: true } )
The command returns the deleted document:
{ "lastErrorObject" : { "connectionId" : 1, "n" : 1, "syncMillis" : 0, "writtenTo" : null, "err" : null, "ok" : 1 }, "value" : { "_id" : ObjectId("54f62a6785e4be1f982b9c9b"), "name" : "XYZ123", "score" : 1, "state" : "active", "rating" : 3 }, "ok" : 1 }
指定排序规则。
排序规则允许用户为字符串比较指定特定于语言的规则,例如字母大小写和重音符号规则。
集合 myColl
包含以下文档:
{ _id: 1, category: "café", status: "A" } { _id: 2, category: "cafe", status: "a" } { _id: 3, category: "cafE", status: "a" }
以下操作包括排序规则选项:
db.runCommand( { findAndModify: "myColl", query: { category: "cafe", status: "a" }, sort: { category: 1 }, update: { $set: { status: "Updated" } }, collation: { locale: "fr", strength: 1 } } )
该操作将返回以下文档:
{ "lastErrorObject" : { "updatedExisting" : true, "n" : 1 }, "value" : { "_id" : 1, "category" : "café", "status" : "A" }, "ok" : 1 }
使用 arrayFilters
执行数组更新操作
注意
arrayFilters
不适用于使用聚合管道的更新。
更新数组字段时,您可以指定 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 ] } ] )
To update all elements that are greater than or equal to 100
in the
grades
array, use the positional $[<identifier>]
operator with the arrayFilters
option:
db.runCommand( { findAndModify: "students", query: { grades: { $gte: 100 } }, update: { $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 ] }
更新文档数组的特定元素
注意
arrayFilters
不适用于使用聚合管道的更新。
使用以下文档创建集合 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 } ] } ] )
The following operation finds a document where the _id
field equals
1
and uses the filtered positional operator $[<identifier>]
with
the arrayFilters
to update the mean
for all elements in the
grades
array where the grade is greater than or equal to 85
.
db.runCommand( { findAndModify: "students2", query: { _id : 1 }, update: { $set: { "grades.$[elem].mean" : 100 } }, arrayFilters: [ { "elem.grade": { $gte: 85 } } ] } )
该操作更新单个文档的 grades
字段,完成该操作后,集合中将包含以下文档:
{ "_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 } ] }
使用聚合管道进行更新
findAndModify
can accept an aggregation pipeline for the update.
The pipeline can consist of the following stages:
$addFields
及其别名$set
$replaceRoot
及其别名$replaceWith
使用聚合分析管道可以进行更具表现力的更新声明,例如基于当前字段值的Express条件更新或使用另一个字段的值更新一个字段。
例如,使用以下文档创建名为 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 } ] } ] )
以下操作查找 _id
字段等于 1
的文档,并使用聚合管道根据 grades
字段计算新字段 total
的值:
db.runCommand( { findAndModify: "students2", query: { "_id" : 1 }, update: [ { $set: { "total" : { $sum: "$grades.grade" } } } ], new: true } )
操作完成后,集合有以下文档:
{ "_id" : 1, "grades" : [ { "grade" : 80, "mean" : 75, "std" : 6 }, { "grade" : 85, "mean" : 90, "std" : 4 }, { "grade" : 85, "mean" :85, "std" : 6 } ], "total" : 250 } { "_id" : 2, "grades" : [ { "grade" : 90, "mean" : 75, "std" : 6 }, { "grade" : 87, "mean" : 90, "std" : 3 }, { "grade" : 85, "mean" : 85,"std" : 4 } ] }
Specify hint
for findAndModify
Operations
在 mongosh
中,创建一个 members
集合,其中包含以下文档:
db.members.insertMany( [ { "_id" : 1, "member" : "abc123", "status" : "P", "points" : 0, "misc1" : null, "misc2" : null }, { "_id" : 2, "member" : "xyz123", "status" : "A", "points" : 60, "misc1" : "reminder: ping me at 100pts", "misc2" : "Some random comment" }, { "_id" : 3, "member" : "lmn123", "status" : "P", "points" : 0, "misc1" : null, "misc2" : null }, { "_id" : 4, "member" : "pqr123", "status" : "D", "points" : 20, "misc1" : "Deactivated", "misc2" : null }, { "_id" : 5, "member" : "ijk123", "status" : "P", "points" : 0, "misc1" : null, "misc2" : null }, { "_id" : 6, "member" : "cde123", "status" : "A", "points" : 86, "misc1" : "reminder: ping me at 100pts", "misc2" : "Some random comment" } ] )
在集合上创建以下索引:
db.members.createIndex( { status: 1 } ) db.members.createIndex( { points: 1 } )
The following operation explicitly hints to use the index
{ status: 1 }
:
db.runCommand({ findAndModify: "members", query: { "points": { $lte: 20 }, "status": "P" }, remove: true, hint: { status: 1 } })
注意
如果指定不存在的索引,则操作出错。
要查看使用的索引,请对操作运行 explain
:
db.runCommand( { explain: { findAndModify: "members", query: { "points": { $lte: 20 }, "status": "P" }, remove: true, hint: { status: 1 } }, verbosity: "queryPlanner" } )
在 let
中使用变量
版本 5.0 中的新增功能。
要定义可在命令中其他位置访问的变量,请使用 let 选项。
注意
要使用变量筛选结果,您必须在 $expr
操作符中访问该变量。
创建集合 cakeFlavors
:
db.cakeFlavors.insertMany( [ { _id: 1, flavor: "chocolate" }, { _id: 2, flavor: "strawberry" }, { _id: 3, flavor: "cherry" } ] )
The following example defines a targetFlavor
variable in let
and
uses the variable to change the cake flavor from cherry to orange:
db.cakeFlavors.runCommand( { findAndModify: db.cakeFlavors.getName(), query: { $expr: { $eq: [ "$flavor", "$$targetFlavor" ] } }, update: { flavor: "orange" }, let: { targetFlavor: "cherry" } } )