多次元インデックスを使用してデータをクエリする場合、あらかじめソート順序を定義するか、クエリ時に順序を指定して、特定の順序で結果を取得できます。結果セットが大きい場合は、オフセットベースまたはトークンベースのページングを使用して、必要なデータをすばやく特定できます。
インデックスソート
デフォルトでは、多次元インデックスはインデックスソートに基づいてデータをソートします。多次元インデックスでデータをクエリする際、このIndexSort設定によってデフォルトのソート順序が決まります。
多次元インデックスを作成する際に、カスタムIndexSortを定義できます。指定しない場合、多次元インデックスはデフォルトでプライマリキーによりソートされます。
インデックスソートは、
PrimaryKeySort(プライマリキーでソート) とFieldSort(フィールド値でソート) のみをサポートします。ネスト型のフィールドを含む多次元インデックスでは、インデックスソートはサポートされません。
クエリ時のソート
enableSortAndAggがtrueに設定されているフィールドでのみソートできます。
各クエリに対してソート順序を指定できます。多次元インデックスは、次の 4 種類のソーター (Sorter) をサポートします。ソーターを組み合わせて、多段 (マルチレベル) ソートを作成することもできます。
ScoreSort
BM25 アルゴリズムで計算される関連度スコアに基づいて結果をソートします。全文検索などのシナリオに適しています。
関連度スコアで結果をソートする必要がある場合は、ScoreSortを明示的に指定する必要があります。指定しない場合、結果は多次元インデックスのIndexSort設定に基づいてソートされます。
SearchQuery searchQuery = new SearchQuery();
searchQuery.setSort(new Sort(Arrays.asList(new ScoreSort())));PrimaryKeySort
プライマリキーで結果をソートします。
SearchQuery searchQuery = new SearchQuery();
searchQuery.setSort(new Sort(Arrays.asList(new PrimaryKeySort()))); // 昇順。
//searchQuery.setSort(new Sort(Arrays.asList(new PrimaryKeySort(SortOrder.DESC)))); // 降順。FieldSort
指定したカラムの値で結果をソートします。
単一カラムのソート
単一カラムの値で結果をソートします。
SearchQuery searchQuery = new SearchQuery();
searchQuery.setSort(new Sort(Arrays.asList(new FieldSort("col", SortOrder.ASC))));複数カラムのソート
1 つのカラムの値でソートした後、別のカラムの値でソートします。
SearchQuery searchQuery = new SearchQuery();
searchQuery.setSort(new Sort(Arrays.asList(
new FieldSort("col1", SortOrder.ASC), new FieldSort("col2", SortOrder.ASC))));フォールバックソート
Long、 Double、または Date カラムでソートする場合は、missingFieldパラメータを使用して、同じ型のフォールバックカラムを指定できます。このフォールバックは、プライマリのソートカラムに値が存在しない行に対して使用されます。
/**
* `Col_Long` を降順でソートします。行に `Col_Long` の値がない場合は、代わりに `Col_Long_sec` の値を使用します。
*/
SearchQuery searchQuery = new SearchQuery();
FieldSort fieldSort = new FieldSort("Col_Long");
// `Col_Long` の欠損値に対するフォールバックとして `Col_Long_sec` を指定します。
fieldSort.setMissingField("Col_Long_sec");
fieldSort.setOrder(SortOrder.DESC); 欠損値のソート
ドキュメントにソートフィールドが存在しない場合は、missingValueパラメータを使用して、ソート結果内での位置を制御できます。
ソート動作は次のとおりです:
missingValueをFieldSort.FIRST_WHEN_MISSINGに設定すると、ソート順序 (昇順または降順) に関係なく、欠損値のあるドキュメントは常に結果の先頭に配置されます。missingValueをFieldSort.LAST_WHEN_MISSINGに設定するか、未設定 (null) の場合、ソート順序に関係なく、欠損値のあるドキュメントは常に結果の末尾に配置されます。/** * `Col_Long` を降順でソートします。`Col_Long` の値が欠損しているドキュメントを結果の先頭に配置します。 */ SearchQuery searchQuery = new SearchQuery(); FieldSort fieldSort = new FieldSort("Col_Long"); // 欠損値のあるドキュメントを先頭に配置します。 fieldSort.setMissingValue(FieldSort.FIRST_WHEN_MISSING); fieldSort.setOrder(SortOrder.DESC); searchQuery.setSort(new Sort(Arrays.asList(fieldSort)));
複数値フィールドのソート
配列やネスト型フィールドなどの複数値フィールドでは、modeパラメータを使用して、コレクション内のどの値をソートに使用するかを指定できます。
配列内の指定した値でソートします。
// 行 doc1 と doc2 には、field1 という名前の配列フィールドが含まれています。doc1 の field1 の値は [2,3]、doc2 の値は [1,3,4] です。
// mode パラメータを設定して、配列内のどの値をソートに使用するかを指定できます。
{
// mode を SortMode.MAX に設定した場合、結果は doc2 (4 でソート) の後に doc1 (3 でソート) となります。
FieldSort fieldSort = new FieldSort("field1", SortOrder.DESC);
fieldSort.setMode(SortMode.MAX);
}
{
// mode を SortMode.MIN に設定した場合、結果は doc1 (2 でソート) の後に doc2 (1 でソート) となります。
FieldSort fieldSort = new FieldSort("field1", SortOrder.DESC);
fieldSort.setMode(SortMode.MIN);
}ネスト型のサブフィールド内の値でソートすることもできます。
// 行 doc1 と doc2 には、field1 という名前のネスト型フィールドが含まれています。
// doc1 の field1 の値は [{"name":"b", "age":1},{"name":"a", "age":7}] です。
// doc2 の field1 の値は [{"name":"a", "age":1},{"name":"c", "age":1},{"name":"d", "age":5}] です。
{
// すべてのサブ行をソートし、mode パラメータでどの値をソートに使用するかを指定します。
// mode を SortMode.MAX に設定し、age フィールドでソートした場合、結果は doc1 (7 でソート) の後に doc2 (5 でソート) となります。
FieldSort fieldSort = new FieldSort("field1.age", SortOrder.DESC);
fieldSort.setMode(SortMode.MAX);
String path = "field1";
NestedFilter nestedFilter = new NestedFilter(path, QueryBuilders.matchAll().build());
fieldSort.setNestedFilter(nestedFilter);
}
{
// age=1 のサブ行のみをソートし、mode パラメータでどの値をソートに使用するかを指定します。
{
// mode を SortMode.MAX に設定し、name フィールドでソートした場合、結果は doc2 ("c" でソート) の後に doc1 ("b" でソート) となります。
FieldSort fieldSort = new FieldSort("field1.name", SortOrder.DESC);
fieldSort.setMode(SortMode.MAX);
String path = "field1";
NestedFilter nestedFilter = new NestedFilter(path, QueryBuilders.term("field1.age",1).build());
fieldSort.setNestedFilter(nestedFilter);
}
{
// mode を SortMode.MIN に設定し、name フィールドでソートした場合、結果は doc1 ("b" でソート) の後に doc2 ("a" でソート) となります。
FieldSort fieldSort = new FieldSort("field1.name", SortOrder.DESC);
fieldSort.setMode(SortMode.MIN);
String path = "field1";
NestedFilter nestedFilter = new NestedFilter(path, QueryBuilders.term("field1.age",1).build());
fieldSort.setNestedFilter(nestedFilter);
}
}GeoDistanceSort
指定した地理座標ポイントからの距離で結果をソートします。
SearchQuery searchQuery = new SearchQuery();
// `geo` (Geopoint) カラムの値からポイント "0,0" までの距離で結果をソートします。
Sort.Sorter sorter = new GeoDistanceSort("geo", Arrays.asList("0, 0"));
searchQuery.setSort(new Sort(Arrays.asList(sorter)));ページング方法
limitとoffsetパラメータ、またはトークンを使用してページングできます。
limit と offset によるページング
limitとoffsetを使用してページングできますが、limitとoffsetの合計は 100,000 を超えてはならず、limitの最大値は 100 です。
limitを 1,000 に引き上げる方法については、「How can I increase the query result limit of the Search API to 1,000 for a search index?」をご参照ください。
指定しない場合、limitのデフォルト値は 10、offsetのデフォルト値は 0 です。
SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(new MatchAllQuery());
searchQuery.setLimit(100);
searchQuery.setOffset(100);トークンベースのページング
ディープページングでは、深さ制限がないため、トークンベースのページングを推奨します。
クエリに返却すべき結果が残っている場合、レスポンスにはnextTokenが含まれます。次のリクエストでこのトークンを使用して、次ページの結果を取得します。
デフォルトでは、トークンベースのページングでは結果を前方向にのみ進めます。ただし、トークンはページングセッションの期間中は有効なため、以前のトークンをキャッシュすることで逆方向のページングを実装できます。
nextToken を永続化するか、フロントエンドアプリケーションに送信するには、Base64 エンコードする必要があります。nextToken は文字列ではなく、バイト配列です。new String(nextToken) を使用して直接変換すると、トークンが破損し、データ損失が発生します。
トークンは、デフォルトのインデックスソートであってもカスタムソート順序であっても、前回リクエストのソート順序を保持します。そのため、トークンベースのリクエストではSortパラメータを指定できません。また、トークン自体が開始位置を決定するため、offsetパラメータも使用できません。
ネスト型のフィールドを含む多次元インデックスでは、インデックスソートはサポートされません。このような多次元インデックスから結果をページングするには、クエリでソート順序を指定する必要があります。指定しない場合、返却すべき結果が残っていても、サーバーはnextTokenを返しません。
private static void readMoreRowsWithToken(SyncClient client) {
SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(new MatchAllQuery());
searchQuery.setGetTotalCount(true);// これを true に設定すると、マッチした行の総数が返されます。
// テーブル名 (例:sampleTable) と多次元インデックス名 (例:sampleSearchIndex) を指定します。多次元インデックス名は、Tablestore コンソールでテーブルの [Indexes] タブに表示されるほか、SDK を使用して多次元インデックスを一覧表示して確認できます。
SearchRequest searchRequest = new SearchRequest("<TABLE_NAME>", "<SEARCH_INDEX_NAME>", searchQuery);
SearchResponse resp = client.search(searchRequest);
if (!resp.isAllSuccess()) {
throw new RuntimeException("not all success");
}
List<Row> rows = resp.getRows();
while (resp.getNextToken()!=null) { // `nextToken` が null の場合は、すべてのデータを取得済みであることを意味します。
// nextToken を取得します。
byte[] nextToken = resp.getNextToken();
{
// nextToken を永続化する、またはフロントエンドアプリケーションに送信する必要がある場合は、Base64 エンコードを使用して文字列に変換します。
// トークン自体は文字列ではありません。new String(nextToken) を使用して直接変換するとトークンが破損します。
String tokenAsString = Base64.toBase64String(nextToken);
// 文字列をバイト配列にデコードします。
byte[] tokenAsByte = Base64.fromBase64String(tokenAsString);
}
// 次のリクエストにトークンを設定します。
searchRequest.getSearchQuery().setToken(nextToken);
resp = client.search(searchRequest);
if (!resp.isAllSuccess()) {
throw new RuntimeException("not all success");
}
rows.addAll(resp.getRows());
}
System.out.println("RowSize: " + rows.size());
System.out.println("TotalCount: " + resp.getTotalCount());// このレスポンスで返された行数ではなく、マッチした行の総数を出力します。
}よくある質問
関連ドキュメント
多次元インデックスは、 Term query、 Terms query、 MatchAll query、 Match query、 MatchPhrase query、 Prefix query、範囲クエリ、 Wildcard query、 Geo query、 Vector query、 Boolean query、 Nested query、 Exists query など、さまざまなクエリタイプをサポートします。多次元データの取得には、用途に応じて適切なクエリタイプを選択できます。
結果セットをソートまたはページングするには、ソート機能とページング機能を使用します。詳細については、「結果のソートとページング」をご参照ください。
結果セットを特定のカラムでグループ化し、各グループにつき 1 つのドキュメントのみを返すには、コラプシング機能を使用します。詳細については、「コラプシング」をご参照ください。
最大値または最小値の検索、合計の計算、行数のカウントなどのデータ分析には、 Search API の集約機能または SQL クエリを使用できます。詳細については、「集約」および「SQL query」をご参照ください。
全体の結果順序が重要ではない高速なデータエクスポートでは、 ParallelScan API と ComputeSplits API を使用して、データを並列にエクスポートできます。詳細については、「Export data in parallel」をご参照ください。