掘金 后端 ( ) • 2021-07-05 14:35
.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:15px;overflow-x:hidden;color:#333}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{line-height:1.5;margin-top:35px;margin-bottom:10px;padding-bottom:5px}.markdown-body h1{font-size:30px;margin-bottom:5px}.markdown-body h2{padding-bottom:12px;font-size:24px;border-bottom:1px solid #ececec}.markdown-body h3{font-size:18px;padding-bottom:0}.markdown-body h4{font-size:16px}.markdown-body h5{font-size:15px}.markdown-body h6{margin-top:5px}.markdown-body p{line-height:inherit;margin-top:22px;margin-bottom:22px}.markdown-body img{max-width:100%}.markdown-body hr{border:none;border-top:1px solid #ddd;margin-top:32px;margin-bottom:32px}.markdown-body code{word-break:break-word;border-radius:2px;overflow-x:auto;background-color:#fff5f5;color:#ff502c;font-size:.87em;padding:.065em .4em}.markdown-body code,.markdown-body pre{font-family:Menlo,Monaco,Consolas,Courier New,monospace}.markdown-body pre{overflow:auto;position:relative;line-height:1.75}.markdown-body pre>code{font-size:12px;padding:15px 12px;margin:0;word-break:normal;display:block;overflow-x:auto;color:#333;background:#f8f8f8}.markdown-body a{text-decoration:none;color:#0269c8;border-bottom:1px solid #d1e9ff}.markdown-body a:active,.markdown-body a:hover{color:#275b8c}.markdown-body table{display:inline-block!important;font-size:12px;width:auto;max-width:100%;overflow:auto;border:1px solid #f6f6f6}.markdown-body thead{background:#f6f6f6;color:#000;text-align:left}.markdown-body tr:nth-child(2n){background-color:#fcfcfc}.markdown-body td,.markdown-body th{padding:12px 7px;line-height:24px}.markdown-body td{min-width:120px}.markdown-body blockquote{color:#666;padding:1px 23px;margin:22px 0;border-left:4px solid #cbcbcb;background-color:#f8f8f8}.markdown-body blockquote:after{display:block;content:""}.markdown-body blockquote>p{margin:10px 0}.markdown-body ol,.markdown-body ul{padding-left:28px}.markdown-body ol li,.markdown-body ul li{margin-bottom:0;list-style:inherit}.markdown-body ol li .task-list-item,.markdown-body ul li .task-list-item{list-style:none}.markdown-body ol li .task-list-item ol,.markdown-body ol li .task-list-item ul,.markdown-body ul li .task-list-item ol,.markdown-body ul li .task-list-item ul{margin-top:0}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-top:3px}.markdown-body ol li{padding-left:6px}.markdown-body .contains-task-list{padding-left:0}.markdown-body .task-list-item{list-style:none}@media (max-width:720px){.markdown-body h1{font-size:24px}.markdown-body h2{font-size:20px}.markdown-body h3{font-size:18px}}

前言

在实际开发维护过程中我们可能会遇到需要刷数据的情况,例如修改数据类型,枚举值的变更等。对于数据集较小的操作可以使用循环一个个修改。例如,假定我们之前员工的年龄都录错了,现在每个员工的年龄需要增加1,这个时候可以使用数据集的快照(snapshot)进行逐个更新,就像下面这样:

db.employees.find({age: {$exists: true, $type: 'number'}})
    ._addSpecial( "$snapshot", true).forEach(function(doc) {
  var newAge = doc.age + 1;
  db.employees.updateOne({_id: doc._id}, {$set: {age: newAge}});
});
复制代码

其中下面的代码是告知 MongoDB 使用快照方式一次性读取完整个数据集。

db.employees.find()._addSpecial( "$snapshot", true)
复制代码

注意的是,在3.x 版本中还有其他两种方式,但是在4.x 版本后已经无法使用。

db.employees.find().snapshot();
db.employees.find({$query: {}, $snapshot: true});
复制代码

这种方式显然很低效,如果数据集过大没法执行,因此 MongoDB 推出了批量操作 API。

批量操作

批量操作可以支持按批量写入的方式一次同步多条数据到服务端。这使得在 forEach 循环中不需要每次都向服务端写入数据。批量操作的代码如下所示:

var snapshot = db.employees.find({age: {$exists: true, $type: 'number'}})
    ._addSpecial( "$snapshot", true);
bulkUpadteOps = [];

snapshot.forEach(function(doc) {
var newAge = doc.age + 1;
bulkUpadteOps.push({
'updateOne': {
'filter': {'_id': doc._id},
'update': {$set: {age: newAge}}
}
});

if (bulkUpadteOps.length === 100) {
db.employees.bulkWrite(bulkUpadteOps);
bulkUpadteOps = [];
}
});

if (bulkUpadteOps.length > 0) {
db.employees.bulkWrite(bulkUpadteOps);
}
复制代码

方式就是将需要进行的操作先存入到一个批量操作的数组中,当积累到一定量时再调用 bulkWrite 批量执行对应的操作。最后,有可能数量不是设定批量操作数量的整数倍,因此若还剩余操作未执行,再执行一次即可完成所有的批量更新操作。

$type指定查询数据类型

在查找快照数据时使用了$type 操作符,用于查找匹配的数据类型,$type 可以使用字符串或数字指定数据类型,这些数据类型都是 MongoDB 内置的数据类型,如下所示。其中 number 代表数值类型的别名,包括了如下类型:

  • Double
  • 32-bit integer
  • 64-bit integer
  • decimal
类型数字别名备注Double1"double"String2"string"Object3"object"Array4"array"Binary data5"binData"Undefined6"undefined"已废弃。ObjectId7"objectId"Boolean8"bool"Date9"date"Null10"null"Regular Expression11"regex"DBPointer12"dbPointer"已废弃。JavaScript13"javascript"Symbol14"symbol"已废弃。JavaScript code with scope15"javascriptWithScope"4.4版本后废弃32-bit integer16"int"Timestamp17"timestamp"64-bit integer18"long"Decimal12819"decimal"3.4版本以后新增Min key-1"minKey"Max key127"maxKey"

具体可以参考官网文档:MongoDB $type 操作符

总结

本篇介绍了 MongoDB 提供的批量操作 API,借助批量操作 API 可以在维护数据库时提高效率,例如变更数据类型,批量调整数据值等。