前言
在执行mongo操作时,有时候大家会觉得力不从心,比如:要给大量的数据更新,但是各个数据更新的内容不一样;需要批量创建大量数据;
以上操作,如果单纯使用findIOneAndUpdate或者save,首先是非常耗时、其次使用了大量的资源;
那么有没有什么好的方法去替代呢?
mongoose提供了bulkWrite批量操作防范,这个方法支持批量插入、更新和删除;
当然,不会有人以为批量操作就不能单独操作一条数据吧!
一: bulkWrite小小解释
mongoose关于bulkWrite的原文:https://mongoosejs.com/docs/api.html#model_Model-bulkWrite
操作语法:
db.collection.bulkWrite(
[ <operation 1>, <operation 2>, ... ],
{
writeConcern : <document>,
ordered : <boolean>
}
)
参数 | 类型 | 是否必填 | 描述 |
---|---|---|---|
operations | array | 必填 | bulkWrite() 写操作的数组。支持操作:insertOne、updateOne、updateMany、deleteOne、deleteMany、replaceOne |
writeConcern | document | 选填 | write concern 文档,省略则使用默认的 write concern,一般无用 |
ordered | boolean | 选填 | 表示mongod实例有序还是无序执行操作。默认值true。 |
关于ordered
- 如果为true,有序执行,按顺序执行并在第一个错误处停止,遇到错误就停止执行后续的operations操作(后续的操作不会被执行)
- 如果为false,无序执行,为并行执行,如果遇到错误,会继续执行在一个操作,直到所有操作成功或出错;
综上所述,个人建议使用时配置为无序执行;各位可以按需求使用,后续会具体说明各项操作及返回值
二:具体操作
insertOne
插入单个文档到集合中;
db.collection.bulkWrite( [
{ insertOne : { "document" : {} } },
{ insertOne : { "document" : {} } }
] ,{ordered:fasle})
db.collection.bulkWrite( [
{ insertOne : { "document" : {} } },
{ insertOne : { "document" : {} } }
])
字段说明
insertOne.document
:需要插入的文档信息,如果文档不包含_id,则会自动生成;ordered
:选填,有序还是无序执行;
updateOne
updateOne 更新集合中 filter 匹配的单个文档。如果匹配到多个文档 updateOne 仅更新第一个匹配到的文档;
db.collection.bulkWrite( [
{ updateOne :
{
"filter" : {name:"bulkWrite6"},
"update" : {$set:{age:19}},
"upsert" :true
}
}
],{ordered:fasle} )
db.collection.bulkWrite( [
{ updateOne :
{
"filter" : {name:"bulkWrite6"},
"update" : {$set:{age:19}},,
"upsert" :true
}
}
] )
字段说明
updateOne.filter
:object,必填,匹配条件;updateOne.update
:object,必填,更新内容,可执行操作为:$ set 、$ unset 、$rename等等;updateOne.upsert
: boolean, 选填,默认为false,如果为true,则没有匹配时插入一个新文档;
updateMany
updateMany 更新集合中所有匹配到的文档;
db.collection.bulkWrite( [
{ updateMany :
{
"filter" : {name:"bulkWrite6"},
"update" : {$set:{age:19}},
"upsert" :true
}
}
],{ordered:fasle} )
db.collection.bulkWrite( [
{ updateMany :
{
"filter" : {name:"bulkWrite6"},
"update" : {$set:{age:19}},,
"upsert" :true
}
}
] )
字段说明
updateOne.filter
:object,必填,匹配条件;updateOne.update
:object,必填,更新内容,可执行操作为:$ set 、$ unset 、$rename等等;updateOne.upsert
: boolean, 选填,默认为false,如果为true,则插入一个文档filter
deleteOne
deleteOne 删除集合中 filter 匹配到的单个文档。如果匹配到多个文档 deleteOne 只会删除一个匹配到的文档;
db.collection.bulkWrite([
{ deleteOne : { "filter" : {age:19} } }
] )
字段说明
deleteOne.filter
:object,必填,删除与此过滤器匹配的第一个文档;
deleteMany
deleteMany 删除集合中 filter 匹配到的所有文档;
db.collection.bulkWrite([
{ deleteMany : { "filter" : {age:19} } }
] )
字段说明
deleteMany.filter
:object,必填,删除与此过滤器匹配的所有文档;
replaceOne
replaceOne 替换集合中 filter 匹配到的单个文档。如果匹配到多个文档 replaceOne 只会替换一个匹配到的文档;
db.collection.bulkWrite([
{ replaceOne :
{
"filter" :{age:19},
"replacement" : {age:20},
"upsert" :true
}
}
] )
字段说明
updateOne.filter
:object,必填,替换与此过滤器匹配的第一个文档;updateOne.update
:object,必填,替换内容;updateOne.upsert
: boolean, 选填,默认为false,如果为true,如果没有匹配的文档,则插入一个文档filter
三:优点
- 可以批量同时执行不同的操作,新增、修改、删除;
- 执行速率比save、findOneAndUpdate等等快,因为,bulkWrite是在一个命令中向 MongoDB 服务器发送多个insertOne、updateOne、updateMany、replaceOne、 deleteOne等等的请求,这比发送多个独立操作更快,因为bulkWrite()只有一次往返 MongoDB,而每一次独立操作都是一次往返的MongoDB。
四:以下需要注意
- ordered参数,由于bulkWrite中可以几个操作同时混合使用,那么,如果使用无序执行时,可能会造成deleteOne或deleteMany 删除的文档可能会变多或变少,具体取决于deleteOne或deleteMany 是在insertOne,updateOne,updateMany或replaceOne操作之前或之后的运行。
如下:
db.collection.bulkWrite(
[
{ insertOne : <document> },
{ updateOne : <document> },
{ updateMany : <document> },
{ replaceOne : <document> },
{ deleteOne : <document> },
{ deleteMany : <document> }
],
{ ordered : false }
)
- 更新(updateOne、updateMany)或替换(replaceOne)操作不能指定与原始文档不同的 _id 值;
五:错误返回
错误返回值主要分为两种,ordered的有序和无序执行的错误返回,
以下述插入为例,第一项、第四项和第五项是不存在的,第二项和第三项的_id是已存在的
有序执行错误
db.collection.bulkWrite(
[
{
insertOne: { document: { name: 'bulkWrite10' } }
},
{
insertOne: { document: { _id: '6321a77b06e79133d99b926c', name: 'bulkWrite6' } }
},
{
insertOne: { document: { _id: '63227cde0795b55523c0e8ac', name: 'bulkWrite7' } }
},
{
insertOne: { document: { name: 'bulkWrite11' } }
},
{
insertOne: { document: { name: 'bulkWrite12' } }
}
],
{ ordered: true }
);
有序执行错误,返回值中只有一个错误的,但是insertedIds中index=2、3、4都没有执行
{
"code": 11000,
"writeErrors": [
{
"code": 11000,
"index": 1,
"errmsg": "E11000 duplicate key error collection: test_one.users index: _id_ dup key: { _id: ObjectId('6321a77b06e79133d99b926c') }",
"op": {
"name": "bulkWrite6",
"age": 0,
"register_time": "2022-09-15T03:45:04.529Z",
"remark": null,
"vip": false,
"address": null,
"_id": "6321a77b06e79133d99b926c"
}
}
],
"result": {
"ok": 1,
"writeErrors": [
{
"code": 11000,
"index": 1,
"errmsg": "E11000 duplicate key error collection: test_one.users index: _id_ dup key: { _id: ObjectId('6321a77b06e79133d99b926c') }",
"op": {
"name": "bulkWrite6",
"age": 0,
"register_time": "2022-09-15T03:45:04.529Z",
"remark": null,
"vip": false,
"address": null,
"_id": "6321a77b06e79133d99b926c"
}
}
],
"writeConcernErrors": [],
"insertedIds": [
{
"index": 0,
"_id": "63229fc8afcdaac9bd268102"
},
{
"index": 1,
"_id": "6321a77b06e79133d99b926c"
},
{
"index": 2,
"_id": "63227cde0795b55523c0e8ac"
},
{
"index": 3,
"_id": "63229fc8afcdaac9bd268105"
},
{
"index": 4,
"_id": "63229fc8afcdaac9bd268106"
}
],
"nInserted": 1,
"nUpserted": 0,
"nMatched": 0,
"nModified": 0,
"nRemoved": 0,
"upserted": []
}
}
无序执行错误
db.collection.bulkWrite(
[
{
insertOne: { document: { name: 'bulkWrite10' } }
},
{
insertOne: { document: { _id: '6321a77b06e79133d99b926c', name: 'bulkWrite6' } }
},
{
insertOne: { document: { _id: '63227cde0795b55523c0e8ac', name: 'bulkWrite7' } }
},
{
insertOne: { document: { name: 'bulkWrite11' } }
},
{
insertOne: { document: { name: 'bulkWrite12' } }
}
],
{ ordered: false }
);
无序执行错误,返回值中有两个报错,三个插入成功
{
"code": 11000,
"writeErrors": [
{
"code": 11000,
"index": 1,
"errmsg": "E11000 duplicate key error collection: test_one.users index: _id_ dup key: { _id: ObjectId('6321a77b06e79133d99b926c') }",
"op": {
"name": "bulkWrite6",
"age": 0,
"register_time": "2022-09-15T03:47:51.976Z",
"remark": null,
"vip": false,
"address": null,
"_id": "6321a77b06e79133d99b926c"
}
},
{
"code": 11000,
"index": 2,
"errmsg": "E11000 duplicate key error collection: test_one.users index: _id_ dup key: { _id: ObjectId('63227cde0795b55523c0e8ac') }",
"op": {
"name": "bulkWrite7",
"age": 0,
"register_time": "2022-09-15T03:47:51.976Z",
"remark": null,
"vip": false,
"address": null,
"_id": "63227cde0795b55523c0e8ac"
}
}
],
"result": {
"ok": 1,
"writeErrors": [
{
"code": 11000,
"index": 1,
"errmsg": "E11000 duplicate key error collection: test_one.users index: _id_ dup key: { _id: ObjectId('6321a77b06e79133d99b926c') }",
"op": {
"name": "bulkWrite6",
"age": 0,
"register_time": "2022-09-15T03:47:51.976Z",
"remark": null,
"vip": false,
"address": null,
"_id": "6321a77b06e79133d99b926c"
}
},
{
"code": 11000,
"index": 2,
"errmsg": "E11000 duplicate key error collection: test_one.users index: _id_ dup key: { _id: ObjectId('63227cde0795b55523c0e8ac') }",
"op": {
"name": "bulkWrite7",
"age": 0,
"register_time": "2022-09-15T03:47:51.976Z",
"remark": null,
"vip": false,
"address": null,
"_id": "63227cde0795b55523c0e8ac"
}
}
],
"writeConcernErrors": [],
"insertedIds": [
{
"index": 0,
"_id": "6322a06b9145a56d3b23fabe"
},
{
"index": 1,
"_id": "6321a77b06e79133d99b926c"
},
{
"index": 2,
"_id": "63227cde0795b55523c0e8ac"
},
{
"index": 3,
"_id": "6322a06b9145a56d3b23fac1"
},
{
"index": 4,
"_id": "6322a06b9145a56d3b23fac2"
}
],
"nInserted": 3,
"nUpserted": 0,
"nMatched": 0,
"nModified": 0,
"nRemoved": 0,
"upserted": []
}
}
六:思考
个人经常使用bulkWrite的场景是:
- 定时更新数据,比如更新人员基本信息,因为每个人更新的内容都不相同;
- 刷数据,有些数据需要删除、有些需要更新、有些需要新增;
bulkWrite是比save、update更为高级的写法,希望大家可以灵活运用。
引用
mongoose关于bulkWrite的原文:https://mongoosejs.com/docs/api.html#model_Model-bulkWrite