MongoDB + Node.js で煩悩APIを作ってみた


年末の慌ただしい時期ですね。除夜の鐘を鳴らす日も近づいてきました。

除夜の鐘の数と言えば108回ですが、鐘の数の由来は（諸説あるようですが）108の煩悩を振り払うためだと言われていますね。

そんな年の瀬に、Node.jsとMongoDBの勉強を兼ねて「[煩悩API](http://bonnou.info/)」というものを作ってみました。

作りながら気づいてしまったのは、煩悩は108個固定でそれ以上増えることはないので実際はMongoDBを使う必要はなかった、ということです。

ですがMongoDBをどうしても使ってみたかったのでそのまま進めました。

基本的には、[「Creating a REST API using Node.js, Express, and MongoDB」](http://coenraets.org/blog/2012/10/creating-a-rest-api-using-node-js-express-and-mongodb/)を参考にNode.jsのExpressとMongoDBを使ってAPIを作りました。

MongoDBとNode.jsの連携部分に関しては上記の解説をご覧いただければと思います。(おい

煩悩の種類に関しては諸説あるようですが、本APIは[こちらの記事](http://www.citydo.com/newyear/column2009/bonno01.html)の煩悩の定義を参考にさせていただき作成しました。

煩悩は108個にそれぞれ名前があると思っていたのですが、同じ煩悩を段階に分けて繰り返しているそうです。

まめ知識ですね。

### ソースを見たい欲求がある方

[煩悩API](http://bonnou.info/)のソースに関しては
[hilotter / bonnou](https://github.com/hilotter/bonnou)
に公開してあります。

### 自分で煩悩APIをいじりたい欲求がある方

以前、chef-soloでMongo+Node.js環境を作成する方法をまとめましたので、上記のソースと[こちら](http://blog.hello-world.jp.net/?p=516)
を元にvagrantで仮想マシンを立ち上げていただければ、オリジナルの煩悩APIが作れると思います。

```
git clone git@github.com:hilotter/bonnou.git
cd bonnou
npm install
node server.js
```

### 解説

煩悩APIのすべては [bonnou.js](https://github.com/hilotter/bonnou/blob/master/routes/bonnou.js) にあるといっても過言ではありません。

bonnou.jsの中でMongoDBからのデータの呼び出し、初期化処理を行っています。

bonnouという名前のDBが存在しない場合は、初期化処理でpopulateDB();を呼び出し、すべての煩悩を作成しています。

populateDBの中ではひたすら煩悩のマスターデータを登録しています。

```bonnou.js
var populateDB = function() {
    Array.prototype.in_array = function(val) {
      for(var i = 0,  l = this.length; i < l; i++) {
        if(this[i] == val) {
          return true;
        }
      }
      return false;
    };

    var rokudai = {
        "貪": {
            name: "貪",
            kana: "とん",
            description: "万の物を必要以上に求める心"
        },
        "瞋": {
            name: "瞋",
            kana: "しん",
            description: "自分に背くことがあれば必ず怒るような心"
        },
        "癡": {
            name: "癡",
            kana: "ち",
            description: "万物の理にくらき心"
        },
        "慢": {
            name: "慢",
            kana: "まん",
            description: "他人と比較して思い上がり、自分を頼んで人を侮るような心"
        },
        "疑": {
            name: "疑",
            kana: "ぎ",
            description: "仏教の示す真理に、まず疑ってかかる心"
        },
        "悪見": {
            name: "悪見",
            kana: "あくけん",
            description: "曲がった事を強く思い、誠の道理を知らない心"
        }
    };

    var akuken = {
        "有身見": {
            name: "有身見",
            kana: "うしんけん",
            description: "悪見：自分と自分のものにこだわる心"
        },
        "辺執見": {
            name: "辺執見",
            kana: "へんしゅうけん",
            description: "悪見：部分に固執するかたよったものの見方"
        },
        "邪見": {
            name: "邪見",
            kana: "じゃけん",
            description: "悪見：因果の法則を無視した考え方"
        },
        "見取見": {
            name: "見取見",
            kana: "けんしゅけん",
            description: "悪見：自己の見解に固執し、他人の見解を否定する、諸々の諍論、対立をひきおこす心"
        },
        "戒禁取見": {
            name: "戒禁取見",
            kana: "かいごんしゅけん",
            description: "悪見：誤った「仏教の実践」を正しいと思いこだわる心"
        }
    };

    var sangai = {
        "欲界": {
            name: "欲界",
            kana: "よくかい",
            description: "欲望にとらわれた生物が住む世界"
        },
        "色界": {
            name: "色界",
            kana: "しきかい",
            description: "欲望を離れた清浄な物質の世界"
        },
        "無色界": {
            name: "無色界",
            kana: "むしきかい",
            description: "欲望も物質的条件も超越し、ただ精神作用にのみ住む世界"
        }
    };

    var shitai = {
        "苦諦": {
            name: "苦諦",
            kana: "くたい",
            description: "苦という真理",
            akuken: ["有身見", "辺執見", "邪見", "見取見", "戒禁取見"]
        },
        "集諦": {
            name: "集諦",
            kana: "じったい",
            description: "苦の原因という真理",
            akuken: ["邪見", "見取見"]
        },
        "滅諦": {
            name: "滅諦",
            kana: "めったい",
            description: "苦の滅という真理",
            akuken: ["邪見", "見取見"]
        },
        "道諦": {
            name: "道諦",
            kana: "どうたい",
            description: "苦の滅を実現する道という真理",
            akuken: ["邪見", "見取見", "戒禁取見"]
        }
    };

    var shudou = {
        "欲界": ["貪", "瞋", "癡", "慢"],
        "色界": ["貪", "癡", "慢"],
        "無色界": ["貪", "癡", "慢"]
    };

    var jiten = {
        "無漸": {
            name: "無漸",
            kana: "むざん",
            description: "内面的に恥じないこと"
        },
        "無愧": {
            name: "無愧",
            kana: "むき",
            description: "人に恥じないこと"
        },
        "嫉": {
            name: "嫉",
            kana: "しつ",
            description: "ねたみ"
        },
        "慳": {
            name: "慳",
            kana: "けん",
            description: "ものおしみ"
        },
        "悪作": {
            name: "悪作",
            kana: "あくさ",
            description: "後悔"
        },
        "睡眠": {
            name: "睡眠",
            kana: "すいめん",
            description: "人に恥じないこと"
        },
        "掉挙": {
            name: "掉挙",
            kana: "じょうこ",
            description: "精神的な躁状態のこと"
        },
        "惘沈": {
            name: "惘沈",
            kana: "こんじん",
            description: "精神的な鬱状態のこと"
        },
        "忿": {
           name: "忿",
           kana: "ふん",
           description: "いきどおり"
        },
        "覆": {
            name: "覆",
            kana: "ふく",
            description: "罪をおおい隠すこと"
        }
    };

    var bonnou = [],
        bonTemp,
        kai, tai, bon, ken, ten, i, key,
        id = 1;

    for(kai in sangai) {
      for(tai in shitai) {
        for(bon in rokudai) {
          if (kai !== "欲界" && bon === "瞋") {
            continue;
          }
          if (bon === "悪見") {
            for(ken in akuken) {
              if (shitai[tai]["akuken"].in_array(ken)) {
                bonnou.push(getBonbouInfo({
                  "base":   akuken[ken],
                  "sub":    {name: "六大煩悩", kana: "ろくだいぼんのう", description: "根本的煩悩"},
                  "tai":    shitai[tai],
                  "kai":    sangai[kai],
                  "level":  "見道"
                }));
              }
            }
          } else {
            bonnou.push(getBonbouInfo({
              "base":   rokudai[bon],
              "sub":    {name: "六大煩悩", kana: "ろくだいぼんのう", description: "根本的煩悩"},
              "tai":    shitai[tai],
              "kai":    sangai[kai],
              "level":  "見道"
            }));
          }
        }
      }
    }
    for(kai in shudou) {
      len = shudou[kai].length;
      for(i = 0; i < len; i++) {
        key = shudou[kai][i];
        bonnou.push(getBonbouInfo({
          "base":   rokudai[key],
          "sub":    null,
          "tai":    null,
          "kai":    sangai[kai],
          "level":  "修道"
        }));
      }
    }
    for(ten in jiten) {
      bonnou.push(getBonbouInfo({
        "base":   jiten[ten],
        "sub":    {name: "十纏", kana: "じってん", description: "枝末煩悩"},
        "tai":    null,
        "kai":    null,
        "level":  null
      }));
    }

    db.collection('bonnou', function(err, collection) {
        collection.insert(bonnou, {safe:true}, function(err, result) {});
    });
 
    function getBonbouInfo(params) {
        var base = params["base"],
            sub = params["sub"],
            tai = params["tai"],
            kai = params["kai"],
            level = params["level"],
            bonnou = {};

        bonnou["id"] = id;
        id++;

        bonnou["name"] = base["name"];
        bonnou["kana"] = base["kana"];
        bonnou["description"] = base["description"];

        bonnou["category"] = sub ? sub["name"] : null;
        bonnou["category_kana"] = sub ? sub["kana"] : null;
        bonnou["category_description"] = sub ? sub["description"] : null;

        bonnou["satya"] = tai ? tai["name"] : null;
        bonnou["satya_kana"] = tai ? tai["kana"] : null;
        bonnou["satya_description"] = tai ? tai["description"] : null;

        bonnou["dhaatu"] = kai ? kai["name"] : null;
        bonnou["dhaatu_kana"] = kai ? kai["kana"] : null;
        bonnou["dhaatu_description"] = kai ? kai["description"] : null;

        bonnou["level"] = level;
        return bonnou;
    }
};
```

長くてひたすら地道なコードです。

煩悩の定義を調べつつ、ここを作るのが一番苦労しました。

あとは/apiにアクセスされたらfindAllですべての煩悩を取得し、

/api/{id}でアクセスがあった場合はfindByIdで個別の煩悩を取得するようにしています。

[「Creating a REST API using Node.js, Express, and MongoDB」](http://coenraets.org/blog/2012/10/creating-a-rest-api-using-node-js-express-and-mongodb/)の記事から少し改良した点は、外部のjsからも使えるようにしたかったので[jsonpに対応させた](http://blog.hello-world.jp.net/?p=1047)ことと、煩悩の結果をキャッシュするようにしたことです。

以下のようなjsを書けば煩悩APIにアクセスできます。

```
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
$.ajax({
  type: 'GET',
  url: 'http://bonnou.info/api/1',
  jsonpCallback: 'cb',
  dataType: 'jsonp',
  success: function(json){
    console.log(json);
  }
});
</script>
```

MongoDB AdventCalendarなのにMongoDBの解説が全然できていないですが、Node.jsとMongoDBの組み合わせはかなり手軽に作ることができたので便利だと思いました。

作ってて楽しかったです。

年の瀬に[煩悩API](http://bonnou.info/)を使って快適な煩悩ライフをお過ごしください

### おまけ
[jsdo.itでのサンプル（今日の煩悩）](http://jsrun.it/hilotter/today_bonnou)

[ソースコードはこちら](http://jsdo.it/hilotter/today_bonnou)

### 参考

* [煩悩ってナニ？](http://www.citydo.com/newyear/column2009/bonno01.html)

* [オンライン版　仏教辞典](http://www.wikidharma.org/jp/index.php/%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9A%E3%83%BC%E3%82%B8)

* [Creating a REST API using Node.js, Express, and MongoDB](http://coenraets.org/blog/2012/10/creating-a-rest-api-using-node-js-express-and-mongodb/)

* [Seems to be a problem compiling mongodb.rb definition with chef-client 11.2.0](https://github.com/edelight/chef-mongodb/issues/89)

* [MongoDBでゆるふわDB体験](http://gihyo.jp/dev/serial/01/mongodb/0001?page=3)

* [MongoDBでdatabaseを削除する方法](http://shoken.hatenablog.com/entry/20120405/p1)

* [「Please ensure that you set the default write concern for the database by setting」on connect-mongodb
](http://qiita.com/ms2sato/items/62f3c336af1ea61ec684)

* [express](http://expressjs.com/api.html)