CouchDBはJSON形式でデータの入出力が行えるNoSQLである。
CloudantはCouchDBをベースに作られたクラウドで利用できるDBaaS(distributed database as a service)である。
現在CloudantはIBMが提供しているbluemixのサービスとして利用できる。
ここではCloudantの使い方をまとめておく。
アカウントは取得済みであることを前提とする。
登録が終わるとusernameとpasswordが払い出される。
以下手順内では${username}、${password}変数部分は各自のものに読み替えていただきたい。
◆ WEBコンソール
ブラウザを開いてアクセスできることを確認する。
https://${username}:${password}@${username}.cloudant.com/dashboard.html"
◆ データベースの操作
WEBのコンソールからも操作はできるが、REST経由で操作できるためコマンドラインで手順を残しておく。
(全データベースの取得)
$ curl -X GET "https://${username}:${password}@${username}.cloudant.com/_all_dbs"
(データベースの新規作成)
$ db="testdb"
$ curl -X PUT "https://${username}:${password}@${username}.cloudant.com/${db}"
$ curl -X PUT "https://${username}:${password}@${username}.cloudant.com/${db}"
(削除)
$ curl -X DELETE "https://${username}:${password}@${username}.cloudant.com/${db}"
◆ jsonデータ(ドキュメント)の操作
リレーショナルなデータベースではないためid検索が基本である。サーチインデックスを付与しない場合、直接それ以外の属性(下記例ではname, ageなど)による検索はできない。
投入する値がユニークであれば、"_id"キーにデータを入れるのも手だろう。
idやサーチインデックスについては後述。
(1ドキュメントを投入する場合)
$ doc='{"name": "aaa",
"age": "20",
"hobbies": {
"1st": "computer",
"2nd": "ferret"
},
"timestamp": "1438354800"
}'
$ curl -v \
-H "Content-type: application/json" \
-X POST \
-d "${doc}" \
https://${username}:${password}@${username}.cloudant.com/${db}/
(2つ以上のドキュメントはbulk投入できる)
jsonをここではdoc.jsonというファイル名で保存したとする。
jsonをここではdoc.jsonというファイル名で保存したとする。
{ "docs": [ { "name": "aaa", "age": "20", "hobbies": { "1st": "computer", "2nd": "ferret" }, "timestamp": "1438354800" }, { "name": "bbb", "age": "30", "hobbies": { "1st": "food", "2nd": "book" }, "timestamp": "1438354860" }, { "name": "ccc", "age": "40", "hobbies": { "1st": "car", "2nd": "movie" }, "timestamp": "1438354920" } ] }
$ curl -v \
-H "Content-type: application/json" \
-X POST \
-d @doc.json \
"https://${username}:${password}@${username}.cloudant.com/${db}/_bulk_docs"
◆ ドキュメントの操作
(全てのドキュメントを取得)
$ curl -X GET "https://${username}:${password}@${username}.cloudant.com/${db}/_all_docs"
結果にはidだけ渡される。ドキュメントその物を含んで欲しいときはinclude_docsフラグを付与する。
$ curl -X GET "https://${username}:${password}@${username}.cloudant.com/${db}/_all_docs?include_docs=true"
?limit=***
を付与することで取得するドキュメント数を制限できる。
(個別のドキュメントの取得)
$ curl -X GET "https://${username}:${password}@${username}.cloudant.com/${db}/${_id}"
(更新)
まずidとrev番号を確認する。
先の個別のドキュメントの取得時の結果の_id、_rev属性の値として確認できる。
それをjsonファイルに追加して対応する。それ以外は更新も新規投入と同じである。
revision番号は更新のたびに変わるため、その都度確認すること。
$ doc='{
"_id": "***","_rev": "***",
"name": "aaa",
"age": "20",
"hobbies": {
"1st": "computer",
"2nd": "ferret"
},
"timestamp": "1438354800"
}'
$ curl -v \
-H "Content-type: application/json" \
-X POST \
-d "${doc}" \
https://${username}:${password}@${username}.cloudant.com/${db}/
(削除)
idをurlパスに、rev番号をurlパラメータにしてdeleteメソッドを呼ぶ。
$ curl -X DELETE https://${username}:${password}@${username}.cloudant.com/${db}/${_id}?rev=${_rev}
◆ インデックスの作成と検索
cloudantはLuceneベースの検索ができる。つまり全文検索である。ランク付き検索、強力な照会タイプ、結果のブックマーキング、ファセット検索、フィールド検索などの機能も備えている。ややこしい点の一つなのだが、NoSQL DB とは別に全文検索機能を実装しているわけである。
(作成)
WEBコンソールから
Databases -> testdb
All Documents -> New Search Index
入力ボックスで指示していく(_design配下でインデックスは管理される)。
・ Save to Design Document: _design/newDesignDoc ※任意の名称である
・ index name: newSearch ※任意の名称である
・ Search index function:
function (doc) {
if (doc.name && doc.hobbies) {
index("name", doc.name, {"store":true});
index("hobby_1st", doc.hobbies["1st"], {"store":true});
index("hobby_2nd", doc.hobbies["2nd"], {"store":true});
index("timestamp", doc.timestamp, {"store":true});
return
}
}
・ Analyzer: Single
・ Type: Standard
インデックスは既に投入済みのドキュメントにも有効である。
(検索)
$ curl -X GET "https://${username}:${password}@${username}.cloudant.com/${db}/_design/newDesignDoc/_search/newSearch?q=name:aaa"
$ curl -X GET "https://${username}:${password}@${username}.cloudant.com/${db}/_design/newDesignDoc/_search/newSearch?q=hobby_1st:computer"
$ curl -X GET "https://${username}:${password}@${username}.cloudant.com/${db}/_design/newDesignDoc/_search/newSearch?q=timestamp:1438354*"
storeオプションの有無で検索結果に値を含むかどうかが変わる。
デフォルトではfalseである。
デフォルトではfalseである。
trueにすることで、include_docsを使わずに欲しい情報を取得することができるようになる。
nameキーで検索した結果をそれぞれ見てみる。
・storeがtrueの場合
{"total_rows":1,"bookmark":"g2wAAAABaANkAB9kYmNvcmVAZGIxMi5pYm0wMDkuY2xvdWRhbnQubmV0bAAAAAJuBAAAAADAbgQA____32poAkY_06N6AAAAAGEAag","rows":[{"id":"3f8705b307d03b42a4fd027d49b58436","order":[0.3068528175354004,0],"fields":{"name":"aaa"}}]}
・storeがfalseの場合
{"total_rows":1,"bookmark":"g2wAAAABaANkAB9kYmNvcmVAZGIxMi5pYm0wMDkuY2xvdWRhbnQubmV0bAAAAAJuBAAAAADAbgQA____32poAkY_06N6AAAAAGEAag","rows":[{"id":"3f8705b307d03b42a4fd027d49b58436","order":[0.3068528175354004,0],"fields":{}}]}
◆ map/reduceの利用1
先の全文検索のためのインデックスによる検索する手段とは別に、map/reduceを使い結果を求めることもできる。こちらがより純粋なNo SQLとしての用途である。
(作成)
WEBコンソールから
Databases -> testdb
newDesignDoc(作成したインデックス名) -> New View
入力ボックスで指示していく。
・ Design Document: _design/newDesignDoc ※すでに作成済みのものを利用
・ index name: calc ※任意の名称である
・ Map function
function(doc) {
emit(doc._id, 1);
}
mapフェーズでは"doc._id"をキーとして数字1を出力し、
reduceフェーズでそれを集約する("doc._name"でも構わない。目的に応じてmap対象を選べばよい)。
・ Reduce function: custom ※この中でsum関数を書くことにする
※
reduceの選択肢の中にすでにsum関数は用意されている。
また、用意されているreduceの中に"_stat"という関数がある。
function(keys, values, rereduce) {
if (rereduce) {
return sum(values)
} else {
return values.length
}
return sum(values)
} else {
return values.length
}
}
※
reduceの選択肢の中にすでにsum関数は用意されている。
また、用意されているreduceの中に"_stat"という関数がある。
_statは_sum, _count, _min, _max, _sumsqrの結果をまとめて出力する関数である。
reducerに_statを選んでクエリを投げると以下の結果を得られる。
$ curl -X GET "https://${username}:${password}@${username}.cloudant.com/${db}/_design/newDesignDoc/_view/calc"
{"rows":[
{"key":null,"value":{"sum":3,"count":3,"min":1,"max":1,"sumsqr":3}}
]}
◆ map/reduceの利用2
単純に特定のkeyに対して検索をしたい場合は、reduceは不要である。例えば、created_at、subject、body要素を含んでいるデータがあり、subjectが"AAA"だけの、日付による検索をし、subjectとbodyを結果として取得したい場合は以下のようにmap関数を登録しておくだけでよいだろう。
function (doc) {
msec = Date.parse(doc.created_at);
if (msec) {
if (doc.subject == "AAA") {
emit([date.getFullYear(),
date.getMonth()+1,
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds()
],
[doc.subject,
doc.body
]);
}
}
msec = Date.parse(doc.created_at);
if (msec) {
if (doc.subject == "AAA") {
emit([date.getFullYear(),
date.getMonth()+1,
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds()
],
[doc.subject,
doc.body
]);
}
}
}
"https://${username}:${password}@${username}.cloudant.com/${db}/_design/${design}/_view/${mapper}" \
--data-urlencode "startkey=[2015,8,10,12,0]" \
--data-urlencode "endkey=[2015,8,31,12,0]"
~参考~
IBM Cloudant 公式マニュアル
CloudantDBとSQL系DBの違い
日付期間でのデータ検索方法など参考になる