はじめに
ここでは、Couchbase Liteデータベースで配列を含むJSONドキュメントへクエリする方法について解説します。
なお、Couchbase Mobileについては、Couchbase Mobileアプリケーション開発へのロードマップに記事をまとめている他、(これらの記事を元に構成した)以下の電子書籍を無償で頒布しています。
また、Couchbase Mobileは、Couchbase LiteとCouchbase Serverとのデータ同期機能を提供します。Couchbase Serverの存在意義、機能詳細、利用方法等については、拙著NoSQLドキュメント指向データベースCouchbase Serverファーストステップガイド(インプレスR&D刊)や、NoSQL/JSONデータベースCouchbase Server理解・活用へのロードマップにまとめてある記事をご参考ください。
配列データへのクエリ
配列は、JSONデータモデリングの不可欠な要素です。ここでは、配列データへのクエリについて説明します。
ここで説明する例ではiOS用のSwiftを使用していますが、シンタックス上の違いはあれど、同じクエリインターフェイスがAndroid等他のプラットフォームでも利用可能です。
サンプルプロジェクト
サンプルのSwiftプロジェクトに興味がある場合は、以下の手順に従ってください
GitHubからiOS Swift Playgroundのクローンを作成します
$ git clone https://github.com/couchbaselabs/couchbase-lite-ios-api-playground
対応するREADMEファイルのインストール手順に従って、構築して実行します。
データモデル
ここでは、ホテルに関する情報を表す以下のJSONドキュメントモデルを用いて説明します。
(簡潔化のために、サンプルデータのモデルからいくつかのプロパティを省略しています)。
このデータモデルには、文字列の配列(public_likes
)や、ネストされたオブジェクトを要素として持つ配列(reviews
)が含まれています。
{
"type": "hotel",
"name": "Medway Youth Hostel",
"address": "Capstone Road, ME7 3JE",
"city": "Medway",
"country": "United Kingdom",
"description": "blah blah",
"public_likes": [
"Julius Tromp I",
"Corrine Hilll"
],
"reviews": [
{
"author": "Ozella Sipes",
"content": "blah blah.",
"date": "2013–06–22 18:33:50 +0300",
"ratings": {
"Cleanliness": 5,
"Location": 4,
"Overall": 4,
"Rooms": 3,
"Service": 5,
"Value": 4
}
},
{
"author": "Jeremy Snapes",
"content": "blah blah.",
"date": "2013–05–05 18:33:50 +0300",
"ratings": {
"Cleanliness": 2,
"Location": 2,
"Overall": 4,
"Rooms": 3,
"Service": 5,
"Value": 4
}
}
],
"url":"http://www.yha.org.uk",
"vacancy": true
}
配列の要素による検索
以下の例では、public_like
配列プロパティに「Corrine Hillll」の値が含まれているドキュメントを検索しています。
let searchQuery = QueryBuilde.select(SelectResult.expression(Meta.id),
SelectResult.expression(Expression.property("name")),
SelectResult.expression(Expression.property("public_likes")))
.from(DataSource.database(db))
.where(Expression.property("type").equalTo(Expression.string("hotel"))
.and(ArrayFunction.contains(Expression.property("public_likes"), value: Expression.string("Corrine Hilll"))))
ArrayFunction.contains
のように、ArrayFunction
クラスのcontains
(スタティック)メソッドを利用します。
配列サイズ取得
以下の例では、 配列のサイズを取得します。
let searchQuery = QueryBuilder.select(SelectResult.expression(Meta.id),
SelectResult.expression(Expression.property("name")),
SelectResult.expression(ArrayFunction.length(Expression.property("public_likes"))).as("NumLikes"))
.from(DataSource.database(db))
.where(Expression.property("type").equalTo(Expression.string("hotel")))
.limit(Expression.int(limit))
ArrayFunction.length(Expression.property("public_likes"))
のように、ArrayFunction
クラスのlength
(スタティック)メソッドを利用します。
また、as("NumLikes"))
のように、サイズを取得した結果に「NumLikes」という名前をつけます。
式の結果をエイリアスしない場合、プロパティキーは自動的に生成されるため、明示的に名前をつけることによって、プログラムの可読性を増すことができます。
配列メンバーの評価
ArrayFunction.contains
メソッドを使用して、指定された配列に特定の値が含まれているかどうかを確認できることを紹介しましたが、Couchbase Liteには、より、強力なフィルタリング機能が用意されています。
以下の例は、public_like
配列プロパティに「Cor」から始まる値が含まれているドキュメントを検索しています。
let VAR_LIKEDBY = ArrayExpression.variable("likedby")
let searchQuery = QueryBuilder.select(SelectResult.expression(Meta.id),
SelectResult.expression((Expression.property("public_likes"))))
.from(DataSource.database(db))
.where(Expression.property("type").equalTo(Expression.string("hotel"))
.and(ArrayExpression.any(VAR_LIKEDBY).in(Expression.property("public_likes"))
.satisfies(VAR_LIKEDBY.like(Expression.string("Cor%")))))
.limit(Expression.int(limit))<code>
まず、public_likes
配列内のすべての要素(要素のイテレーション)を表すために、VAR_LIKEDBY
という変数を宣言しています(ここで用いる名称は任意のものです。いわばFORループの内部で用いられる変数と考えると理解しやすいでしょう)。
この変数と評価対象の配列との組み合わせで、ArrayExpression.any(VAR_LIKEDBY).in(Expression.property("public_likes"))
のように、an <変数> in <配列>
という構文を用いて、クエリを構築します。
それに続く、.satisfies(VAR_LIKEDBY.like(Expression.string("Cor%")))
では、満たすべき条件〜項目の値が「Cor」で始まるかどうか〜チェックしています。
インデックスによる配列アクセス
以下のように、インデックス指定により要素をクエリすることができます。
let searchQuery = QueryBuilder.select(SelectResult.expression(Meta.id),
SelectResult.expression(Expression.property("name")),
SelectResult.expression(Expression.property("public_likes[0]")))
.from(DataSource.database(db))
.where(Expression.property("type").equalTo(Expression.string("hotel")))
.limit(Expression.int(limit))
ここで、Expression.property("public_likes[0]")
は、public_likes
配列のはじめの要素を指します。
ネストされた配列の評価
ネストされた配列のメンバーを評価できます。
上記のデータモデルからわかるように、reviews
プロパティはオブジェクトの配列を保持します。各オブジェクトにはratings
という(ディクショナリ)プロパティが含まれています。
次のクエリ、複数種類存在するレーティングの内、Overall
での評価が4以上のドキュメントを返します。
let VAR_OVERALL = ArrayExpression.variable("review.ratings.Overall")
let VAR_REVIEWS = ArrayExpression.variable("review")
let searchQuery = QueryBuilder.select(SelectResult.expression(Meta.id),
SelectResult.expression(Expression.property("name")))
.from(DataSource.database(db))
.where(Expression.property("type").equalTo(Expression.string("hotel"))
.and(ArrayExpression.any(VAR_REVIEWS).in(Expression.property("reviews"))
.satisfies(VAR_OVERALL.greaterThanOrEqualTo(Expression.int(4)))))
.limit(Expression.int(limit))
まず、review.ratings.Overall
要素を表す変数を宣言しています。
そして、reviews
配列内のすべての要素を表す変数を宣言しています
ArrayExpression.any(VAR_REVIEWS).in(Expression.property("reviews")) .satisfies(VAR_OVERALL.greaterThanOrEqualTo(Expression.int(4))
が条件指定箇所ですが、any ~ in ~ satisfies
という全体の構造は、上述の単純なケースと同じであることが同じです。
satisfies
メソッドの引数として、ネストされたオブジェクトのプロパティをドット表記で指定した変数を定義することによって.greaterThanOrEqualTo(Expression.int(4))
のように、そのプロパティの値を条件として、フィルタリングを行うことができます。
関連情報