mongoDBでcursorでコレクションを取得しつつupdateすると、同じドキュメントが二回updateされたり、全くされなかったりすることがあるようだ。
たとえば次のようなクエリをmongoシェルから流すと
db.Hoge.find().forEach(function(result) { db.Hoge.update({_id: result._id}, {$set: {x: result.x * 2, y: someObject}}); });
updateが二回行われてxが4倍になっているドキュメントや、xが変化していないドキュメントが存在していた。
これはドキュメントのサイズが変わりデータの再配置が行われ、natural orderが変化することによって同一のドキュメントが二回読まれたり、一度も読まれないドキュメントが出てしまったのだと思われる。
もしそうであればnatural orderを使わなければ問題ないはず。以下のようにsortに_idを指定したところ全てのドキュメントが一度だけupdateされた。
db.Hoge.find().sort({_id: 1}).forEach(function(result) { db.Hoge.update({_id: result._id}, {$set: {x: result.x * 2, y: someObject}}); });
また、ドキュメントのサイズが変わらないin-placeなupdateではデータが再配置されないので、やはり問題ないと思われる。以下のような$incのみのupdateでも意図通りに動作した。
db.Hoge.find().sort({_id: 1}).forEach(function(result) { db.Hoge.update({_id: result._id}, {$inc: {x: result.x}}); });
他の接続によってupdateやinsertされて再配置やチャンク移動が発生することもあるが、カーソルを回している最中はreadロックされ他の接続はwriteできないはずなので問題ないと思う。これは検証していないので実際のところは分からない。