SQLからCouchdbへ

SQL的にこういうクエリはCouchdbではこうするという感じのメモ

いくらNoSQLと言われてもSQLに馴染んできた人にとっては、じゃあどうするのということで、SQL的にこういう事はCouchdbではこんな風に呼び出せるというのをまとめてみました。

※デザインドキュメントの作り方などは今回はすっ飛ばします。使っているデザインドキュメントは下記参照。

http://gist.github.com/563730

SELECT * from car(全件取得)

テーブルから全件取得します。mapコードは下記のように、keyもvalueは何でも構わないです。全件取得するだけですので、単純にmapするだけでreduceは必要ありません。複数の形式のドキュメントが入っていてだし分けたい場合などはここで条件判定すればできます。下記の例だとtypeに"car"と指定されているもののリストを出すようにしています。

// map
function(doc) {
    if (doc.type == 'car') {
       emit(null, null);
    }
}

あとはリクエストを投げてみると

$ curl -X GET http://localhost:5984/dbname/_design/select/_view/all?include_docs=true

下記のようにJSONで出力結果が得られます。

{"total_rows":2,"offset":0,"rows":[
{"id":"b0b5c3537c0c39e32c6045a4260025f0","key":null,"value":null,"doc":{"_id":"b0b5c3537c0c39e32c6045a4260025f0","_rev":"3-99978135684ebb90ff34416214f0e19c","type":"car","maker":"AMG","model":"C65","year":2009,"price":10200000}},
{"id":"b0b5c3537c0c39e32c6045a426002fa2","key":null,"value":null,"doc":{"_id":"b0b5c3537c0c39e32c6045a426002fa2","_rev":"2-2e56ce81f3608095b296f94cfd35028b","type":"car","maker":"BMW","model":"M3","year":2009,"price":10200000}}
]}

リクエスト時にinclude_docs=trueを付けることですることで、ドキュメント全体も取得できるようになります。欲しい情報が決まっているならinclude_docsを指定せずに、emitするときにkey,valueを指定しても良いかも。

このままだと全件一気に出力されるのですが、下記のようにskip,limitのパラメーターを付けることでページングさせることも可能です。

curl -X GET http://localhost:5984/dbname/_design/select/_view/all?include_docs=true&skip=10&limit=10

descendingパラメーターを使用することによりSQLで言う、descをつけた時と同じように、降順でソートすることも可能です。

curl -X GET http://localhost:5984/dbname/_design/select/_view/all?include_docs=true&descending=true

SELECT count(*) from TABLE (レコード数の取得)

テーブル(ドキュメント)のレコードを数を取得してみたいと思います。
下記のようなdesignviewを用意します。

//map
function(doc) {
    emit(null, 1);
}
// reduce
function(keys, values) {
    sum(values) 
}

mapでは単純にvalue値として1とmapします。それをreduceで集計し件数を出します。5件のレコードならば1+1+1+1+1=5ということみたいです。

そしてcouchdbにリクエストを投げます。

http://localhost:5984/dbname/_design/select/_view/count

結果次のように帰ってきます。

{"rows":[
{"key":null,"value":10}
]}

この場合10件のドキュメントが見つかりました。
keyがnullなのは不思議なのですが全件reduceするとkeyは関係なくなるので、必ずnullで良いみたいです。

SELECT * from TABLE WHERE maker = 'AMG'

テーブルからmakerがAMGであるのドキュメントの一覧を取得してみます。

mapでkeyにmakerを指定しておくだけでOKです。

// map
function (doc) {
  emit(doc.maker, null);
}

下記のようにリクエストします。keyに渡す文字列はjsonである必要があるので、”で囲みます。

http://localhost:5984/test/_design/select/_view/by_maker?key=%22AMG%22&include_docs=true

SELECT maker, model from TABLE WHERE maker = 'AMG' group by model

makerをAMGで絞り込んだ上model名でgroupします。

// map
function (doc) {
  emit([doc.maker, doc.model], 1);
}
// reduce
function (keys, values, rr) {
  return sum(values);
}

emit時にキーに配列でmakerとmodelを指定します。

starkeyとendkeyをうまく指定し,groupを指定することでgroup by的なアクセスが出来ます。

http://localhost:5984/dbname/_design/select/_view/by_maker?startkey=["AMG"]&endkey=["AMG",{}]&group=true

AMGのmodel一覧が帰ってきました。

{"rows":[
{"key":["AMG","C65"],"value":1},
{"key":["AMG","E55"],"value":1}
]}

startkeyとかendkeyとかまだあまり理解できてないので、ちょっと調べてみます。