简短的回答是问题出在 JS 代码上。
Mongo 中的更新默认是触发后忘记的,因此即使单个更新由于重复键而失败,“try”语句仍然会成功完成,并且“catch”部分中的代码将永远不会执行。可能看起来“catch”代码正在执行,因为当 forEach 循环结束时,JS shell 返回 db.getLastError(),如果操作成功,它将返回 null。GetLastError 在此处的文档中进行了解释:
http ://www.mongodb.org/display/DOCS/getLastError+Command
这也许最好通过示例来解释:
让我们创建一个简单的集合和一个唯一索引:
> 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 }
>
我们将运行一个脚本来将所有“may”值更改为“msa”。在此之前,让我们进行一些更改,因此将“may”的某些值更改为“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
>
如您所见,“boo”从未被打印出来,因为“catch”代码从未被执行,即使两条记录无法更新。从技术上讲,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())
}
});