SQLite3の旧データベースをMongoDBへ引っ越し


昨日の休日練習で試した node-csv-parser ( http://qiita.com/items/b8137a66ca35e1b6a729 ) がうまくいったのに気をよくして(^^)v、、、

今日は業務で使ってたSQLite3の旧データベースをMongoDBへさくっと引っ越してみました。

SQLite3のデータをダンプ

まず、旧データベース側のデータを全部出力してみます。

$ sqlite3 hoge.db 
sqlite> .output 20120909.dump
sqlite> .dump

これで20120909.dumpというテキストデータが出来上がります。こんなの。

PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE hoge (
    ctime     INTEGER NOT NULL,
    date      TEXT NOT NULL,
    eventName TEXT,
    type      TEXT,
    status    TEXT,
    clientId  TEXT
  );
INSERT INTO "hoge" VALUES(1343096611063,'da.2012.7.22','event127878','hoge','tyu','157183_1343096648191');
INSERT INTO "hoge" VALUES(1343096624381,'da.2012.7.22','event129600','hoge','tyu','157183_1343096648191');
INSERT INTO "hoge" VALUES(1343096625820,'da.2012.7.22','event129600','hoge','zumi','157183_1343096648191');
  (略)
INSERT INTO "hoge" VALUES(1347159497371,'da.2012.9.9','event138761','hoge','tyu','375533_1347147506930');
CREATE INDEX date ON hoge (date COLLATE NOCASE);
COMMIT;

要するにSQL文がずらずら書かれているやつ。

これを実行すればCREATE TABLE文でテーブルが再構築されて、そこへINSERT文でデータをズンズン入れていけば、リストアできちゃうというわかりやすいバックアップ。

で、同じタイプのDB、つまりSQLite3へ入れるならこれで復元すれば良いのだけれど、今回はこれをMongoDBへ放り込もうという物語。

データ量は数千件程度と少ないので考え考え手作業しながらやてまう。

サイボウズの日付スタイル'da.2012.7.22'とかイベントの書き方'event127878'を流用してることに気付いたあなたはサイボーザー?まぁ、サンプルデータなのでアバウトですけど、このサンプルなど( http://jsgt.org/sb/ws/test.htm )で使ってるやつでサイボウズを四苦ハックしての運用版です。

Mongoのスキーマを決める

スキーマレスなのにスキーマとは、、、という無駄な?ことは考えず、マングース様の言うとおりMongoのスキーマを考えておく。

こんな感じ。

// スキーマ
var hogeSchema = {
   ctime     : { type: Number, default: Date.now } //サーバーへの登録時
  ,date      : { type: String, required: true, index: true} //スケジュールの日付
  ,eventName : { type: String} //イベントID
  ,type      : { type: String, required: true } //メッセージのタイプ
  ,status    : { type: String } //メッセージステータス
  ,clientId  : { type: String } //クライアントID
};

新しいデータのサンプル

データサンプルで見るとこんな感じになります。SQLite3の各カラムをJavaScriptのオブジェクトにして放り込もうという作戦。

データサンプル  {
   ctime     : 1342322851988
  ,date      : "da.2012.7.15"
  ,eventName : "event125197"
  ,type      : "hoge"
  ,status    : "zumi"
  ,clientId  : "609369_1342319334920"
}

作戦会議

さて、上のSQLite3のデータをどうやってMongoへ放り込むと簡単でしょう?
はいっ。テキストデータをnode-csvで読み込んでマングースへ食わせる手がありますっ。
採用っ。

準備

上のデータのSQL文をそぎ落とし、ダブルクオートにしてCSVぽくしてみます。

20120909.txt
1343096611063,"da.2012.7.22","event127878","hoge","tyu","157183_1343096648191"
1343096624381,"da.2012.7.22","event129600","hoge","tyu","157183_1343096648191"
1343096625820,"da.2012.7.22","event129600","hoge","zumi","157183_1343096648191"
  (略)
1347159497371,"da.2012.9.9","event138761","hoge","tyu","375533_1347147506930"

とりあえず、これを20120909.txtとか名前を付けて保存します

さて、一気にやります。

csv2mongo.js
//CSV perser
var csv = require('csv');
//入力データ
var infile = __dirname+'/20120909.txt';

// マングース降臨
var mongoose = require('mongoose');
// スキーマ
var UketukeSchema = {
   ctime     : { type: Number, default: Date.now } //サーバーへの登録時
  ,date      : { type: String, required: true, index: true} //スケジュールの日付
  ,eventName : { type: String} //イベントID
  ,type      : { type: String, required: true } //メッセージのタイプ
  ,status    : { type: String } //メッセージステータス
  ,clientId  : { type: String } //クライアントID
};

// DB接続
var conn = mongoose.createConnection('mongodb://localhost/hoge');
// スキーマ定義
var schema = new mongoose.Schema(UketukeSchema);
// モデル定義
var model = conn.model('Uketuke', schema);

//全削除してから、、、
delAll (function(){})
//全インストール
csv2mongo(infile);

// 全データ削除
function delAll (callback) {
  var U = new model();
  model
    .find({})
    .remove()
  U.save(function(err) {
    if (err) { console.log(err); }
    else callback(U);
  });
}

// データ挿入
function insert (data, callback) {
  var ins = new model();

  //ins.ctime = data.ctime; //nowを入れるなら不要
  ins.date = data.date;
  ins.eventName = data.eventName;
  ins.type = data.type;
  ins.status = data.status;
  ins.clientId = data.clientId;

  ins.save(function(err) {
    if (err) { console.log('/////////////////////////'+err); }
    else callback(data, ins);
  });
}

//CSVをマングースへ食わせろっ
function csv2mongo(infile){

  csv()
    .fromPath(infile) //読み込むCSVファイル
    .transform(function(data){
        var data ={
             ctime     : data[0] //データの対応注意
            ,date      : data[1]
            ,eventName : data[2]
            ,type      : data[3]
            ,status    : data[4]
            ,clientId  : data[5]
        };

        //DBへ記録
        insert(data,
          function(data, ins){
            console.log('==inserted');
          }
        ); 
        console.log(data);
        return data;
    })
    .on('data',function(data){
        console.log(data);
    })
    .on('end',function(count){
        console.log('Number of lines: '+count);
    })
    .on('error',function(error){
        console.log(error.message);
    }) 
}

実行

node csv2mong.js

出来上がり。

ちなみに、実際にはうちではMongoのレプリカントさんなので、マングースは3匹こんな感じで接続してます。(参考: http://qiita.com/items/b5e99bf4923b84b2bb40 )

//レプリカリスト
var ReplicaSets = ''
  + 'mongodb://192.168.1.11:27020/hoge,'
  + 'mongodb://192.168.1.12:27020/hoge,'
  + 'mongodb://192.168.1.13:27020/hoge,'

// DB接続
var conn = mongoose.connectSet(ReplicaSets);