Couchbase Lite機能解説：クエリ② WHERE句

## はじめに

Couchbase Lite　は、SQLと同様に、WHERE句を使用して、クエリによって返されるドキュメントを選択できます。

WHERE句を表現するためには、`Expression`(式)クラスが使われます。WHERE句のフィルタリングを実現するために、任意の数の式をチェーンできます。

## 比較演算子

WHERE句では、条件を指定するために、比較演算子を使用することができます。

以下の例では、`equalTo`演算子を使用して、「type」プロパティが「hotel」に等しいドキュメントを選択しています。

以下は、「type」プロパティの値として「hotel」を持つドキュメントの例です。

```JSON
[
  { 
    "id": "hotel123",
    "type": "hotel",
    "name": "Hotel Ghia"
  },
  { 
    "id": "hotel456",
    "type": "hotel",
    "name": "Hotel Deluxe",
  }
]
```

以下のコードの、`where(Expression.property("type").equalTo(Expression.string("hotel")))`の部分で、比較条件を指定しています。

```JAVA
Query query = QueryBuilder
    .select(SelectResult.all())
    .from(DataSource.database(database))
    .where(Expression.property("type").equalTo(Expression.string("hotel")))
    .limit(Expression.intValue(10));
for (Result result : query.execute()) {
    Dictionary all = result.getDictionary(DATABASE_NAME);
    Log.i("Sample", String.format("name -> %s", all.getString("name")));
    Log.i("Sample", String.format("type -> %s", all.getString("type")));
}
```

## 配列
`ArrayFunction`コレクション演算子は、指定された値が配列に存在するかどうかを確認するために利用できます。

### CONTAINS
次の例では、`ArrayFunction`を使用して、`public_likes`配列プロパティに「ArmaniLangworth」と等しい値が含まれているドキュメントを検索します。

以下のように、プロパティの値が配列となっているドキュメントに対して用います。

```JSON
{
    "_id": "hotel123",
    "name": "Apple Droid",
    "public_likes": ["Armani Langworth", "Elfrieda Gutkowski", "Maureen Ruecker"]
}
```

`.and(ArrayFunction.contains(Expression.property("public_likes"), Expression.string("Armani Langworth")`というように、条件を連結して指定することができます。

```JAVA
Query query = QueryBuilder
    .select(
        SelectResult.expression(Meta.id),
        SelectResult.property("name"),
        SelectResult.property("public_likes"))
    .from(DataSource.database(database))
    .where(Expression.property("type").equalTo(Expression.string("hotel"))
        .and(ArrayFunction
            .contains(Expression.property("public_likes"), Expression.string("Armani Langworth"))));
for (Result result : query.execute()) {
    Log.i(
        "Sample",
        String.format("public_likes -> %s", result.getArray("public_likes").toList()));
}
```

## IN

IN演算子は、条件に指定する対象をリストする場合に使います。

次の例では`first`、`last`または`username`のいずれかのプロパティの値が「Armani」に等しいドキュメントを検索します。

```JAVA
Expression[] values = new Expression[] {
    Expression.property("first"),
    Expression.property("last"),
    Expression.property("username")
};

Query query = QueryBuilder.select(SelectResult.all())
    .from(DataSource.database(database))
    .where(Expression.string("Armani").in(values));
```

## LIKE

#### 文字列照合
文字列マッチングのために、`Like（）`演算子を使用することができます。

`like`演算子では、大文字と小文字を区別されます。
大文字と小文字を区別しないマッチングを実行するには、`Function.lower`または`Function.upper`を使用して、大文字と小文字の違いを取り除きます。


以下のクエリは、大文字の使用方法に関係なく、「name」プロパティが、文字列「Royal Engineers Museum」に一致する「landmark」タイプのドキュメントを返します（したがって、「Royal Engineers Museum」、「ROYAL ENGINEERS　MUSEUM」など）。


```JAVA
Query query = QueryBuilder
    .select(
        SelectResult.expression(Meta.id),
        SelectResult.property("country"),
        SelectResult.property("name"))
    .from(DataSource.database(database))
    .where(Expression.property("type").equalTo(Expression.string("landmark"))
        .and(Function.lower(Expression.property("name")).like(Function.Expression.string("royal engineers museum")))));
for (Result result : query.execute()) {
    Log.i("Sample", String.format("name -> %s", result.getString("name")));
}
```

#### ワイルドカードマッチ:`%`
`like`式内で`%`記号を使用して、0個以上の文字に対してワイルドカード一致を行うことができます。

ワイルドカードを使用すると、検索文字列にあいまいさを持たせることができます。

以下の例では、「name」プロパティが、「eng」で始まり、0個以上の文字が続き、文字「e」が続き、0個以上の文字が続く任意の文字列と一致する「type」が「landmark」のドキュメントを検索します。

検索で大文字と小文字を区別しないようにするために`Function.lower`を使用しています。

次のクエリはtype、「Engineers」、「engine」、「english egg」、「EnglandEagle」などの名前に一致する「landmark」ドキュメントを返します。一致が単語の境界にまたがることがあることに注意してください。

```JAVA
Query query = QueryBuilder
    .select(
        SelectResult.expression(Meta.id),
        SelectResult.property("country"),
        SelectResult.property("name"))
    .from(DataSource.database(database))
    .where(Expression.property("type").equalTo(Expression.string("landmark"))
    .and(Function.lower(Expression.property("name")).like(Expression.string("eng%e%"))));
for (Result result : query.execute()) {
    Log.i("Sample", String.format("name -> %s", result.getString("name"))); }
```

#### キャラクターマッチ:`_`
like式の中で`_`記号を使用して、単一の文字に対してワイルドカード照合を行うことができます。

以下の例では、「name」プロパティが「eng」で始まり、その後に正確に4つのワイルドカード文字が続き、文字「r」で終わる任意の文字列(「Engineer」、「engineer」など)に一致する「type」プロパティが、「landmark」のドキュメントを探しています。



```JAVA
Query query = QueryBuilder
    .select(
        SelectResult.expression(Meta.id),
        SelectResult.property("country"),
        SelectResult.property("name"))
    .from(DataSource.database(database))
    .where(Expression.property("type").equalTo(Expression.string("landmark"))
    .and(Function.lower(Expression.property("name")).like(Expression.string("eng____r"))));
for (Result result : query.execute()) {
    Log.i("Sample", String.format("name -> %s", result.getString("name"))); }
```

#### 正規表現演算子
like式のワイルドカードと同様に、`regex`ベースのパターンマッチングを使用すると、検索文字列にあいまいさの要素を導入できます。

`regex`演算子では大文字と小文字が区別されることに注意してください。

Couchbase Liteで使用される正規表現仕様の詳細については、[cplusplus.com正規表現リファレンスページ](http://www.cplusplus.com/reference/regex/ECMAScript/)を参照してください。


この例で使われている表現(`Expression.string("\\beng.*e\\b")`)では、name「eng」で始まり文字「e」で終わる任意の文字列に一致するプロパティを持つドキュメントを返します。

```JAVA
Query query = QueryBuilder
    .select(
        SelectResult.expression(Meta.id),
        SelectResult.property("country"),
        SelectResult.property("name"))
    .from(DataSource.database(database))
    .where(Expression.property("type").equalTo(Expression.string("landmark"))
    .and(Function.lower(Expression.property("name")).regex(Expression.string("\\beng.*e\\b"))));            ResultSet rs = query.execute();
for (Result result : query.execute()) {
    Log.i("Sample", String.format("name -> %s", result.getString("name"))); }
```



## 削除ドキュメントの検索

Couchbase Liteでは、削除されたドキュメント（トゥームストーン）をクエリできます 。`where(Meta.deleted)`のように記述します。


次の例は、データベース内の削除されたドキュメントをクエリしています。返される結果として、ドキュメントID(`Meta.id`)を指定しています。

```JAVA
// Query documents that have been deleted
Where query = QueryBuilder
    .select(SelectResult.expression(Meta.id))
    .from(DataSource.database(database))
    .where(Meta.deleted);
```

## Date/Timeデータ・関数

Couchbase Liteは、JSONドキュメントのプロパティとして、ISO8601形式の日付をGMT/UTCタイムゾーンで内部的に保存する[Dateタイプ](https://docs.couchbase.com/couchbase-lite/current/android/querybuilder.html#initializers)をサポートしています。

Couchbase　Liteでは、Dateタイプの値の比較を実行することができます。

ISO8601形式と、他のDate/Timeフォーマットとを一致させるために、次の4つの関数が提供されています。


#### `StringToMillis`
ISO8601に従って有効にフォーマットされた文字列を入力として取ります。最終結果は、クエリビルダーにさらに入力できる式(Expression)オブジェクト（数値コンテンツを含む）になります。

#### `StringToUTC`
入力は、有効にフォーマットされたISO8601文字列になります。最終結果は、クエリビルダーにさらに入力できる式(Expression)オブジェクト（文字列コンテンツを含む）になります。

#### `MillisToString`
入力は、Unixエポックからのミリ秒を表す数値です。最終結果は、クエリビルダーにさらに入力できる式(Expression)オブジェクト（デバイスのタイムゾーンでISO 8601文字列として日付と時刻を表す文字列コンテンツを含む）になります。

#### `MillisToUTC`
入力は、Unixエポックからのミリ秒を表す数値です。最終結果は、クエリビルダーにさらに入力できる式(Expression)オブジェクト（日付と時刻をUTC ISO8601文字列として表す文字列コンテンツを含む）になります。

## 参考情報

https://docs.couchbase.com/couchbase-lite/current/android/querybuilder.html