連載
» 2009年10月26日 00時00分 公開

ゆったリラックス! CouchDBがあるところ(2):APIとDesign Documentでカクテルブックに挑戦 (3/5)

[z.ohnami,CouchDB JP]

ドキュメントの一括処理

 DBを操作していると、複数のデータを一括して処理したいことがよくあります。CouchDBでは“_bulk_docs”APIを使用して、複数のドキュメントに対する処理を一度に実行できます。その場合は“docs”という項目の中に、配列として複数のドキュメントを記述します。各ドキュメントの間はカンマで区切ります。マティーニ以外のカクテルを一括して登録してみましょう。

{
   "docs":[
    {
      "name":"スティンガー",
      "proof":31, ...
      },
     {
      "name":"サイドカー",
      "proof":26, ...
       }
    ]
リスト2 サンプル内 document/cocktails.txt (一部略)

 データベース名の次に“/_bulk_docs”と付与し、curlを以下のように実行します。

$curl -X POST -d  @'./document/cocktails.txt' 'http://127.0.0.1:5984/cocktail-book/_bulk_docs'

 ドキュメントの追加のみではなく、更新と削除もまとめて実行できます。1つのドキュメントを処理するときと同じように、更新と削除は“_rev”パラメータでrevisionを指定します。さらに、削除の場合は“_deleted”のフラグをtrueにしておきます。“all_or_nothing”という項目をtrueにすると、実行結果は複数のドキュメントの更新がすべて適用されるか、まったく適用されないかのどちらかになります。ACIDにおける原子性(Atomicity)が要件として求められる場合は利用するとよいでしょう。

{
     "all_or_nothing":true,
     "docs":[
        { "fieled1":"data1","fieled2":"data2",... },
        { "fieled1":"data1","fieled2":"data2",... },
        { "fieled1":"data1","fieled2":"data2",... },
        {"_id": "1", "_rev": "1-2089673485", "integer": 2, "string": "2"}, 
              ↑ 更新の場合
        {"_id": "0", "_rev": "1-62657917", "_deleted": true} 
              ↑ 削除の場合
     ]
  }
リスト3 追加、更新、削除を一度に書いたJSONファイルの例

 複数のドキュメントを同時に取得することも可能です。その場合は、“_all_docs”APIを使用します。そのまま使用した場合はデータベースに格納されているドキュメントの“__id”と“__rev”のみが返ってきます。一緒にドキュメントの内容自体も取得するには“_include_docs=true”というパラメータを追加します。

 3番目のkeyというパラメータは検索に使用します。start_keyとend_keyを使用すると範囲指定で絞込みを実行します。SQLのBETWEENと同じ要領ですね。“_all_docs”APIでkeyに指定できるのは“_id”のみです。しかし、これだと検索のキーとして使用できるのは“_id”のみとなり使い勝手が悪いです。特にPOSTメソッドでuuidを利用して採番している場合は検索がやりにくいでしょう。

 CouchDBでは検索のバリエーションを増やすために、viewという機能を提供しています。viewの詳細については次の“Design Document”で詳しく解説します。

$curl -X GET .../cocktail-book/_all_docs'
$curl -X GET .../cocktail-book/_all_docs?include_docs=true'
$curl -X GET .../cocktail-book/_all_docs?key="matini"'
$curl -X GET .../cocktail-book/_all_docs?startkey="ma"&endkey="na"'

$curl -X GET .../cocktail-book/_all_docs?startkey="ma"&endkey="na"'

Design Documentによる多彩なドキュメントの表現

 CouchDBにはDesign Documentという特別なドキュメントがあります。Design Documentはドキュメントの検索や集計処理、ドキュメントの表示を動的に処理するために使用します。Design Documentにはいまのところview、show、listという3種類の機能が用意されています。初期設定では、JavaScriptで書いたソースコードをドキュメントの一種として、CouchDBに格納するようになっています。

viewによるドキュメントの柔軟な検索と集計処理

 はじめにviewを紹介します。viewでは特定の条件に合致するドキュメントを検索したり、ドキュメントの集計処理を実行できます。viewがあることで、ドキュメント内のすべての項目を検索用のキーとして使用できます。あるドキュメントの集合に対して複数の検索方法を用意することが可能になります。また、ドキュメントの構造を設計するときに必ずしもキーを決める必要がないことも魅力的だと思います。ドキュメントが蓄積されていった後でも簡単に検索の基準となるキーをいくつでも追加することできるのです。具体的な例を見てみましょう。

 次のコードはベースのお酒がジンのカクテルをアルコール度数の高い順に表示させるためのviewになります。関数の引数である“doc”にはドキュメントが格納されています。1つのドキュメントに対して一回ずつ、この関数が実行されることになります。cocktail_book内のドキュメントに、doc.base == 'ジン'の条件を当てはめてフィルタリングをしています。emitという関数はviewの出力結果を指定するために使用します。引数の指定は“emit(viewのキーとなる項目,viewの値として出力する内容)”といった具合になります。例の場合、キーに度数を指定し、出力内容にはカクテルの名前を指定しています。

function(doc) {
      if (doc.proof && doc.base == "ジン") {
        emit(doc.proof, doc.name);
      } 
   }
リスト4 viewのソースコードの例

 このビューを、まずはFutonから実行してみましょう。データベースを選択し、ドキュメントを表示させたら右上のコンボボックスから“Temporary view...”を選択し、viewを編集するための画面に移動します。

図2 “Temporary view...”を選択し、view編集画面へ(クリックで拡大) 図2 “Temporary view...”を選択し、view編集画面へ(クリックで拡大)

 先ほどのソースコードを左上の“map”と書いてある部分に入力し、Runボタンを押します。画面の下部に実行結果が表示されたでしょうか。表示結果の降順と昇順を切り替えるには、三角形のマークがある枠の部分をクリックします。こうして作成したviewはSaveボタンを押すことで、CouchDB内に保管されます。ダイアログボックスが表示されるので、viewの名前と、viewを格納するDesign Documentの名前を指定してください。Design Documentがない場合は、このタイミングで新規に作成されます。

図3 viewの実行結果を確認する(クリックで拡大) 図3 viewの実行結果を確認する(クリックで拡大)

 viewは一般のドキュメントと同じように、PUTメソッドで格納できます。

$curl -X PUT -H'Content-Type: application/json' \
  -d @'./design/d01.txt' 'http://127.0.0.1:5984/cocktail-book/_design/d01'

 viewをJSON形式で定義する場合は以下のように記述します。viewという項目の配下に“gin-by-proof”というviewを定義しています。1つのDesign Documentの中に複数のviewを定義できます。design/d01.txtではベースとなるお酒の名前順にすべてのカクテルの名前を出力する“all-by-base”というviewも定義しています。各viewはカンマで区切る点に注意してください。

  {
    "language": "javascript",
    "views": {
      "gin-by-proof" : {
        "map": "function(doc) {
          if (doc.proof && doc.base == \"ジン\") {
              emit(doc.proof, doc.name);
            }}"
         },
     "all-by-base" : {
        "map": "function(doc) {
          if (doc.name) {
              emit(doc.base, doc.name);
            }}"
         }
     }
  }
リスト5 サンプル内 design/d01.txt

 保管したviewはFutonから再び実行できますし、GETメソッドで直接指定して使うこともできます。直接指定する場合は以下のようにURIを指定します。結果はJSON形式で取得できます。

$curl -X GET 'http://127.0.0.1:5984/cocktail-book/_design/d01/_view/gin-by-

 viewは先ほどの“_all_docs”APIと同じように、実行時にさまざまなパラメータを追加できます。以下の例のように、viewで任意に指定したkeyを使用して検索できます。gin-by-proofというviewでは、keyに“_id”ではなく“proof”を指定していますから、アルコール度数が30度のカクテルという条件で検索していることになります。3番目のdescendingは、viewの結果を降順で出力するためのパラメータです。SQLのORDER BY句でDESCを指定するのと似ていますね。最後のlimitは、表示させる件数に制限をかけるパラメータです。“&”で“descending”パラメータと組み合わせて使用しています。

.../_design/d01/_view/gin-by-proof?key=30' 
.../_design/d01/_view/gin-by-proof?startkey=20&endkey=60' 
.../_design/d01/_view/gin-by-proof?descending=true' 
.../_design/d01/_view/gin-by-proof?descending=true&limit=10'

 CouchDBでは、Map&Reduceの考え方を基にDesign Documentが実装されています。先ほどのソースコードではmapに当たる部分のみを実装しました。mapで取得したデータに対してreduceの部分でさらに処理を加えることができます。

 例えば、レシピブックにジンベースのカクテルがいくつ登録されているかを調べてみましょう。先ほどと同じように、Futonに以下のコードを入力し、実行してください。reduceのコードは右側のreduceの部分に入力します。

(map)
    function(doc) {
       if (doc.name) {
          if (doc.base == "ジン") {
             emit(null, 1);
           }
       } 
    } 
 (reduce)
    function(key,value) {
       return sum(value);
    }
リスト5 サンプル内 design/d02.txt

 mapでは、doc.base == "ジン"にヒットしたときに、出力内容に1を指定してemit関数を実行しています。reduceではその結果を受け取って、sum関数で集計しています。CouchDBではSQLの集計関数のような機能もviewの機能を利用して簡単に実装できます。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。