カテゴリー : MongoDB

MongoDB shell で moment.js, moment-timezone を使う方法

MongoDB shell で moment.js, moment-timezone を load() して使う方法をご紹介します。

MongoDB

続きを読む

[MongoDB] 小数点第 n 位以下の doc を取得して小数点第 m 位の数値で更新するクエリ

JavaScript で小数点以下の桁数を取得する方法の応用で、MongoDB で小数点第3位以下の doc を取得して小数点第2位に更新するクエリを書いてみました。

MongoDB

var skus = db.skus.find(
  {
    $or: [
      {
        $where: function() {
          var numbers = String(this.price).split('.');
          var result = 0;
 
          if (numbers[1]) {
            result = numbers[1].length;
          }
 
          // 小数点第3位以下か判定
          return result > 2;
        }
      },
      {
        $where: function() {
          var numbers = String(this.salePrice).split('.');
          var result = 0;
 
          if (numbers[1]) {
            result = numbers[1].length;
          }
 
          return result > 2;
        }
      }
    ]
  }
).toArray();
 
// 小数点第2位の数値に変換するコンバーター
var converter = function(num) {
  return parseFloat(num.toFixed(2));
};
 
skus.forEach(function(s){
  if (s.price) {
    s.price = converter(s.price);
  }
  if (s.salePrice) {
    s.salePrice = converter(s.salePrice);
  }
 
  // printjsononeline(s);
 
  var ret = db.skus.save(s);
  printjsononeline(ret);
  print('');
});

ポイントは $where: function() { } で price や salePrice が小数点第3位以下か判定している部分です。

$where operator を使うと複雑な条件のクエリも JavaScript でのプログラミングを駆使すれば実現できることが多いので、ぜひ $where を使ってみて下さい。

[MongoDB] Storage Engine の確認方法

MongoDB で Storage Engine (ストレージエンジン) を確認する方法をご紹介します。

MongoDB

MongoDB version 3.x

db.serverStatus() コマンドで Storage Engine が MMAPv1 か WiredTiger を確認できます。

db.serverStatus().storageEngine
{ "name" : "wiredTiger" }

MongoDB version 2.x 以下

安心してください。

Storage Engine は MMAP 一択で選択できないので、確認する方法もありません。

参考情報

[MongoDB] ISODate の年月日の文字列を取得する

MongoDB の ISODate から年月日の部分だけ文字列を取得するには、toISOString() と substring() を使って以下のようなコードで取得できます。

MongoDB

now = ISODate()
// ISODate("2016-12-06T05:02:20.675Z")
 
now.toISOString().substring(0, 10)
// 2016-12-06

年月日だけ使いたいことがあったので自分用のメモとして記事に残しておきます。

substring() じゃなくても slice() でもよいです。その辺はお好みでどうぞ。

[MongoDB] データベース名の変更方法

MongoDB でデータベース名を変更する手順をご紹介します。

MongoDB

前提

まず、MongoDB には DB 名をアトミックにリネームする方法はないので、コピーして古い DB を削除するという手順になります。

DB名の変更手順

以下、MongoDB でデータベース名を変更するコマンドです。

// データベースを変更したい名前をつけてコピーする
db.copyDatabase('old_name', 'new_name');
// 古いデータベースに切り替える
use old_name
// 古いデータベースを削除する
db.dropDatabase();

データベースの容量が大きいと db.copyDatabase に時間が掛かってしまいますが、気長に待つしかなさそうです。

MongoDB shell で moment.js や underscore.js などの便利ライブラリを使えるように拡張する mesh.js が神

MongoDB shell で JavaScript の有名なライブラリを使えるように拡張する mesh.js が便利だったのでご紹介します。

MongoDB

mesh.js のインストール

まず mesh.js をダウンロードします。

curl -O https://raw.githubusercontent.com/skratchdot/mesh/master/mesh.js

次に MongoDB shell で mesh.js ファイルを読み込んで shell から使えるようにします。読み込み方は2パターンあります。

.mongorc.js に load メソッドで読み込むパターン

.mongorc.js ファイルに下記のように

load('/path/to/mesh.js');

MongoDB shell 起動時に読み込むパターン

MongoDB shell 起動時に mesh.js ファイルを指定して読み込むことができます。

mongo --shell /path/to/mesh.js

個人的には .mongorc.js に書く方が無意識に使えるので便利です。

[事例] moment.js が使えて日付演算がめっちゃ楽

例えば、EC サイトで商品ページ productpages が公開日 published_at から1ヶ月毎の日付を確認したい場合は以下のように使えます。

> var productPage = db.productpages.findOne( ObjectId( "568f2d5f2e2195f01f663e3d" ) )
 
> productPage.published_at
ISODate("2016-01-08T04:08:59.822Z")
 
> var m = moment( productPage.published_at );
> m.toDate()
ISODate("2016-01-08T04:08:59.822Z")
 
> m.clone().add( 30, 'days' ).toDate()
ISODate("2016-02-07T04:08:59.822Z")
> m.clone().add( 60, 'days' ).toDate()
ISODate("2016-03-08T04:08:59.822Z")
> m.clone().add( 90, 'days' ).toDate()
ISODate("2016-04-07T04:08:59.822Z")

他にも時刻情報を Google スプレッドシートで扱いたい場合は moment.js の format メソッドを使って整形できるのは最高です。

> m.format('YYYY-MM-DD HH:mm:ss')
2016-01-08 04:08:59

まとめ

MongoDB shell で日付の演算をしたくなったら迷わずに mesh.js をインストールすると moment.js が使えて幸せになれそうです。

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

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

MongoDB

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

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

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

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

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

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

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

MongoDB

以下、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() を使うといいかもですね。

参考情報

[MongoDB] field 名の命名規則

MongoDB のドキュメントを確認すると field の命名規則にゆらぎがあるので決めの問題なんでしょうね。

MongoDB

places_id と snake_case で書かれています。

こちらは cancelDate と lowerCamelCase で書かれています。

アプリケーションコードで扱いやすい命名規則に書き方を統一するといいんじゃないでしょうか。

[MongoDB] E11000 duplicate key error index の code 11000 と 11001 の違い

MongoDB version 2.4.5 で unique index による duplicate key error index エラー発生時の error code には 11000 と 11001 があるのですが、その違いについて調べてみました。

MongoDB

事前準備

> db.version()
2.4.5
 
> db.so.drop();
> db.so.insert( { foo: 5 } );
> db.so.ensureIndex( { foo: 1 }, { unique: true } );
> db.so.insert( { foo: 6 } );

新規追加系メソッド insert のケース

db.collection.insert() で duplicate key error index エラーが発生したときは 11000 のエラーコードを返します。

> db.so.insert( { foo: 5 } );
E11000 duplicate key error index: test.so.$foo_1  dup key: { : 5.0 }
 
> db.getPrevError();
{
	"err" : "E11000 duplicate key error index: test.so.$foo_1  dup key: { : 5.0 }",
	"code" : 11000,
	"n" : 0,
	"nPrev" : 1,
	"ok" : 1
}

更新系メソッド update / save のケース

db.collection.update()db.collection.save() などの更新系のメソッドで duplicate key error index エラーが発生したときは 11001 のエラーコードを返します。

このときに紛らわしいのは、エラーメッセージ E11000 duplicate key error index に含まれているエラーコードが 11000 ということです。

> db.so.update( { foo: 6 }, { $set: { foo: 5 } } );
E11000 duplicate key error index: test.so.$foo_1  dup key: { : 5.0 }
 
> db.getPrevError();
{
	"err" : "E11000 duplicate key error index: test.so.$foo_1  dup key: { : 5.0 }",
	"code" : 11001,
	"n" : 0,
	"nPrev" : 1,
	"ok" : 1
}
> var so6 = db.so.findOne( { foo: 6 } )
> so6.foo = 5
5
 
> db.so.save( so6 )
E11000 duplicate key error index: test.so.$foo_1  dup key: { : 5.0 }
 
> db.getPrevError();
{
	"err" : "E11000 duplicate key error index: test.so.$foo_1  dup key: { : 5.0 }",
	"code" : 11001,
	"n" : 0,
	"nPrev" : 1,
	"ok" : 1
}

まとめ

MongoDB version 2.4.5 では、エラーメッセージは E11000 duplicate key error index と同じだけど、エラーコードは 11000 と 11001 の2種類があるバグっぽい処理になっていました。

このため、プログラム上で duplicate key error を処理したいときは if(err.code === 11000 || err.code === 11001) みたいな感じで扱うといいかもしれません。

なお MongoDB version 3.0.2 で試してみたところエラーコードは 11000 に統一されているようでした。

なので、err.code === 11001 だけのコードは MongoBD を upgrade したタイミングで動かなくなってしまうので注意が必要そうです。

> db.version()
3.0.2
 
> db.so.insert( { foo: 5 } );
WriteResult({
	"nInserted" : 0,
	"writeError" : {
		"code" : 11000,
		"errmsg" : "E11000 duplicate key error index: test.so.$foo_1 dup key: { : 5.0 }"
	}
})
 
> db.so.update( { foo: 6 }, { $set: { foo: 5 } } );
WriteResult({
	"nMatched" : 0,
	"nUpserted" : 0,
	"nModified" : 0,
	"writeError" : {
		"code" : 11000,
		"errmsg" : "E11000 duplicate key error index: test.so.$foo_1 dup key: { : 5.0 }"
	}
})
 
> var so6 = db.so.findOne( { foo: 6 } )
> so6.foo = 5
5
> db.so.save( so6 )
WriteResult({
	"nMatched" : 0,
	"nUpserted" : 0,
	"nModified" : 0,
	"writeError" : {
		"code" : 11000,
		"errmsg" : "E11000 duplicate key error index: test.so.$foo_1 dup key: { : 5.0 }"
	}
})

以上です。

参考情報