MongoDB的findAndModify
文章目录
db.collection.findAndModify()
定义
findAndModify()方法具有以下形式:
|
|
该db.collection.findAndModify()方法采用带有以下嵌入式文档字段的文档参数:
参数 | 类型 | 描述 |
---|---|---|
query | 文件 | 可选的。修改的选择标准。该query字段和db.collection.find()方法采用相同的查询选择器.db.collection.findAndModify()尽管查询可能匹配多个文档,但只会选择一个文档进行修改。如果未指定,则默认为空文档。从MongoDB 4.2(和4.0.12 +,3.6.14 +和3.4.23+)开始,如果查询参数不是文档,则操作错误。 |
sort | 文件 | 可选的。确定如果查询选择多个文档,则操作将修改哪个文档。db.collection.findAndModify()按照此参数指定的排序顺序修改第一个文档。从MongoDB 4.2(和4.0.12 +,3.6.14 +和3.4.23+)开始,如果sort参数不是文档,则操作会出错。 |
remove | 布尔值 | 必须指定remove或update字段。删除在query字段中指定的文档。设置此项以true 删除选定的文档。默认值为false。 |
update | 文件或数组 | 必须指定remove或update字段。执行所选文档的更新。如果传递带有update运算符表达式的文档,则db.collection.findAndModify()执行指定的修改。如果通过了替换文件,则执行替换。{ <field1>: <value1>, ...} .在MongoDB中4.2开始,如果通过一个汇聚管道 , 修改每个管道中的文档。管道可以包括以下阶段:[ <stage1>, <stage2>, ... ] ,$addFields 及其别名 , $set ,$project 及其别名 $unset ,$replaceRoot 及其别名$replaceWith 。 |
new | 布尔值 | 可选的。当为true时,返回修改后的文档而不是原始文档。若remove为true,new选项被忽略。默认值为false。 |
fields | 文献 | 可选的。要返回的字段子集。该fields文档使用来指定包含一个字段,见投影。fields: { <field1>: 1, <field2>: 1, ... } .从MongoDB 4.2(和4.0.12 +,3.6.14 +和3.4.23+)开始,如果fields参数不是文档,则操作错误。 |
upsert | 布尔值 | 可选的。与update现场配合使用。当true,findAndModify()如果没有文档与匹配,则创建一个新文档query。更新与匹配的单个文档query。为避免多次更新,请确保query字段被唯一索引。默认为false。 |
bypassDocumentValidation | 布尔值 | 可选的。允许db.collection.findAndModify在操作过程中绕过文档验证。这使您可以更新不满足验证要求的文档。3.2版中的新功能。 |
writeConcern | 文献 | 可选的。表达书面关切的文件。省略使用默认的写关注。如果在事务中运行,则不要为操作明确设置写关注点。要对事务使用写关注,请参见 事务和写关注。3.2版中的新功能。 |
maxTimeMS | 整数 | 可选的。指定用于处理操作的时间限制(以毫秒为单位)。 |
collation | 文档 | 可选的。指定 用于操作的排序规则。归类允许用户为字符串比较指定特定于语言的规则,例如字母大写和重音符号的规则。排序规则选项具有以下语法:{locale: <string>,caseLevel: <boolean>,caseFirst: <string>,strength: <int>,numericOrdering: <boolean>,alternate: <string>,maxVariable: <string>,backwards: <boolean>} 指定排序规则时,该locale字段为必填字段;所有其他排序规则字段都是可选的。有关字段的说明,请参见整理文档。如果未指定排序规则,但是集合具有默认排序规则(请参阅参考资料db.createCollection()),则该操作将使用为集合指定的排序规则。如果没有为集合或操作指定排序规则,则MongoDB使用先前版本中使用的简单二进制比较进行字符串比较。您不能为一个操作指定多个排序规则。例如,您不能为每个字段指定不同的排序规则,或者如果对排序执行查找,则不能对查找使用一种排序规则,而对排序使用另一种排序规则。 |
arrayFilters | 数组 | 可选的。筛选器文档数组,用于确定要对数组字段进行更新操作要修改的数组元素。在更新文档中,使用$[<identifier>] 过滤后的位置运算符定义一个标识符,然后在数组过滤器文档中引用该标识符。如果该标识符未包含在更新文档中,则不能具有标识符的数组过滤器文档。注意,在<identifier> 必须以小写字母开头,并且只包含字母数字字符。您可以在更新文档中多次包含相同的标识符;但是,对于$[identifier] 更新文档中的每个不同的标识符(),必须精确地指定一个 对应的数组过滤器文档。也就是说,您不能为同一标识符指定多个数组过滤器文档。 |
返回数据
对于删除操作,如果查询与文档匹配,则 findAndModify()返回删除的文档。如果查询与要删除的文档不匹配,则 findAndModify()返回null。
对于更新操作,findAndModify()返回以下之一:
- 如果new参数未设置或为false:
- 如果查询与文档匹配,则为修改前文档;
- 否则,null。
- 如果new是true:
- 如果查询返回匹配项,则修改后的文档;
- 如果没有文档匹配查询且upsert: true,则插入文档;
- 否则,null。
特性
Upsert和唯一索引
当findAndModify()包含选项upsert: true并且查询字段不是唯一索引时,该方法在某些情况下可以多次插入文档。
在以下示例中,不存在具有名称Andy的文档,并且多个客户端发出以下命令:
|
|
然后,如果这些客户机的findAndModify() 方法query在任何命令开始阶段之前完成了该 modify阶段,并且该name 字段上没有唯一索引,则这些命令可能全部执行upsert,从而创建多个重复文档。
为防止创建多个具有相同名称的重复文档,请在 字段上创建唯一索引name。有了此唯一索引,多种方法将表现出以下特性之一:
- 恰好findAndModify() 成功插入了一个新文档。
- 零个或多个findAndModify()方法将更新新插入的文档。
- findAndModify()尝试插入相同名称的文档时,零个或多个方法失败。如果该方法由于在name字段上违反唯一索引约束而失败 ,则可以重试该方法。如果没有删除文档,重试应该不会失败。
分片集合
当使用findAndModify在分片环境中,query 必须包含关于平等条件 片键针对用于分片的集群中的所有操作分片集合。
findAndModify针对非分片集合的mongos 实例发出的操作正常运行。
从MongoDB 4.2开始,除非分片键字段是不可变_id字段,否则您可以更新文档的分片键值。
在MongoDB 4.2之前,文档的分片键字段值是不可变的。
文档验证
该db.collection.findAndModify()方法增加了对该bypassDocumentValidation选项的支持 ,使您可以在使用验证规则向集合中插入或更新文档时绕过文档验证。
与update方法的比较
当更新一个文件,db.collection.findAndModify()和update()方法有不同操作:
- 默认情况下,这两种操作都会修改单个文档。但是update()方法及其multi选项可以修改多个文档。
- 如果多个文档符合的更新条件,则 db.collection.findAndModify()可以指定sort来提供对要更新的文档的某种控制措施。
- 使用该update() 方法的默认特性,当多个文档匹配时,您无法指定要更新哪个文档。
- 默认情况下,db.collection.findAndModify()返回文档的预修改版本。要获取更新的文档,请使用new选项。
- 该update()方法返回一个 WriteResult包含操作状态的对象。若要返回更新的文档,请使用find() 方法。但是,其他更新可能已在您的更新和文档检索之间修改了文档。另外,如果更仅修改了一个文档,但匹配了多个文档,则您将需要使用其他逻辑来标识更新后的文档。
- 修改单个文档时,两者db.collection.findAndModify()和 update()方法都会原子性的更新文档。
事务
db.collection.findAndModify()可以在多文档事务中使用。
重要
在大多数情况下,与单文档写入相比,多文档事务产生的性能成本更高,并且多文档事务的可用性不应代替有效的架构设计。在许多情况下, 非规范化数据模型(嵌入式文档和数组)对于您的数据和用例将继续是最佳的。也就是说,在许多情况下,对数据进行适当的建模将最大程度地减少对多文档事务的需求。
现有的集合和事务
在事务内部,您可以指定对现有集合的读/写操作。如果db.collection.findAndModify()使用upsert,则该集合必须已经存在。
写关注和事务
如果在事务中运行,则不要为操作明确设置写关注点。要对事务使用写关注,请参见 事务和写关注。
例子
更新和返回
以下方法更新并返回人员集合中与查询条件相匹配的现有文档:
|
|
此方法执行以下操作:
- 在query找到的文件people,其中收集name字段的值Tom,该state字段值为active和rating字段的值大于10
- 该sort订单升序排序查询的结果。如果多个文档满足query条件,则该方法将选择按此命令修改的第一个文档sort。
- 用1更新字段increments的score值。
- 该方法返回为此更新选择的原始(即预先修改)文档:
1 2 3 4 5 6 7
{ "_id" : ObjectId("50f1e2c99beb36a0f45c6453"), "name" : "Tom", "state" : "active", "rating" : 100, "score" : 5 }
要返回修改后的文档,请将new:true选项添加到方法中。
如果没有文档符合query条件,则该方法返回null。
UPSERT
以下方法包括用于 更新匹配文档或如果不存在匹配文档的话,创建新文档的操作选项:upsert: true.
|
|
如果该方法找到匹配的文档,则该方法将执行更新。
如果该方法没有找到匹配的文件,该方法将创建一个新文档。由于该方法包括该sort选项,因此它返回一个空文档作为原始(预修改)文档:{ }
|
|
如果该方法不包含sort选项,则该方法返回 null。
|
|
返回新建文档
以下方法同时包含new:true选项和upsert: true选项。该方法或者更新匹配的文档并返回更新的文档,或者,如果不存在匹配的文档,则插入文档并在字段中返回新插入的文档
在以下示例中,people集合中没有任何文档符合query条件:
|
|
该方法返回新插入的文档:
|
|
排序和删除
通过sort在rating字段中包含一个规范,以下示例从people集合中删除单个文档,该单个文档的state值active等于rating匹配文档中的最低值:
|
|
该方法返回已删除的文档:
|
|
指定归类
3.4版的新功能。
归类允许用户为字符串比较指定特定于语言的规则,例如字母大写和重音符号的规则。
集合myColl包含以下文档:
|
|
以下操作包括排序规则 选项:
|
|
该操作返回以下文档:
|
|
指定arrayFilters更新数组
注意:arrayFilters 不适用于使用聚合管道的更新。
3.6版的新功能。
从MongoDB 3.6开始,在更新数组字段时,您可以指定arrayFilters确定要更新的数组元素。
更新元素匹配arrayFilters条件
使用以下文档创建一个集合:
|
|
要修改大于或等于所有元素100中 grades数组,使用 $[<identifier>]
与arrayFilters
在选项 db.collection.findAndModify方法:
|
|
该操作将更新grades单个文档的字段,并且在操作之后,集合将包含以下文档:
|
|
更新文档数组的特定元素
使用以下文档创建一个集合:
|
|
以下操作将查找一个文档,其中该_id字段等于1,并使用带过滤后的位置运算符$[<identifier>]
和arrayFilters
来修改grades数组中所有grade大于或等于85的元素的mean。
|
|
该操作将更新grades单个文档的字段,并且在操作之后,集合将包含以下文档:
|
|
使用聚合管道进行更新
从MongoDB 4.2开始,db.collection.findAndModify()可以接受聚合管道进行更新。管道可以包括以下阶段:
$addFields
及其别名$set
$project
及其别名$unset
$replaceRoot
及其别名$replaceWith
。
使用聚合管道可以实现更具表达力的更新语句,例如根据当前字段值表达条件更新,或使用另一个字段的值更新一个字段。
例如,students2使用以下文档创建一个集合:
|
|
以下操作将查找一个_id字段等于1的文档,并使用聚合管道total从该grades字段中计算一个新字段:
|
|
注意
该$set
管道中的使用是指聚集阶段$set
,而不是更新操作$set
。
该操作返回更新的文档:
|
|
文章作者 Forz
上次更新 2020-05-12