1

MongoDB でいくつかのオブジェクトを修正しようとすると、奇妙な動作が発生します。lc言語コード ( ) を からmayに変更しようとしてmsaいます。テキストと言語コードに固有のインデックスがあります。{t:1, lc:1}

まず、カウントを取得します。

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" }

ここで、失敗する前に一致が正しく更新されると予想していましたが、カウントはまったく同じ数を返します。

Javascript で明らかな何かを見逃していませんか?

更新:例外をスローせずに出力されるオブジェクトの一部も重複しているようです。そのため、エラーがスローされるまでに多少の遅延があるようです (ジャーナリングを有効にしています)。これは正常な動作ですか?

4

1 に答える 1

6

簡単に言えば、問題は JS コードにあるということです。

Mongo の更新はデフォルトで発火後は忘れられるため、個々の更新が重複キーのために失敗した場合でも、「try」ステートメントは正常に完了し、「catch」セクションのコードは実行されません。forEach ループが終了すると、JS シェルが db.getLastError() を返し、操作が成功すると null が返されるため、「catch」コードが実行されているように見える場合があります。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
> 

ご覧のとおり、2 つのレコードを更新できなかったにもかかわらず、「catch」コードが実行されなかったため、「boo」は出力されませんでした。技術的には、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" }
>

ご覧のとおり、最後のエラー メッセージは 2 回出力されました。1 回目はスクリプトで出力したとき、もう 1 つはスクリプトが終了したときです。

この応答の冗長な性質を許してください。これにより、getLastError と、JS シェルでの操作の実行方法についての理解が深まったことを願っています。

スクリプトは、try/catch ステートメントを使用せずに書き直すことができ、更新できなかったドキュメントの _id を出力するだけです。

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())
    }
});
于 2012-04-11T19:42:12.360 に答える