在MongoDB中更新文档时的条件upsert(insert)
我在MongoDB中有一些文档,看起来是这样的:在MongoDB中更新文档时的条件upsert(insert),mongodb,mongodb-query,conditional,upsert,database-update,Mongodb,Mongodb Query,Conditional,Upsert,Database Update,我在MongoDB中有一些文档,看起来是这样的: {type: type1, version: 2, data: ...} {type: type1, version: 3, data: ...} {type: type2, version: 1, data: ...} {type: type2, version: 2, data: ...} ... 我希望更新匹配类型和版本的数据,或者在版本不匹配时为给定类型创建新文档,但希望禁止使用新类型创建新文档 当我这样做时: db.getCollec
{type: type1, version: 2, data: ...}
{type: type1, version: 3, data: ...}
{type: type2, version: 1, data: ...}
{type: type2, version: 2, data: ...}
...
我希望更新匹配类型和版本的数据,或者在版本不匹配时为给定类型创建新文档,但希望禁止使用新类型创建新文档
当我这样做时:
db.getCollection('products').update({"type": "unknown_type", "version" : "99"}, {$set: {"version": 99, "data": new data}}, {"upsert": true})
它将创建一个新文档:
{type: unknown_type, version: 99, data: ...}
这正是我想禁止的。
有没有办法在一次呼叫中完成此操作?有没有办法限制某些字段的值?对于这个用例,我能看到的最好的处理方法是使用,以便在同一请求中发送“update”和“insert”命令。我们还需要在这里有一个唯一的索引,以强制您实际上不创建两个字段的新组合 从这些文件开始:
{ "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}
选项将其标记为“unordered”,它将至少提交没有导致错误的任何指令
在这种情况下,典型的结果是要么“插入”起作用而没有更新,要么“插入”在“更新”适用的地方失败。当响应中返回失败时,您可以检查错误的“索引”是否为1
,表明预期的“插入”失败,并且由于预期的“重复键”,错误代码是否为11000
因此,可以忽略“预期”情况下的错误,您只需处理发出的批量指令中不同代码和/或不同位置的“意外”错误。不要使用upsert。获得新内容的唯一原因是您专门将选项设置为
true
。您还期待什么?正如我在问题中所说:“我想更新匹配类型和版本的数据,或者在版本不匹配时为给定类型创建一个新文档”,然后不使用一个命令。不可以。只需不设置upsert并允许仅更新行为,即可关闭upsert。然后,当操作没有更新任何内容时,可以执行插入操作。您可能会对这两条语句执行大容量操作,即“无序”,这将在尝试插入时忽略重复键错误。这是最好的了。谢谢,这就是我一直在寻找的