这种用例中我能想到的最佳处理方式是使用
"批量操作",以便在同一请求中发送“更新”和“插入”命令。我们还需要在此处设置唯一索引来强制执行不创建两个字段的新组合。
从这些文件开始:
{ "type" : "type1", "version" : 2 }
{ "type" : "type1", "version" : 3 }
{ "type" : "type2", "version" : 1 }
{ "type" : "type2", "version" : 2 }
在这两个字段上创建一个唯一索引:
db.products.createIndex({ "type": 1, "version": 1 },{ "unique": true })
然后我们尝试做一些实际的插入,使用批量操作来执行更新和插入:
db.products.bulkWrite(
[
{ "updateOne": {
"filter": { "type": "type3", "version": 1 },
"update": { "$set": { "data": {} } }
}},
{ "insertOne": {
"document": { "type": "type3", "version": 1, "data": { } }
}}
],
{ "ordered": false }
)
我们应该得到这样的回复:
{
"acknowledged" : true,
"deletedCount" : 0,
"insertedCount" : 1,
"matchedCount" : 0,
"upsertedCount" : 0,
"insertedIds" : {
"1" : ObjectId("594257b6fc2a40e470719470")
},
"upsertedIds" : {
}
}
注意这里的
matchedCount
是
0
,反映了“更新”操作:
"matchedCount" : 0,
如果我再做同样的事情,但使用不同的数据:
db.products.bulkWrite(
[
{ "updateOne": {
"filter": { "type": "type3", "version": 1 },
"update": { "$set": { "data": { "a": 1 } } }
}},
{ "insertOne": {
"document": { "type": "type3", "version": 1, "data": { "a": 1 } }
}}
],
{ "ordered": false }
)
然后我们看到:
BulkWriteError({
"writeErrors" : [
{
"index" : 1,
"code" : 11000,
"errmsg" : "E11000 duplicate key error collection: test.products index: type_1_version_1 dup key: { : \"type3\", : 1.0 }",
"op" : {
"_id" : ObjectId("5942583bfc2a40e470719471"),
"type" : "type3",
"version" : 1,
"data" : {
"a" : 1
}
}
}
],
"writeConcernErrors" : [ ],
"nInserted" : 0,
"nUpserted" : 0,
"nMatched" : 1,
"nModified" : 1,
"nRemoved" : 0,
"upserted" : [ ]
})
这将在所有驱动程序中始终抛出错误,但我们也可以在响应的详细信息中看到:
"nMatched" : 1,
"nModified" : 1,
这意味着即使“插入”失败了,“更新”实际上也完成了它的工作。这里需要注意的重要事情是,虽然“批处理”中可能会出现“错误”,但如果它们是我们预测的类型,即我们预期的重复键错误代码
11000
,那么我们可以处理它们。
因此,最终的数据当然如下所示:
{ "type" : "type1", "version" : 2 }
{ "type" : "type1", "version" : 3 }
{ "type" : "type2", "version" : 1 }
{ "type" : "type2", "version" : 2 }
{ "type" : "type3", "version" : 1, "data" : { "a" : 1 } }
这是您想在这里实现的目标。
因此,通过使用
{"ordered": false}
选项将其标记为“无序”,
.bulkWrite()
操作将产生异常,但至少会提交未导致错误的任何指令。
在这种情况下,通常的结果是“插入”成功并且没有更新,或者“插入”失败而“更新”应用。当响应返回失败时,您可以检查错误的“索引”是否为
1
,表示预期的“插入”失败,错误代码为
11000
,因为出现了预期的“重复键”。
因此,“预期”的错误可以被忽略,您只需要处理“意外”的错误,例如不同的代码和/或不同的发出批量指令的位置。