2017-06-15 47 views
1

更新文檔時,我有一個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

然後不是在一個命令中。不可以。只需不設置它就可以關閉加註,並允許僅更新行爲。然後,當沒有任何操作更新時,您可以執行插入操作。您可以使用這兩個語句執行批量操作,因爲「無序」會在插入嘗試時忽略重複的鍵錯誤。這就像它得到的一樣好。 –

回答

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