読者です 読者をやめる 読者になる 読者になる

Node + MongoDBでチャット的ななにかをつくる - Mongodbの導入、セッションを貼る、チャットロビーの作成まで

Redisはキツかった

MongoはBSONというJSONに近い構造のデータ構造らしいし*1、いろいろと親和性がありそうでよさそう。練習のつもりで同じように作成してみる。

MongoDBの導入

普通にaptで

sudo apt-get install mongo

起動は mongo で

Mac Brewの場合
brew install mongo

インストールは簡単だけど、そのままだと動かない。 mongodの path を変えてやるか、デフォルトの /data/db を作る必要がある

#mongodでdb_pathを変えるという方法もあるが、今回はチュートリアルどおりに掘った
sudo mkdir -p /data/db/
sudo chown `id -u` /data/db

参考:: おもむろに mac に mongodb をインストールする - 名称未定ドキュメント”Que”

MongoDB自体の簡単な操作とか

#name:altを保存する
db.hoge.save({name: 'alt'})
#見つける
db.hoge.find()
#{ "_id" : ObjectId("4f9114049d0e8dc3e306d36a"), "name" : "alt" }
#消すときはこういうコマンドとか。詳しくは知らない
db.hoge.drop()
db.hoge.remove()

参考::Mongo db勉強会
あと、あとでModelをSchemaで定義して登録するんだけど、その場合は複数形の s を付けないといけないようだった

#Userという名前で定義した
db.users.find()

NodeでMongoでSessionを貼る

とりあえずこの3つがみつかった

  • express-connect-mongo
    • 古い?
  • connect-mongodb
    • これも使われてるっぽい
  • connect-mongo
    • 今回はこれを使用

app.jsはこんな感じで。githubのサンプルとほぼ同じですが、storeのdb定義を適当にしても見つからないんだけどどうしよう?
ちなみにExpressは 3.0.0alpha1です。npmでいれたら自然と3系になった。あとExpressで雛形を作るときにセッション指定をデフォルトでしておく。

node_module/express/bin/express -s chat_app2

app.js

var express = require('express'),
    routes = require('./routes'),
    http = require('http'),
    MongoStore = require('connect-mongo')(express);

var app = express();

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.static(__dirname + '/public'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.cookieParser('your secret here'));
  //適当すぎるけどいいのかな?
  app.use(express.session({
    secret: "secret",
    store: new MongoStore({ db: 'chat' })
  }));
  app.use(app.router);
});

URLで指定したりする方法もあるけど、結局どうすればよかったんだろう?
参考::API Only - Stack Exchange
まあよくわからないがセッションは確立した。

Mongoのドライバ

いろいろある。
参考::Node.js用のmongodbドライバー6選 - memo.yomukaku.net
GridFSを使わなければMongoose、使う場合はnode-mongodb-native(npm install mongodbではいる)が主流らしい。なので今回はMongoose。

mongooseの参考資料

参考:: node.js から MongoDB にアクセス (Mongoose の紹介) - KrdLabの不定期日記
参考::node.js+MongooseでMongoDBを試してみる – ServersMan@VPS | はるかなる熊
参考::ナマケモノになりたいishiducaが書いてます : Node.js+Express+Mongoose(MongoDB)でログイン認証
参考::API Only - Stack Exchange
これらを参考にして以下の感じで新規会員登録、ログイン、チャットロビー作成までつくりました。Schemaの定義とかさえちゃんとやればRedisのときより圧倒的に楽ですね(まあそりゃ用途が違うんだからそりゃそうだが)。

Schema
//Schema

var mongoose = require('mongoose');
    mongoose.connect('mongodb://localhost/chat');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;

var User = new Schema({
  name : String,
  password : String
});
mongoose.model('User', User);
var UserModel = mongoose.model('User');

var Room = new Schema({
  name : String
});
mongoose.model('Room', Room);
var RoomModel = mongoose.model('Room');

var Chat = new Schema({
  //ここらへんのSchemaはちゃんと定義してあげないとだめっぽいかったので修正した
  // date : { type: Date, default: Date.now }
  // name : { type: String, required: true }
  date : Date,
  name : String,
  chat : String,
  room : [Room]
});
mongoose.model('Chat', Chat);
var ChatModel = mongoose.model('Chat');
//Schema
Routes
exports.create_signup= function(req, res){
  //各種バリデーションがここにはいる
  //MongoのSchemaでバリデーションかけるのも本当は必要っぽいけど、まあ省略
  UserModel.findOne({name:req.body.name}, function(err, obj){
    if (obj){
      console.log('同じ名前の人がいます');
      res.redirect('/signup');
    }
    else {
      console.log('新規登録します');
      var user = new UserModel();
      user.name = req.body.name;
      user.password = req.body.password;
      user.save();
      req.session.name = user.name;
      res.redirect('/');
    }
  });
  //今回は使わないけどデバッグ用に一覧をとってくる
  //UserModel.find({}, function(err, docs){
    //docs.forEach(function(doc){
      //console.log(doc);
    //});
  //});
};

exports.login = function(req, res){
  res.render('login', { title: 'login' });
};

exports.create_login = function(req, res){
  UserModel.findOne({name:req.body.name, password:req.body.password}, function(err, obj){
    if (obj){
      console.log('ログインします');
      req.session.name = req.body.name;
      res.redirect('/');
    }
    else {
      console.log('ログイン失敗');
      res.redirect('/login');
    }
  });
};

exports.logout= function(req, res){
  delete req.session.name;
  delete req.session.room;
  res.redirect('/');
};

exports.roby= function(req, res){
  RoomModel.find({}, function(err, room){
      res.render('roby', {title: 'roby',
                          room: room});
  });
};

exports.create_roby= function(req, res){
  if (req.body.room === "") return false;
  console.log('新規room登録します');
  var room = new RoomModel();
  room.name = req.body.room;
  room.save();
  res.redirect('/roby');
};

node_redisでやってたように、UserModel.findOneでとってきたコールバックの関数内部でいろいろ処理する感じ。でもこっちのほうが圧倒的に楽だね。実際Redisほどは手間取らなかった。

次回はチャット画面の作成

Schemaの定義さえできてしまえば思いの外サクサクに進んでいい感じである。NodeとMongoが相性がいいってものあって、わりと気分がいい。

*1:あとで調べたらMongo自体のShellもSpiderMonkeyだかJSのエンジンで動いてるらしい。どうりでJSなコードが表示されるわけだ