MongoDBでバッチ処理

こんにちは増島です! 実家の猫が昨日19歳の誕生日を迎えました。月日が経つのは早いものです(*´∀`*)

話は変わりますが、MongoDBを日次バッチで改廃したい要件があったので調べてみました!

概要

今回僕がバッチ処理で実現したかったのが、下記の内容でした。

処理対象のスキーマ

  • Rooms
  • Messages

スキーマ補足

  • Messageは一つのRoomに属する
  • Roomは複数のMessageを持つ

処理内容

  1. Roomsから作成日付が60日前のデータのroomIdを抽出する
  2. 1で抽出されたドキュメントをRoomsから削除
  3. Messagesから1で抽出されたroomIdを持つドキュメントを削除

javascriptを渡して実行

MongoDBに対してシェルなどから操作したい時は処理内容を記述したjavascriptファイルを用意してmongoクライアントに渡すのが簡単そうでした。

実行するスクリプト

[delete_expiration.js]

var expirationDate = before(60); // before関数は省略しますが、指定した日数前の日付を返します  
var query = { "created" : { "$lte" : expirationDate } };  
var expirationRooms = db.rooms.find(query);  
var expirationRoomIds = Array();

// 削除対象のルームIDを取得する
expirationRooms.forEach(function(room) {  
  if (room) {
    expirationRoomIds.push(room.room_id);
    print(room.room_id);
  }
});

// roomsから削除する
db.rooms.remove({room_id: {$in: expirationRoomIds} })  
// messagesから削除する
db.messages.remove({room_id: {$in: expirationRoomIds} })  

シェルから処理を実行する

#!/bin/sh

DB_NAME="test"

DAT=../data/expiration_rooms.dat  
mongo --quiet ${DB_NAME} ../js/delete_expiration.js > ${DAT}

echo "削除されたRoomのID一覧です"  
ls -l ${DAT}  

データベース名を指定してmongoクライアントにjavascriptファイルを渡してあげるだけで実行出来ます。
quietオプションを付けることによって、標準出力にMongoのバージョン等余計なものが表示されなくなります。
渡すscript内にあるprint文などは標準出力に表示されるので、削除対象となったRoomのIDはリダイレクトでexpiration_rooms.datに出力するようにしています。

あとはこのシェルをcronで毎日動かすようにすれば日次バッチの完成です!!

Mongoでストアドプロシージャ

今回はscriptをmongoクライアントに渡す方法をとりましたが、 mongo側に処理を保存しておく仕組みもちゃんとあるようです。

[userスキーマのオブジェクトIDを全件出力する処理]

[mas]$ mongo
// スクリプトを保存する
> db.system.js.save({_id: "printUsers", value: function () { 
>   var users = db.user.find();
>      var userIds = Array();
>      users.forEach(function(user) {
>          userIds.push(user._id);
>   });
>   return userIds;
> }});
> 
> // スクリプトの実行
> db.eval("printUsers();");
[
    ObjectId("55ced68280e717cafd3a4979"),
    ObjectId("55cedd79a2d7a95604fa235c")
]
>

以上、MongoDBでバッチ処理でした。