Node.jsでmongodbを使う

なんとなく思いつきでmongodbを触ってみた。
手元の環境はMaxOSX10.10で、mongodbのバージョンはv2.6.6です。

まずはmongodbのインストール。brewでインストールできる。

$ brew install mongodb

試しにサーバプロセスを手動で起動してみる。
ログが標準出力に垂れ流しになるので、別にターミナル上げてやった方がよい。

$ mkdir -p /tmp/mongo
$ mongod --dbpath /tmp/mongo

dbpathの場所にファイルがいろいろできる。何も指定しないとデフォルトは/data/dbにできる。

クライアントのCLIはmongoコマンドで起動する。

$ mongo
MongoDB shell version: 2.6.6
connecting to: test
>

稼働確認でfooというdbを作って適当にJSONオブジェクトを突っ込んでみる。

> use foo
switched to db foo
> db.foo.save({text:"hello mongodb.", type:"message"})
WriteResult({ "nInserted" : 1 })
> db.foo.save({text:"hoge", type:"message"})
WriteResult({ "nInserted" : 1 })
> db.foo.find()
{ "_id" : ObjectId("549a97030c7fbf2751eaa215"), "text" : "hello mongodb.", "type" : "message" }
{ "_id" : ObjectId("549a97200c7fbf2751eaa216"), "text" : "hoge", "type" : "message" }
> exit
bye

動いってるっぽい。

ここからはnodeからの呼び出しをやってみる。
今回書いたコードの全体は以下に置いておきました。
https://github.com/minamijoyo/mongo-example

元にするのは前に書いたsocket.ioのチャットサンプルを改造してメッセージをDBに保存するようにしてみる。

$ git clone git@github.com:minamijoyo/chat-example.git mongo-example
$ cd mongo-example
$ npm install

mongodbをnodeから触るのはmongooseというライブラリが有名っぽいです。

$ npm install mongoose --save

package.jsonはこんなかんじになった。

{
  "name": "mongo-example",
  "version": "0.0.1",
  "description": "mongodb example",
  "dependencies": {
    "express": "^4.10.2",
    "mongoose": "^3.8.21",
    "socket.io": "^1.2.1"
  }
}

で、サーバサイドのメインindex.jsは以下のようにした。

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var mongoose = require('mongoose');

var db = mongoose.connect('mongodb://localhost/chatlog');
var ChatSchema = new mongoose.Schema({
  message: {type: String}
});
var ChatLog = db.model('chatlog', ChatSchema);

app.get('/', function(req, res){
  res.sendFile(__dirname + '/index.html');
});

io.on('connection', function(socket){
  ChatLog.find(function(err,items){
    if(err){
      console.log(err);
      return;
    }
    if(items.length > 0){
      for(var i=0; i<items.length; i++){
        socket.emit('chat message',items[i].message);
      }
    }
  });

  socket.on('chat message', function(msg){
    var chatlog = new ChatLog({message:msg});
    chatlog.save(function(err){
      if(err){
        console.log(err);
        return;
      }
      io.emit('chat message', msg);
    });

  });
});

http.listen(3000, function(){
  console.log('listening on *:3000');
});

ポイント補足。

最初にmongooseを読み込んで、connectでDBのURLを指定して接続する。localhostの後ろのchatlogというのがDB名になる。

var mongoose = require('mongoose');

var db = mongoose.connect('mongodb://localhost/chatlog');

次にSchemaの定義で、JSON形式でテーブルレイアウトを定義する。データベースによって用語がバラバラで混乱するけど、まぁレコードの型とか定義するみたいな理解でだいたい合ってると思う。

var ChatSchema = new mongoose.Schema({
  message: {type: String}
});

で、db.modelにスキーマ渡してjavascriptから触れるオブジェクトを作る。第1引数に「chatlog」と指定している名前の複数形の「chatlogs」というコレクション(いわゆるテーブル)がmongodb上にできる。なんで指定した名前に勝手にs付けるのか気持ち悪い。

var ChatLog = db.model('chatlog', ChatSchema);

で、あとは前回のsocketのイベントに検索と保存を実装する。
まずはsocket接続してきたときに処理。

io.on('connection', function(socket){
  ChatLog.find(function(err,items){
    if(err){
      console.log(err);
      return;
    }
    if(items.length > 0){
      for(var i=0; i<items.length; i++){
        socket.emit('chat message',items[i].message);
      }
    }
  });

モデルのChatLogのfind()で検索できる。検索結果はitemsのオブジェクトの配列で返ってくるので、オブジェクトからmessageを取り出してクライアントに返す。

次にsocketのメッセージ送ってきたときの処理。

  socket.on('chat message', function(msg){
    var chatlog = new ChatLog({message:msg});
    chatlog.save(function(err){
      if(err){
        console.log(err);
        return;
      }
      io.emit('chat message', msg);
    });

JSONのオブジェクトにしてモデルを作って、save()で保存する。元々あったio.emitのsocketのブロードキャストはどっちでもよいけどsaveが成功したときだけにした。

使ってみて雑感。
トランザクションとかないNoSQLなのでミッションクリティカルなところには使いづらいけど、こんなかんじで、JSONのオブジェクトをお手軽に永続化できるかんじなのでWebサービスとかのバックエンドでゆるふわなかんじでJSONと合わせて使うのにはよさそう。

参考