Node.jsでLevelDB
Node.js+Expressでタスク管理アプリを作った時のメモ。
今回はデータストアに選択したLevelDBの話。
bbpink/node-shpapad · GitHub
LevelDBを選択した背景
- 単純なタスク管理アプリだから高機能なデータストアが必要なかった
- Node.js Bindingsが存在する
- クソ速そう
使い方
まずはパッケージをインストールします。
npm install levelup npm install leveldown
shpapadではpackage.jsonに書かれています。
leveldownはLevelDBのレイヤーが低い部分で、サーバサイドで使うのであればあったほうが良いみたいです。
DBへはput、get、delでCRUDを行います。
var levelup = require("levelup"); var db = levelup("./data/db/shpapad_db"); //データを保存するディレクトリを指定してコンストラクト var hashedID = "hashed!"; //ハッシュ化されたユーザID //ユーザIDが存在する場合はログイン、存在しない場合はユーザを作成 db.get(hashedID, function(err, value) { if (err) { if (err.notFound) { //create new user db.put(hashedID, "", function(err) { if (err) { res.redirect(303, "/login"); } else { //auth OK req.session.user = userid; res.redirect(303, "/list"); } }); } else { //キー存在なし以外のなんかのエラー res.redirect(303, "/login"); } } else { //auth OK req.session.user = userid; res.redirect(303, "/list"); } });
データ構造
shpapadに必要なデータは「ユーザ」「リスト」「タスク」の3種類であり、
「ユーザ」が頂点に位置する木構造で表現する必要があります。
- ユーザ Aさん
- 明日やること
- 掃除する
- 明日やること
- ユーザ Bさん
- 好きな人
- ドラッグストアで買うものリスト
- トイレットペーパー
LevelDBのデータはキーでソートされているため、キー値を"!"で区切って木構造を表現しました。
「キー:バリュー」で表現した場合、上記のデータはこんな感じになります。
ハッシュ化されたユーザAさんのID:"" ハッシュ化されたユーザAさんのID!リスト作成時UnixDateTime:{name:リスト名(明日やること),リストに紐付くタスク一覧の件数(1)} ハッシュ化されたユーザAさんのID!リスト作成時UnixDateTime!タスク作成時UnixDateTime:タスク名(掃除する)} ハッシュ化されたユーザBさんのID:"" ハッシュ化されたユーザBさんのID!リスト作成時UnixDateTime:{name:リスト名(好きな人),リストに紐付くタスク一覧の件数(3)} ハッシュ化されたユーザBさんのID!リスト作成時UnixDateTime!タスク作成時UnixDateTime:タスク名(篠崎愛)} ハッシュ化されたユーザBさんのID!リスト作成時UnixDateTime!タスク作成時UnixDateTime:タスク名(星野源)} ハッシュ化されたユーザBさんのID!リスト作成時UnixDateTime!タスク作成時UnixDateTime:タスク名(ウーピー・ゴールドバーグ)} ハッシュ化されたユーザBさんのID!リスト作成時UnixDateTime:{name:リスト名(ドラッグストアで買うものリスト),リストに紐付くタスク一覧の件数(1)} ハッシュ化されたユーザBさんのID!リスト作成時UnixDateTime!タスク作成時UnixDateTime:タスク名(トイレットペーパー)}
また、db.get()ではキーを指定して1件ずつしかデータを取得できないため、db.createReadStream()を使用してキー値を範囲指定でがさっと取得します。
リストを取得する
app.get("/list", function(req, res) { if (req.session.user) { var hashedID = crypto.createHash("sha256").update(req.session.user).digest("hex"); //get lists var lists = []; db.createReadStream({gt: hashedID + "!", lte: hashedID + "!9999999999999" }) .on("data", function(data) { var obj = JSON.parse(data["value"]); lists.push({key:data["key"], name:obj["name"], count:obj["count"]}); }) .on("end", function(data) { res.render("list", { menu: [{href: "/list", value:"リスト"}], logoutable: true, lists: lists }); }); } else { res.redirect(303, "/login"); } });
1人のユーザに紐付くリストを取得する場合、キー値の検索条件は"ハッシュ化されたユーザID!" から"ハッシュ化されたユーザID!UnixDateTimeのMAX値" の範囲です。
タスクの場合も階層が1つ深くなるだけでリストの場合と同様の処理となります。
続く!(たぶん)