这里的解决方案概念是使用游标遍历您的集合,并为游标内的每个文档收集有关Ancestors 数组元素的索引位置的数据。
您稍后将在循环中将此数据用作更新操作参数,以正确识别要更新的元素。
假设你的集合不是那么庞大,上面的直觉可以使用光标的 forEach() 方法来实现,就像你在尝试进行迭代并获取索引数据时所做的那样涉及的所有数组元素。
以下演示了这种用于小型数据集的方法:
function isValidHexStr(id) {
var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
if(id == null) return false;
if(typeof id == "string") {
return id.length == 12 || (id.length == 24 && checkForHexRegExp.test(id));
}
return false;
};
db.users.find({"Ancestors.0": { "$exists": true, "$type": 2 }}).forEach(function(doc){
var ancestors = doc.Ancestors,
updateOperatorDocument = {};
for (var idx = 0; idx < ancestors.length; idx++){
if(isValidHexStr(ancestors[idx]))
updateOperatorDocument["Ancestors."+ idx] = ObjectId(ancestors[idx]);
};
db.users.updateOne(
{ "_id": doc._id },
{ "$set": updateOperatorDocument }
);
});
现在为了提高性能,尤其是在处理大型集合时,请利用 Bulk() API 批量更新集合。
与上述操作相比,这非常有效,因为使用 bulp API,您将分批将操作发送到服务器(例如,批量大小为 1000),这会给您带来更好的效果
性能,因为您不会向服务器发送每个请求,而是每 1000 个请求中发送一次,从而使您的更新更加高效和快捷。
以下示例演示如何使用 MongoDB 版本 >= 2.6 和 < 3.2 中提供的 Bulk() API。
function isValidHexStr(id) {
var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
if(id == null) return false;
if(typeof id == "string") {
return id.length == 12 || (id.length == 24 && checkForHexRegExp.test(id));
}
return false;
};
var bulkUpdateOps = db.users.initializeUnorderedBulkOp(),
counter = 0;
db.users.find({"Ancestors.0": { "$exists": true, "$type": 2 }}).forEach(function(doc){
var ancestors = doc.Ancestors,
updateOperatorDocument = {};
for (var idx = 0; idx < ancestors.length; idx++){
if(isValidHexStr(ancestors[idx]))
updateOperatorDocument["Ancestors."+ idx] = ObjectId(ancestors[idx]);
};
bulkUpdateOps.find({ "_id": doc._id }).update({ "$set": updateOperatorDocument })
counter++; // increment counter for batch limit
if (counter % 1000 == 0) {
// execute the bulk update operation in batches of 1000
bulkUpdateOps.execute();
// Re-initialize the bulk update operations object
bulkUpdateOps = db.users.initializeUnorderedBulkOp();
}
})
// Clean up remaining operation in the queue
if (counter % 1000 != 0) { bulkUpdateOps.execute(); }
下一个示例适用于新的 MongoDB 版本 3.2,它从 deprecated 开始使用 Bulk() API,并使用 bulkWrite() 提供了一组更新的 API。
它使用与上面相同的游标,但使用相同的 forEach() 游标方法创建具有批量操作的数组,以将每个批量写入文档推送到数组。因为写入命令可以接受不超过 1000 次操作,所以您需要将操作分组为最多 1000 次操作,并在循环达到 1000 次迭代时重新初始化数组:
var cursor = db.users.find({"Ancestors.0": { "$exists": true, "$type": 2 }}),
bulkUpdateOps = [];
cursor.forEach(function(doc){
var ancestors = doc.Ancestors,
updateOperatorDocument = {};
for (var idx = 0; idx < ancestors.length; idx++){
if(isValidHexStr(ancestors[idx]))
updateOperatorDocument["Ancestors."+ idx] = ObjectId(ancestors[idx]);
};
bulkUpdateOps.push({
"updateOne": {
"filter": { "_id": doc._id },
"update": { "$set": updateOperatorDocument }
}
});
if (bulkUpdateOps.length == 1000) {
db.users.bulkWrite(bulkUpdateOps);
bulkUpdateOps = [];
}
});
if (bulkUpdateOps.length > 0) { db.users.bulkWrite(bulkUpdateOps); }