HOWTO · MongoDB
更新 MongoDB 中的多个文档
在本文中,updateMany() 方法将用于一次更新多个文档。此方法更新与指定过滤器匹配的所有集合文档。
本页内容
使用 MongoDB 的 updateMany() 方法,可以将同一项更新应用于所有符合筛选条件的文档。本指南使用 products 集合演示如何更新标量字段、嵌入式文档和数组位置,并说明如何读取操作结果。
updateMany() 的工作方式
db.collection.updateMany() 方法接受筛选条件、更新文档,以及一个可选的选项文档。它会对筛选条件选出的所有文档应用更新。使用本指南中的示例时,该方法不会更改文档的 _id;更新也可以添加原先不存在的字段。
这些示例中的更新文档使用 $set 等更新运算符。此运算符形式不同于聚合管道更新;本指南不涉及管道更新。
理解筛选条件、更新和选项
第一个参数 filter 使用与 find() 相同的查询文档格式来选择文档。例如,{ price: 899 } 会选出 price 为 899 的文档;{} 则会匹配集合中的所有文档,因此只有在确实要覆盖如此广的范围时才使用空筛选条件。
第二个参数 update 用来描述要进行的更改。更新文档使用 $set 等运算符修改字段。可选的第三个参数可以包含 upsert,其默认值为 false。如果 upsert 为 true 且没有文档匹配筛选条件,MongoDB 会根据筛选条件和更新内容插入一个文档。如果有文档匹配,则会更新这些匹配项。
使用 $set 可为字段赋值;如果该字段不存在,也会创建它。点表示法可指定嵌入式文档中的字段:$set: { "specs.ram": 32 } 会更改 specs 中的 ram,同时保留该嵌入式文档中的其他字段。这与直接设置 specs 不同;直接设置会替换整个字段的值。
下面的小型 JavaScript 模型用普通对象演示相同的筛选和赋值思路;它不会调用 MongoDB:
本节中的代码以及显示的标签或输出仍为英文。
const products = [
{ _id: 1, name: "xPhone", price: 799 },
{ _id: 2, name: "xTablet", price: 899 },
{ _id: 3, name: "SmartTablet", price: 899 }
];
const matched = products.filter(product => product.price === 899);
for (const product of matched) product.price = 895;
console.log(JSON.stringify(matched));
准备 products 集合
以下文档构成一个一致的示例数据集,供后续 MongoDB shell 操作使用。每个文档都包含价格、specs 嵌入式文档和 storage 数组。
在一个新的 products 集合中,使用 db.products.insertMany([...]) 插入以下文档:
{ _id: 1, name: "xPhone", price: 799, specs: { ram: 4, screen: 6.5, cpu: 2.66 }, storage: [64, 128, 256] }{ _id: 2, name: "xTablet", price: 899, specs: { ram: 16, screen: 9.5, cpu: 3.66 }, storage: [128, 256, 512] }{ _id: 3, name: "SmartTablet", price: 899, specs: { ram: 12, screen: 9.7, cpu: 3.66 }, storage: [16, 64, 128] }{ _id: 4, name: "SmartPad", price: 699, specs: { ram: 8, screen: 9.7, cpu: 1.66 }, storage: [128, 256, 1024] }{ _id: 5, name: "SmartPhone", price: 599, specs: { ram: 4, screen: 5.7, cpu: 1.66 }, storage: [128, 256] }
以下示例假设这些记录已经存在,并且每项操作都针对集合的当前状态执行。如果多次运行标量字段更新,请重置示例数据,或调整筛选条件以匹配当前值。
更新匹配的文档
要将当前价格为 899 的所有产品改价,请调用 db.products.updateMany({ price: 899 }, { $set: { price: 895 } })。筛选条件会选出文档 2 和 3,而 $set 会将每个匹配文档的 price 字段设为 895。
可以使用 db.products.find({ price: 895 }, { name: 1, price: 1 }) 验证更改后的值。投影会请求产品名称和价格,让查询结果集中显示本次更新涉及的字段。对于示例数据,应该会返回这两种产品。
更新嵌入式文档
使用点表示法,可以更新 specs 内的字段,而不必替换整个嵌入式文档。例如,db.products.updateMany({ price: { $gt: 700 } }, { $set: { "specs.ram": 32, "specs.screen": 9.8, "specs.cpu": 5.66 } }) 会选出示例数据中的产品 1、2 和 3,并在它们现有的 specs 文档中设置这三个值。
筛选条件使用示例数据中存在的 price 字段,更新路径则使用与嵌入式文档字段名一致的 specs。使用 db.products.find({ price: { $gt: 700 } }, { name: 1, price: 1, specs: 1 }) 检查选中的产品及其 specs 值。
更新数组元素
MongoDB 数组索引从零开始。要更改 _id 值为 1、2 和 3 的产品中 storage 数组的前两个位置,请使用 db.products.updateMany({ _id: { $in: [1, 2, 3] } }, { $set: { "storage.0": 16, "storage.1": 32 } })。点分路径指定位置 0 和 1;后续元素保持不变。
使用 db.products.find({ _id: { $in: [1, 2, 3] } }, { name: 1, storage: 1 }) 检查受影响的文档。此查询会返回同三种产品的名称和数组,方便查看已更改的位置。
查看更新结果和选项
updateMany() 会返回一个结果文档。其中的 matchedCount 表示匹配筛选条件的文档数,modifiedCount 表示实际更改的文档数。如果匹配的文档已经具有所请求的值,这两个数量可能不同。对于插入了新文档的 upsert,upsertedId 会标识所插入的文档;如果没有插入文档,该字段就不会提供新插入文档的标识符。
可以用这些数量检查操作范围:将 matchedCount 与预期筛选条件对照,再使用后续的 find() 查询检查已更改的字段。结果文档描述更新操作,而查询可用于验证文档中实际存储的值。