この記事はCouchbase Advent Calendar 2015 の13日目の記事です。
はじめに
Couchbase Server 4.0からN1QLというSQLライクな文法を持つクエリ言語が登場したことによって、従来のViewでは困難もしくは不可能であったクエリが書けるようになりました。文法はJavaScriptベースのものから慣れ親しんだSQLベースのものになり、開発速度や保守性も大幅に向上しました。
従来のViewではクエリが制限されていたがために、JSONドキュメントの構造をViewに合わせなければならないケースがありました。ここではN1QLの強力な表現力によってJSONのデータモデル自体もより柔軟に考えられるようになりめでたいという話をします。
JOINの壁
従来のViewはJOINをサポートしておらず、複数ドキュメントを取得するクエリの場合は複数回クエリを発行する必要がありました。そのためアプリケーション側のロジックが複雑になる、トランザクションもサポートしていないためクエリ発行中にデータが更新されてしまう可能性があるという問題がありました。以前はこの問題を解決するために、本来であれば複数に分割したいJSONドキュメントを単一のドキュメントに埋め込むというアプローチを取っていました。
Couchbase Server 4.0で新しく追加されたサンプルバケットの travel-sample バケットのデータを例に考えてみます。なおサンプルバケットはGUIコンソールの Settings -> Sample Buckets -> Available Samples の中にあるバケット名にチェックを入れてCreateボタンを押せばいつでも追加できます。
type:airline の airline_10 ドキュメントが航空会社を、type:route の route_46586 ドキュメントが航空便を表していますね。route_46586 ドキュメントは airlineid キーで airline_10 というドキュメントIDを保持しており、RDBのテーブル間の関連付けのようにドキュメント間で関連付けがなされています。
{
"id": 10,
"type": "airline",
"name": "40-Mile Air",
"iata": "Q5",
"icao": "MLA",
"callsign": "MILE-AIR",
"country": "United States"
}
{
"id": 46586,
"type": "route",
"airline": "Q5",
"airlineid": "airline_10",
"sourceairport": "FAI",
"destinationairport": "HKB",
"stops": 0,
"equipment": "CNA",
"schedule": [
{
"day": 0,
"utc": "06:11:00",
"flight": "Q5972"
},
{
"day": 0,
"utc": "03:07:00",
"flight": "Q5940"
},
---- 省略 ----
],
"distance": 118.2018358510763
}
N1QLであれば、以下のように容易にこのふたつのドキュメントを結合して単一のクエリで結果を返すことができます。
SELECT * FROM `travel-sample` AS route USE KEYS "route_46586" JOIN `travel-sample` AS airline ON KEYS route.airlineid;
Viewではこのようなクエリが書けないため、JSONの構造を工夫する必要がありました。例えば以下のようなドキュメント構造が考えられます。
{
"id": 46586,
"type": "route",
"airline": {
"name": "40-Mile Air",
"iata": "Q5",
"icao": "MLA",
"callsign": "MILE-AIR",
"country": "United States"
},
"sourceairport": "FAI",
"destinationairport": "HKB",
"stops": 0,
"equipment": "CNA",
"schedule": [
{
"day": 0,
"utc": "06:11:00",
"flight": "Q5972"
},
{
"day": 0,
"utc": "03:07:00",
"flight": "Q5940"
},
---- 省略 ----
],
"distance": 118.2018358510763
}
type:routeのドキュメントにtype:airlineのドキュメントを子要素として埋め込んでいます。あらかじめ単一のドキュメントにしておくことでJOINができないというクエリの弱さをカバーしていました。
このような構造にするとデータが冗長になる、データの更新性が大幅に低下するという欠点があります。例えば航空会社名が変わった際には変更対象のドキュメントのID一覧を取得し、ひとつずつJSONを更新する処理を走らせなければなりません(12/10 リリースの Couchbase Server 4.1でUPDATE、INSERT、DELETEなどのDMLが正式サポートされ、複数ドキュメントの一括更新ができるようになりました)。
ただし、ふたつのドキュメントが頻繁に同時に参照される、データの更新はほとんど発生しないといった条件であればこのような埋め込み型の構造も充分考えられると思います。
まとめ
N1QLによってクエリやアプリケーション側のロジックに引っ張られることなくJSON構造を考えられるようになりました。JSONのデータモデルの柔軟さを謳歌できます。楽しいです。
CouchbaseはちょっとViewが独特すぎて・・・という方もN1QLならとっつきやすいと思います。使ってみてください。N1QLの構文については以下が参考になります。
Querying with N1QL
最後に
モバイルのDBであるCouchbase LiteでもN1QLが使えるようになる日を楽しみにしています。