タグ : mongo

MongoDB query でタブ文字区切りの文字列を出力させた結果をコピペするとタブ文字が消えることがあるのでファイル出力するのがベスト

本記事でお伝えしたいことはタイトルで完結していますが、先日 MongoDB の Query を書ける営業社員から「Mongo Query の出力結果を Google スプレッドシートに貼り付けると左に列がずれるのでコードレビューしてほしい」と相談されました。

結論としては query 自体には何も問題なく、タブ文字 \t 区切りで文字列を出力すると Mac の Terminal アプリからコピー&ペーストするときに折り返し位置によって \t が消えてしまうことがあるようでした。

ファイル出力すればタブ文字が消えてしまう問題は発生しないので、

mongo --quiet mydb script.js > example.csv

のような感じでファイルに出力して、これを Google スプレッドシートにインポートすると列がずれることがなくなりました。

もし、似たような問題が発生した方は Terminal アプリからのコピペの部分の挙動を疑ってみると解決するかもしれませんよ。

MongoDB shell で Object を出力するなら printjson() を使おう

MongoDB shell で Object を出力したいなら print() では出力できないので printjson() を使いましょう。

以下、Object を print() と printjson() の実行結果のサンプルを貼っておきます。

> var object = { a : 1 , b : 2 };
 
> print(object);
[object Object]
 
> printjson(object);
{ "a" : 1, "b" : 2 }
 
> printjson(1)
1
> printjson("a")
"a"

ほとんどの場合は printjson() を使うといいかもですね。


参考情報

[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 のエラーハンドリングの実装が微妙なのでは…


参考情報

[MongoDB] db.runCommand( { cloneCollection } ) の使い方

MongoDB で collection をコピーする db.runCommand( { cloneCollection } ) の使い方をメモ。

host: targethost1, port: 27017 と host: currenthost, port: 27018 で mongod instance が2つ立ち上がっている状態とします。

以下のようなクエリで targethost1:27017 の targetDb database にある users collection から verified_at が null ではない document を currentDb に同じ collection 名でコピーできます。

mongo --host currenthost --port 27018 currentDb
 
var query = { verified_at : { $ne : null } };
db.runCommand( { cloneCollection: 'targetDb.users', from: 'targethost1:27017', query : query } );

コピーした collection 名を変更したい場合は renameCollection でできます。

以上です。

[Mongoose] lean option を有効にすると MongooseDocument ではなく plain javascript object が返ってくる

タイトルで完結していますが、Mongoose で lean option を有効にすると MongooseDocument ではなく plain javascript object が返ってきます。

Documents returned from queries with the lean option enabled are plain javascript objects, not MongooseDocuments. They have no save method, getters/setters or other Mongoose magic applied.

Mongoose ドキュメントではないので、save メソッドや virtual の getter, setter や instance メソッドなどなど Mongoose にしかない便利機能は使えなくなります。

(例) lean option を使った Query

new Query().lean() // true
new Query().lean(true)
new Query().lean(false)
 
Model.find().lean().exec(function (err, docs) {
  docs[0] instanceof mongoose.Document // false
});

業務で lean option を有効にしたときに困ったことがありまして、populate した document の virtual を使っている箇所があり、その部分が動かなくなってしまったことです。

改修方針としては virutal を使わないで、plain js object を引数として渡して使う method を実装して、それを使うように修正しました。

パフォーマンスチューニングをするような状況にならないと lean option を使うことはなさそうですが、そのときは Mongoose の機能を利用した既存処理が動かなくなっていないか注意してみるといいかもしれません。