2012-04-11 78 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