2012-04-11 86 views
1

我有一些奇怪的行爲,試圖修復我的MongoDB中的一些對象。我試圖將語言代碼(lc)從may更改爲msa,並且我在文本和語言代碼上有一個唯一索引,例如{t:1, lc:1}試圖更新並忽略重複鍵,通過db.XYZ.update失敗

首先我得到的計數:

db.Unit.count({lc: "may"}); 

的我嘗試:

db.Unit.find({lc: "may"}, {"t":1}).limit(1000).forEach(function(obj) { 
    try { 
     db.Unit.update({ _id: obj._id }, {$set : { "lc": "msa"}}); 
     print('Changed :' + obj.t + '#' + obj._id); 
    } catch (err) { 
     print(err); 
    } 
}); 

這似乎是工作的打印出大量的對象,那麼失敗:

E11000 duplicate key error index: jerome5.Unit.$t_1_lc_1 dup key: { : "laluan", : "msa" } 

現在我預計在失敗之前的比賽將被正確更新,但計數返回ns完全一樣的數字。

我錯過了一些與我的Javascript明顯的東西?

更新:它看起來像打印出來的一些對象沒有拋出異常也是重複的。所以看起來在發生錯誤之前有一些延遲(我啓用了日誌功能)。這是正常的行爲嗎?

回答

6

簡而言之,問題出在JS代碼上。

Mongo中的更新默認情況下是火併且遺忘,所以即使單個更新因爲重複鍵而失敗,「try」語句仍然會成功完成,「catch」部分中的代碼永遠不會執行。它可能看起來正在執行「catch」代碼,因爲當forEach循環結束時,JS shell返回db.getLastError(),如果操作成功,將返回null。

讓我們創建一個簡單的集合,和唯一索引:

> db.unit.save({_id:0, lc: "may", t:0}) 
> db.unit.ensureIndex({t:1, lc:1}, {unique:true}) 
> for(var i=1; i<10; i++){db.unit.save({_id:i, lc: "may", t:i})} 
> db.unit.find() 
{ "_id" : 0, "lc" : "may", "t" : 0 } 
{ "_id" : 1, "lc" : "may", "t" : 1 } 
{ "_id" : 2, "lc" : "may", "t" : 2 } 
{ "_id" : 3, "lc" : "may", "t" : 3 } 
{ "_id" : 4, "lc" : "may", "t" : 4 } 
{ "_id" : 5, "lc" : "may", "t" : 5 } 
{ "_id" : 6, "lc" : "may", "t" : 6 } 
{ "_id" : 7, "lc" : "may", "t" : 7 } 
{ "_id" : 8, "lc" : "may", "t" : 8 } 
{ "_id" : 9, "lc" : "may", "t" : 9 } 
> 

我們要運行一個 http://www.mongodb.org/display/DOCS/getLastError+Command

這也許是最好通過例子來解釋:GetLastError函數的文檔在這裏解釋腳本將所有「可能」值更改爲「msa」。在開始之前,讓我們做一些改變,所以改變「可能」到「MSA」將在索引創建重複值的一些值:

> db.unit.update({_id: 3}, {"lc" : "msa", "t" : 4 }) 
> db.unit.update({_id: 6}, {"lc" : "msa", "t" : 5 }) 
> db.unit.find() 
{ "_id" : 0, "lc" : "may", "t" : 0 } 
{ "_id" : 1, "lc" : "may", "t" : 1 } 
{ "_id" : 2, "lc" : "may", "t" : 2 } 
{ "_id" : 3, "lc" : "msa", "t" : 4 } 
{ "_id" : 4, "lc" : "may", "t" : 4 } 
{ "_id" : 5, "lc" : "may", "t" : 5 } 
{ "_id" : 6, "lc" : "msa", "t" : 5 } 
{ "_id" : 7, "lc" : "may", "t" : 7 } 
{ "_id" : 8, "lc" : "may", "t" : 8 } 
{ "_id" : 9, "lc" : "may", "t" : 9 } 
> 

現在,當我們的腳本點擊文件_id:4和_id:5,它將無法將「lc」的值更改爲「may」,因爲這樣做會在索引中創建重複條目。

讓我們運行您的腳本的一個版本。我添加了一些多餘的線條,使其更詳細:

db.unit.find({lc: "may"}, {"t":1}).limit(1000).forEach(function(obj) { 
    try { 
     print("Found _id: " + obj._id); 
     db.unit.update({ _id: obj._id }, {$set : { "lc": "msa"}}); 
     if(db.getLastError() == null){ 
      print('Changed t :' + obj.t + ' _id : ' + obj._id); 
     } 
     else{ 
      print("Unable to change _id : " + obj.id + " because: " + db.getLastError()) 
     } 
    } catch (err) { 
     print("boo"); 
     print(err); 
    } 
}); 

Found _id: 0 
Changed t :0 _id : 0 
Found _id: 1 
Changed t :1 _id : 1 
Found _id: 2 
Changed t :2 _id : 2 
Found _id: 4 
Unable to change _id : undefined because: E11000 duplicate key error index: test.unit.$t_1_lc_1 dup key: { : 4.0, : "msa" } 
Found _id: 5 
Unable to change _id : undefined because: E11000 duplicate key error index: test.unit.$t_1_lc_1 dup key: { : 5.0, : "msa" } 
Found _id: 7 
Changed t :7 _id : 7 
Found _id: 8 
Changed t :8 _id : 8 
Found _id: 9 
Changed t :9 _id : 9 
> 

正如你所看到的,「噓」從來沒有印刷,因爲從來沒有執行的「捕獲」的代碼,即使兩個記錄不能被更新。從技術上講,update()並沒有失敗,因爲重複的索引條目,它只是無法更改文檔,並生成了一個消息。

所有可以更改的記錄都已成功更改。

> db.unit.find() 
{ "_id" : 0, "lc" : "msa", "t" : 0 } 
{ "_id" : 1, "lc" : "msa", "t" : 1 } 
{ "_id" : 2, "lc" : "msa", "t" : 2 } 
{ "_id" : 3, "lc" : "msa", "t" : 4 } 
{ "_id" : 4, "lc" : "may", "t" : 4 } 
{ "_id" : 5, "lc" : "may", "t" : 5 } 
{ "_id" : 6, "lc" : "msa", "t" : 5 } 
{ "_id" : 7, "lc" : "msa", "t" : 7 } 
{ "_id" : 8, "lc" : "msa", "t" : 8 } 
{ "_id" : 9, "lc" : "msa", "t" : 9 } 

如果腳本再次運行,會生成以下的輸出:

Found _id: 4 
Unable to change _id : undefined because: E11000 duplicate key error index: test.unit.$t_1_lc_1 dup key: { : 4.0, : "msa" } 
Found _id: 5 
Unable to change _id : undefined because: E11000 duplicate key error index: test.unit.$t_1_lc_1 dup key: { : 5.0, : "msa" } 
E11000 duplicate key error index: test.unit.$t_1_lc_1 dup key: { : 5.0, : "msa" } 
> 

正如你可以看到印刷的最後一條錯誤消息兩次:一次是當我們在腳本打印,然後再次當腳本完成時。

原諒這個回覆的詳細性質。我希望這可以提高你對getLastError的理解以及如何在JS shell中執行操作。

該腳本可以被改寫沒有try/catch語句,並簡單地打印出無法更新任何文件的_ids:

db.unit.find({lc: "may"}, {"t":1}).limit(1000).forEach(function(obj) { 
    print("Found _id: " + obj._id); 
    db.unit.update({ _id: obj._id }, {$set : { "lc": "msa"}}); 
    if(db.getLastError() == null){ 
     print('Changed t :' + obj.t + ' _id : ' + obj._id); 
    } 
    else{ 
     print("Unable to change _id : " + obj.id + " because: " + db.getLastError()) 
    } 
}); 
+1

感謝馬克抽出時間給這樣一個明確的迴應。這個'db.getLastError()'的例子正是我所需要的! – 2012-04-11 20:31:10