使用 Mongoose 在 MongoDB 中更新许多记录的正确方法是啥
Posted
技术标签:
【中文标题】使用 Mongoose 在 MongoDB 中更新许多记录的正确方法是啥【英文标题】:What is the right approach to update many records in MongoDB using Mongoose使用 Mongoose 在 MongoDB 中更新许多记录的正确方法是什么 【发布时间】:2016-12-09 02:37:10 【问题描述】:我正在使用 Mongoose 从 MongoDB 中提取一些记录,将它们导入另一个系统,然后我想将所有这些文档的状态(文档属性)设置为 processed
。
我可以找到这个解决方案:Update multiple documents by id set. Mongoose
我想知道这是否是正确的方法,建立一个包含所有文档 ID 的标准,然后执行更新。还请考虑一个事实,即它将是许多文件。
(更新查询的限制是什么?到处都找不到。官方文档:http://mongoosejs.com/docs/2.7.x/docs/updating-documents.html)
【问题讨论】:
猫鼬支持Model.bulkWrite()
【参考方案1】:
建立一个包含所有文档 ID 的标准然后执行更新的方法必然会导致潜在的问题。当您使用每个文档迭代发送更新操作的文档列表时,在 Mongoose 中,您冒着炸毁服务器的风险,尤其是在处理大型数据集时,因为您无需等待异步调用完成,然后再继续下一个迭代。您将实质上构建一个未解决操作的“堆栈”,直到这导致问题 - ***。
例如,假设您有一个文档 ID 数组,您想更新状态字段上的匹配文档:
const processedIds = [
"57a0a96bd1c6ef24376477cd",
"57a052242acf5a06d4996537",
"57a052242acf5a06d4996538"
];
您可以在哪里使用updateMany()
方法
Model.updateMany(
_id: $in: processedIds ,
$set: status: "processed" ,
callback
);
或者对于非常小的数据集,您可以在数组上使用 forEach()
方法来迭代它并更新您的集合:
processedIds.forEach(function(id))
Model.update( _id: id, $set: status: "processed" , callback);
);
上述方法适用于小型数据集。但是,当您面对成千上万个要更新的文档时,这会成为一个问题,因为您将在循环中重复服务器调用异步代码。
要克服这个问题,请使用异步的 eachLimit
之类的方法,并遍历数组,为每个项目执行 MongoDB 更新操作,同时永远不会同时执行超过 x 个并行更新。
最好的方法是为此使用批量 API,这在批量处理更新方面非常有效。性能与对众多文档中的每一个都调用更新操作的区别在于,批量 API 不是在每次迭代时向服务器发送更新请求,而是在每 1000 个请求(批处理)中发送一次请求。
对于支持 MongoDB Server 3.2.x
的 Mongoose 版本 >=4.3.0
,您可以使用 bulkWrite()
进行更新。下面的例子展示了如何去做:
const bulkUpdateCallback = function(err, r)
console.log(r.matchedCount);
console.log(r.modifiedCount);
// Initialize the bulk operations array
const bulkUpdateOps = [], counter = 0;
processedIds.forEach(function (id)
bulkUpdateOps.push(
updateOne:
filter: _id: id ,
update: $set: status: "processed"
);
counter++;
if (counter % 500 == 0)
// Get the underlying collection via the Node.js driver collection object
Model.collection.bulkWrite(bulkUpdateOps, ordered: true, w: 1 , bulkUpdateCallback);
bulkUpdateOps = []; // re-initialize
)
// Flush any remaining bulk ops
if (counter % 500 != 0)
Model.collection.bulkWrite(bulkOps, ordered: true, w: 1 , bulkUpdateCallback);
对于支持 MongoDB Server >=2.6.x
的 Mongoose 版本 ~3.8.8
、~3.8.22
、4.x
,您可以使用 Bulk API,如下所示
var bulk = Model.collection.initializeOrderedBulkOp(),
counter = 0;
processedIds.forEach(function(id)
bulk.find( "_id": id ).updateOne(
"$set": "status": "processed"
);
counter++;
if (counter % 500 == 0)
bulk.execute(function(err, r)
// do something with the result
bulk = Model.collection.initializeOrderedBulkOp();
counter = 0;
);
);
// Catch any docs in the queue under or over the 500's
if (counter > 0)
bulk.execute(function(err,result)
// do something with the result here
);
【讨论】:
您介意告诉我bulkWrite
与insertMany
有何不同吗?
或者collection.insert
与collection.bulkWrite
有何不同?我似乎找不到关于这些东西的任何官方文档 :( 参考:unknownerror.org/opensource/Automattic/mongoose/q/***/…
insertMany()
是在 Mongoose 4.4 及更高版本中使用 mongodb 驱动程序进行批量写入的新方法,而 bulkWrite()
将在未来的某个时候得到支持 #3998。基本上,insertMany
在后台使用Model.collection.insertMany()
。我可以确定的主要区别是bulkWrite()
方法提供了执行批量插入、更新和删除操作的能力,而insertMany()
只支持批量插入操作。 Doc reference.
这很好用。我添加的唯一内容是在 bulkUpdateCallback 计算所有行都已处理时对调用函数的最终回调。否则很难将这些计数器合并到其他迭代模式中,因为它们到达太晚而无法返回。我发现异步模式通常如何为批量/迭代过程带来挑战,这很有趣——这是一个很棒的混合解决方案。
这种方法真的很有帮助......我花了几天时间寻找正确的解决方案......这对我有用......谢谢...... :)【参考方案2】:
要更新许多记录,据我所知,$in 是最好的选择。
db.collectionName.updateMany(
_id:
$in:
[
ObjectId("your object id"),
ObjectId("your object id")
]
,
$inc: quantity: 100
)
我想再补充一点,你可以使用$in来获取多个文档
db.collectionName.find(
_id:
$in:
[
ObjectId("your object id"),
ObjectId("your object id")
]
)
【讨论】:
这是 MongoDB,不是 mongoose,$in 已经讨论过 in the top answer。这个答案究竟对现有答案增加了什么?【参考方案3】:您可以在更新查询中使用 multi: true
选项进行批量更新。
示例:
employees.update( _id: $gt: 3 ,$inc: sortOrder: -1 ,'multi':true);
mongoose中的上述代码等价于mongodb中的以下代码:
db.employees.updateMany( _id: $gt: 3 ,$inc: sortOrder: -1 );
【讨论】:
以上是关于使用 Mongoose 在 MongoDB 中更新许多记录的正确方法是啥的主要内容,如果未能解决你的问题,请参考以下文章
如何在 mongodb/mongoose 的嵌套数组中检查是不是存在并更新对象?
如何在 mongoose 和 mongodb 中更新嵌套模型