IBM Bluemix を通じて DBaaS で提供されている NoSQL データベース Cloudant 。特に Bluemix の IoT Foundation などと親和性が高く、NodeRED などを使ってセンサーデバイスから送られてくる非構造化データを非常に簡単に格納することができるなど、SQL データベースが苦手とするようなケースでのデータストレージとして便利に使えます:
2016021201


ところで、データを Cloudant に格納する所までは(特に NodeRED を使うなどすれば)コーディングなどをほぼ意識することなく出来てしまうのですが、では実際に集まったデータを外部から利用するにはどうすればいいでしょうか? まあ「プログラミング」といえばプログラミングを行うのですが、SQL やら JDBC やらとは違う方法で各データレコードにアクセスする必要があり、その辺りをまとめてみます。

まずは今回扱う Cloudant 上のデータを用意します。実際にはどんなものでもいいのですが、ここでは以下の様なものとします。具体的には IBM Bluemix と NodeRED を使い、IBM IoT Foundation の MQTT ブローカー経由でラズベリーパイから送信されたデバイスデータを Cloudant 内のデータベースに格納したものを使います。この辺りの具体的な手順はこちらを参照ください:
Bluemix の Node-RED サービスで IoT アプリを作る(2/2)

2016021103


上記リンク先で解説した方法で集めたデータは "iotdata" という名前のデータベース内に格納されているものとします:
2016021101


個別のデータの中身はこのような感じです。"_id", "_rev" というデフォルトのキー値に加えて、ラズベリーパイから取り出した "myName", "cputemp(CPU温度)", "cpuload(CPU負荷率)", "sine(サインカーブの値)" という4つのキー値が各レコードに含まれて JSON 形式で格納されています:
2016021102


この iotdata データベースから目的のデータを取り出す API とその使い方を紹介していきます。その前に API 実行のために必要な情報を Bluemix 画面から取り出しておきます。Bluemix でこの Cloudant サービスをバインドしているランタイムプロジェクトから「環境変数」を参照して、この Cloudant サービスにアクセスするための credentials 情報を調べておきます。具体的には username, password, host, port, url の情報が API 実行時に必要になるため、メモするなどして控えておくようにします:
2016021104


(1) _id 値を指定してドキュメントを取り出す

NoSQL データベースではもっともシンプルな API です。目的のドキュメントを特定する _id 値を指定して、目的のドキュメントを取り出す、というものです。これは環境変数の url 値と、_id 値を指定して、以下の様な API を GET リクエストで実行します:
(https で始まる "url" の値)/(データベース名)/(_id の値)

例えば url の値が https://NNNN:PPPP@xx.xx.xx.xx/ であったとして、データベース名が iotdata、_id 値が 3bfeaf10db8b47a1021dc51397773a99 であれば以下の URL に GET アクセスすることになります:
https://NNNN:PPPP@xx.xx.xx.xx/iotdata/3bfeaf10db8b47a1021dc51397773a99

この URL をウェブブラウザのアドレス欄にそのまま打ち込んでアクセスすれば目的のドキュメントデータが JSON オブジェクトとして得られるはずです:
2016021105

↑目的のドキュメントが得られるはずです。ここまでは簡単。


(2) 特定のキーの値で検索してドキュメントを取り出す

_id 値がわかっていればドキュメントを取り出すことができることは分かりました。でも実際には _id 値がわかっているデータにアクセスする、という使い方はほとんどなく、_id 値ではない値に対して何らかの条件を与えて検索して、その結果のドキュメントのデータを取得する、というケースがほとんどのはずです。 この「検索」を NoSQL データベースではどのように行うか、というのが問題になります。

例えば「CPU 温度(cputemp)が 42 度より大きいレコードのみ取り出して、_id 値でソートする」という検索をするにはどうすればいいでしょう? SQL データベースであれば深く考えずに
select * from iotdata where cputemp > 42 order by _id asc
みたいに実行すればいいだけなのですが、NoSQL データベースでは(当たり前ですが) SQL は使えません。NoSQL データベースで検索するには検索条件を JSON で与えて API をポストする、という必要があります。そしてその前に検索するキー値(今回の例であれば cputemp)のインデックスを作成されている必要があります。念のため現在作成されているインデックスの一覧を確認してみます:
(https で始まる "url" の値)/(データベース名)/_index

上述の条件であれば、以下の URL に GET アクセスすることになります:
https://NNNN:PPPP@xx.xx.xx.xx/iotdata/_index


上記の URL にウェブブラウザでアクセス(GET リクエスト)すると、指定したデータベースに現在作成されているインデックスの一覧が JSON フォーマットで確認することができます:
2016021202

↑デフォルトでは "_id" キーを対象とした "type":"special" なインデックスだけが作成されていることがわかります。


では cputemp キーで検索できるよう、cputemp にインデックスを作成します。新規にインデックスを作成するには、同じ URL に、以下のような JSON データを POST します:
{
  "index": {
    "fields": ["cputemp"]
  },
  "name" : "cputemp-index",
  "type" : "json"
}

↑フィールドに cputemp を指定し、インデックス名を cputemp-index にしています(インデックス名は適当で構いません)。

ウェブブラウザでは POST ができないため、別途ツールを使って実行してください。以下は curl を使った場合のコマンドライン例です(実行結果を緑にしています):
$ curl -X POST https://NNNN:PPPP@xx.xx.xx.xx/iotdata/_index -d '{"index":{"fields":["cputemp"]},"name":"cputemp-index","type":"json"}'

{"result":"created","id":"_design/c233b8dd41b208ef1d9c8370f7ff3906857b0089","name":"cputemp-index"}

↑cputemp-index インデックスの作成が成功しました。この状態で再度上記のインデックス一覧確認 URL にアクセスすると、作成された cputemp-index が追加されて、以下の様な結果になります:
2016021201


cputemp のインデックスが作成されたので、このインデックスを使って CPU 温度でデータの検索を行います。例えば上記で紹介した「CPU 温度が 42 度より上なデータを検索」してみましょう。

この検索を行うための指定は以下の JSON になります:
{
  "selector": {
    "cputemp": {
      "$gt": 42
    }
  }
}

↑"cputemp"が42より大きい("$gt" は "Greater Than"の意)の条件を selector に指定しています。

この JSON データを本文として、以下の URL に POST することで検索を実行します:
(https で始まる "url" の値)/(データベース名)/_find

curl を使う場合は青のような入力になります。成功すると緑のような結果が返ってきます。cputemp が 42 より大きなものだけを取り出すことができました:
$ curl -X POST https://NNNN:PPPP@xx.xx.xx.xx/iotdata/_find -d '{"selector":{"cputemp":{"$gt":42}}}'

{"docs":[
{"_id":"*****","_rev":"xxxxx","myName":"myPi","cputemp":42.24,"cpuload":0,"sine":-0.32},
{"_id":"*****","_rev":"xxxxx","myName":"myPi","cputemp":42.24,"cpuload":0,"sine":-1},
   :
: {"_id":"*****","_rev":"xxxxx","myName":"myPi","cputemp":46.54,"cpuload":0.45,"sine":-0.75} ]}

クエリーには更に条件を加えることもできます。例えばこれは cputemp が 42 より大きなものを最初の10個だけ、_id と _rev と cputemp のフィールドだけ抜き出す、という検索をする場合の指示です:
{
  "selector": {
    "cputemp": {
      "$gt": 42
    }
  },
  "fields": ["_id","_rev","cputemp"],
  "limit": 10
}

これで検索すると検索結果には _id と _rev と cputemp だけが含まれたデータが最初の 10 件分だけ返されます。インデックスとこの検索条件を工夫することで色々なクエリーを実現することができるようになります。
$ curl -X POST https://NNNN:PPPP@xx.xx.xx.xx/iotdata/_find -d '{"selector":{"cputemp":{"$gt":42}},"fields":["_id","_rev","cputemp"],"limit":10}'

{"docs":[
{"_id":"*****","_rev":"xxxxx","cputemp":42.24},
{"_id":"*****","_rev":"xxxxx","cputemp":42.24},
   :
(10件分だけ)
   :
{"_id":"*****","_rev":"xxxxx","cputemp":46.54}
]}


ただ SQL データベースのように「インデックスさえ作れば SQL のような検索が自由にできる」と考えるべきではありません。インデックスを作らないと検索できないともいえるわけで、データリソース(データベースサイズ)への影響を考える必要も考えながら、最小限のインデックスを作るべきです。また SQL のように全文検索を得意としているわけではないので、場合によっては検索エンジンや SQL データベースと組み合わせて使う必要も出てきます。

その一方で分散データベースの得意分野といえるスケーリングや高可用性を活かした使い方ができるので、適材適所的な考えでシステムを構築していく中で、上記のような得意分野を活かした使い方をさせることで便利に使っていけると思っています。


なお、クエリーについてのより詳しい情報も含めた Cloudant の API に関してはこちらのリファレンスを参照ください:
IBM Cloudant Documentation - API Reference