前回の【node.js】socket.ioを使って簡単にチャットを作る方法の続きです。今回はチャットした内容をデータベースに保存する方法です。こうすることによって、一回ブラウザを閉じて再度開いた時に閉じる前のメッセージが残っていたり、途中から参加した人も今までのメッセージが読めるようになります。いくら保存できるようになったとしても、製品レベルには程遠く、脆弱性対策もしていないので、今回は、node.jsでMongoDBを使うことと、MongoDB のオブジェクトモデリングツールであるMongoose(マングース)の使い方のポイントだけを抑えて頂ければと思います。

今回のポイント

  • Mongooseをインストールして簡単な使い方。
  • チャットのメッセージを保存する。
  • ブラウザを閉じてもメッセージが残っている。
  • メッセージを削除する。
  • 削除したメッセージは、相手にも削除されるようにする。

※予めMongoDBが使える状態にしててください。

必要なnode_modules

  • ejs
  • express
  • mongoose
  • socket.io

Mongooseのインストール

インストールはいつものnpmでめっちゃ簡単にインストールできます。

$ npm install mongoose

app.jsを編集

前回の【node.js】socket.ioを使って簡単にチャットを作る方法のソースからMongooseの追記だけをしたサンプルです。

app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
 * Module dependencies.
 */
 
var express = require('express')
  , routes = require('./routes')
  , mongoose = require('mongoose');
 
var app = module.exports = express.createServer();
 
// Configuration
app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'ejs');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.cookieParser());
  app.use(express.session({ secret: 'your secret here' }));
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});
 
app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
 
app.configure('production', function(){
  app.use(express.errorHandler());
});
 
// Routes
app.get('/', routes.index);
app.listen(3000, function(){
  console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
});
 
//mongoose
var Schema = mongoose.Schema;
var UserSchema = new Schema({
  message: String,
  date: Date
});
mongoose.model('User', UserSchema);
mongoose.connect('mongodb://localhost/chat_app');
var User = mongoose.model('User');
 
//socket
var io = require('socket.io').listen(app);
io.sockets.on('connection', function (socket) {
  socket.on('msg update', function(){
    //接続したらDBのメッセージを表示
    User.find(function(err, docs){
      socket.emit('msg open', docs);
    });
  });
 
  console.log('connected');
 
  socket.on('msg send', function (msg) {
    socket.emit('msg push', msg);
    socket.broadcast.emit('msg push', msg);
    //DBに登録
    var user = new User();
    user.message  = msg;
    user.date = new Date();
    user.save(function(err) {
      if (err) { console.log(err); }
    });
  });
 
  //DBにあるメッセージを削除
  socket.on('deleteDB', function(){
    socket.emit('db drop');
    socket.broadcast.emit('db drop');
    User.find().remove();
  });
 
  socket.on('disconnect', function() {
    console.log('disconnected');
  });
});

chat.jsを編集

chat.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Client
$(function() {
 
  var socket = io.connect('http://localhost');
 
  socket.on('connect', function() {
    socket.emit('msg update');
  });
 
  $('#btn').click(function() {
    var message = $('#message');
    socket.emit('msg send', message.val());
  });
 
  $('#delete').click(function(){
    socket.emit('deleteDB');
  });
 
  socket.on('msg push', function (msg) {
    var date = new Date();
    $('#list').prepend($('<dt>' + date + '</dt><dd>' + msg + '</dd>'));
  });
 
 
  //接続されたらDBにあるメッセージを表示
  socket.on('msg open', function(msg){
    //DBが空っぽだったら
    if(msg.length == 0){
        return;
    } else {
      $('#list').empty();
      $.each(msg, function(key, value){
        $('#list').prepend($('<dt>' + value.date + '</dt><dd>' + value.message + '</dd>'));
      });   
    }
  });
 
  //DBにあるメッセージを削除したので表示も消す
  socket.on('db drop', function(){
    $('#list').empty();
  });
 
});

ターミナルでMongoDBを起動します。

$ mongod

app.jsを起動します。

$ node app.js

上手く起動すれば、 http://localhost:3000 でアクセスできると思います。

解説します。

さて、ここでのポイントは、送られてきたメッセージをMongoDBに登録する作業です。はじめに、

mongoose = require('mongoose');

でMongooseをrequireします。

次はMongooseの初期設定です。MongoDBに保存するデータ型を設定します。今回は簡単なチャットなので、メッセージのString型と、日付のDate型だけ登録しておきます。

js
1
2
3
4
5
//mongoose
var UserSchema = new Schema({
  message: String, //チャットのメッセージ
  date: Date //メッセージの送受信時間
});

connectでMongoDBと接続しますが、今回もローカルなのでlocalhost、次はデータベース名です。「chat_app」としました。

js
1
mongoose.connect('mongodb://localhost/chat_app');

socket.ioが接続されたら、まずはMongoDBの中に既にメッセージがあるかチェックします。

js
1
2
3
User.find(function(err, docs){ //メッセージを探す。
  socket.emit('msg open', docs); //返って来た値をemitする。
});

emitでchat.jsの方に命令が行きます。引数にメッセージの中身が入っているので、chat.jsで表示させてあげる記述をします。chat.jsを見て下さい。

chat.js
1
2
3
4
5
6
7
8
9
10
11
12
13
//chat.js
 //接続されたらDBにあるメッセージを表示
  socket.on('msg open', function(msg){
    //DBが空っぽだったら
    if(msg.length == 0){
        return;
    } else {
      $('#list').empty();
      $.each(msg, function(key, value){
        $('#list').prepend($('<dt>' + value.date + '</dt><dd>' + value.message + '</dd>'));
      });   
    }
  });

ここのところですね。引数msgが空っぽならretuneで返して、入っていたらjQueryのeachでタグにして表示させる記述です。最初にempty()してあるのは、途中で誰かが入ってきたら実行されてしまうので、重複した内容が表示されるのを防ぐために、empty()して空にすることにしました。もう少しスマートなやり方がありそうですね・・・。次はメッセージを送った時の処理です。

app.js
1
2
3
4
5
6
7
8
9
10
11
12
//app.js
socket.on('msg send', function (msg) {
  socket.emit('msg push', msg); //自分へemit。
  socket.broadcast.emit('msg push', msg); //broadcastで相手側。
  //DBに登録
  var user = new User();
  user.message  = msg; //メッセージを代入。
  user.date = new Date(); //時間を記録。
  user.save(function(err) { //saveでMongoDBに登録。
    if (err) { console.log(err); }
  });
});

たったこれだけです。とても簡単ですね!次はMongoDBの内容を削除して、チャットの内容も削除する方法です。削除のボタンを押した時のイベントを設定します。

chat.js
1
2
3
4
//chat.js
$('#delete').click(function(){
  socket.emit('deleteDB'); //emitしてapp.jsに伝えます。
});
app.js
1
2
3
4
5
6
7
//app.js
//DBにあるメッセージを削除
socket.on('deleteDB', function(){
  socket.emit('db drop'); //表示を消すためにchat.jsに伝えます。
  socket.broadcast.emit('db drop'); //broadcastで開いてる全員に伝えます。
  User.find().remove(); //データベースを削除します。
});
chat.js
1
2
3
4
5
//chat.js
//DBにあるメッセージを削除したので表示も消す
socket.on('db drop', function(){
  $('#list').empty(); //表示を消す。
});

今回は一気に全部のメッセージを消しましたが、もちろん個別に消すこともできますし、投稿した本人しか消せないようにすることもできます。

まとめ

MongoDBを使うとWEBアプリの表現の幅が一気に広がります。MongoDBはnode.jsと親和性が高いので、Mongooseを使って簡単にデータベースの操作ができます。チャットとしては完成度が低く、脆弱性対策もしていないので、このサンプルコードをそのまま公開するのは避けましょう。あくまでも、node.jsの可能性と、Mongooseの使い方だけを紹介だけなので、その辺はあしからず。