条件的upsert(插入)在MongoDB中

问题描述:

更新文档时,我有一个MongoDB的一些文件,它看起来像这样:条件的upsert(插入)在MongoDB中

{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: ...} 

这也正是我想禁止什么。 有没有办法在一次调用中执行此操作?有没有办法限制某些字段的值?

+0

然后不要使用upsert。你得到新东西的唯一原因是你专门将选项设置为“true”。你还有什么期望? –

+0

正如我在我的问题中所述:“我想更新匹配类型AND版本的数据或在版本不匹配时创建给定类型的新文档” – karlitos

+0

然后不是在一个命令中。不可以。只需不设置它就可以关闭加注,并允许仅更新行为。然后,当没有任何操作更新时,您可以执行插入操作。您可以使用这两个语句执行批量操作,因为“无序”会在插入尝试时忽略重复的键错误。这就像它得到的一样好。 –

对于此用例,我可以看到的最佳处理方法是使用"Bulk Operations"为了在同一请求中发送“更新”和“插入”命令。我们还需要在此处有一个唯一的索引来强制您实际上不会创建这两个字段的新组合。

这些文件开始:

{ "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" : { 

     } 
} 

注意到这里的matchedCount0体现了 “更新” 操作:

 "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

因此可以忽略“预期”情况下的错误,您只需处理发布的批量指令中不同代码和/或不同位置的“意外”错误。

+0

谢谢,那正是我正在寻找的 – karlitos