[Mongoose] index が未作成な field を sort に指定して stream で取得すると default batchSize だけ処理して正常終了するので困った
Mongoose で index が作成されていない field を sort に指定して stream で取得すると、 default batchSize の 1000 件だけ処理して正常終了するので困った話をご紹介します。
find() で全件一括取得するケース
find() で全件一括取得すると callback の第一引数 err には値は入らず、配列の末尾の要素に $err, code が含まれていました。
User.find().sort({ createdAt: -1 }).setOptions({ noCursorTimeout: true }).exec(function(err, docs) { console.log(err); console.log(docs.length); console.log(docs[docs.length - 1]); /** 以下、console.log の出力結果 null 1001 { '$err': 'getMore runner error: Overflow sort stage buffered data usage of 33555763 bytes exceeds internal limit of 33554432 bytes', code: 17406, ... **/ }); |
stream() で全件取得するケース
stram() で取得する場合も同じで、最後に取得した document に $err, code が含まれていました。
var stream = User.find() .sort({ createdAt: -1 }) .setOptions({ noCursorTimeout: true }) .stream(); stream.on('data', function(user){ /** default batchSize 1000 件だけ取得して、1001 件目に $err, code を含むデータを取得して、正常終了する { '$err': 'getMore runner error: Overflow sort stage buffered data usage of 33555763 bytes exceeds internal limit of 33554432 bytes', code: 17406, ... **/ }); stream.on('error', function(err){ // エラーイベントは呼び出されない }); |
解決方法
sort に指定する field の index を作成する
index が作成されていないのが元々の問題なので、index 作成すれば解決します。
User.path('createdAt').index(true); |
index が存在する field を sort に指定する
index size が気になるので作成したくないという場合は、default で用意されている _id を sort に指定する対応でもいいと思います。
User.find().sort({ _id: -1 }).setOptions({ noCursorTimeout: true }).exec(function(err, docs) {}); |
所感
document が 1000 件しか取得できてないことに気付かない限り、エラーが発生していることに気付けないので Mongoose のエラーハンドリングの実装が微妙なのでは…