Tablestore SDK for Java を使用して多次元インデックスをクエリする場合、インデックスソートまたはクエリタイムソートを使用して結果の順序を制御し、オフセットまたはトークンを使用して結果をページ分割します。
前提条件
Tablestore SDK for Java をインストールして、クライアントを初期化します。
仕組み
検索インデックスは、以下のソートメカニズムに対応しています:
インデックスソート:多次元インデックスを作成する際に、
IndexSchema.indexSortを構成してデフォルトの結果順序を定義します。インデックスソートが構成されていない場合、結果はプライマリキーでソートされます。インデックスソートはPrimaryKeySortとFieldSortのみをサポートします。Nested フィールドを含む多次元インデックスは、インデックスソートをサポートしません。クエリタイムソート:個別のクエリに対して
SearchQuery.sortを構成します。結果は、関連性スコア、プライマリキー、フィールド値、または地理的距離によってソートできます。複数のソーターを組み合わせて、複数レベルのソートを行うことも可能です。プライマリキーフィールドを除き、ソートフィールドは多次元インデックススキーマでenableSortAndAggをtrueに設定する必要があります。
プライマリキーソーター以外のクエリタイムソーターが指定された場合、サーバーはデフォルトでプライマリキーソーターを追加し、同じソート値を持つ行が確定的な順序になるようにします。この動作を無効にするには、Sort.disableDefaultPkSorter を true に設定します。
大規模な結果セットには、次のいずれかのページネーションメソッドを使用します。
|
メソッド |
ユースケース |
特徴 |
|
limit と offset |
結果セットが 100,000 行以下で、特定の位置にアクセスする必要がある場合。 |
ページジャンプをサポートします。 |
|
トークン |
ディープページネーション、またはすべての結果を順次読み取る場合。 |
100,000 行の深さ制限はありませんが、結果は順次読み取りしかできません。 |
search メソッドを呼び出してデータをクエリします。
SearchResponse search(SearchRequest request)
次の例では、score フィールドで降順にソートし、次にプライマリキーで昇順にソートして、最初の 10 行を返します。
SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(new MatchAllQuery());
searchQuery.setLimit(10);
searchQuery.setSort(new Sort(Arrays.<Sort.Sorter>asList(
new FieldSort("score", SortOrder.DESC),
new PrimaryKeySort(SortOrder.ASC))));
SearchRequest request =
new SearchRequest("example_table", "example_index", searchQuery);
SearchResponse response = client.search(request);
パラメーター
クエリリクエスト
request のタイプは SearchRequest です。次の表にそのパラメーターを示します。
|
名前 |
タイプ |
説明 |
|
tableName (必須) |
String |
データテーブルの名前です。 |
|
indexName (必須) |
String |
検索インデックスの名前。 |
|
searchQuery (必須) |
SearchQuery |
クエリ条件、およびソートとページネーションの構成です。 |
|
columnsToGet (任意) |
SearchRequest.ColumnsToGet |
返す列です。このパラメーターが構成されていない場合、プライマリキー列のみが返されます。 |
クエリ構成
request.searchQuery のタイプは SearchQuery です。次の表では、ソートとページネーションに関連するパラメーターのみを説明します。
|
名前 |
タイプ |
説明 |
|
query (必須) |
Query |
クエリ条件です。 |
|
sort (任意) |
Sort |
クエリタイムソートの構成です。このパラメーターが構成されていない場合、インデックスソートが使用されます。トークンベースのページネーションでは、このパラメーターを構成しないでください。 |
|
offset (任意) |
Integer |
現在のクエリが開始される位置です。デフォルト値: |
|
limit (任意) |
Integer |
返す行の最大数です。デフォルト値: |
|
token (任意) |
byte[] |
ページネーショントークンです。前の応答の |
|
trackTotalCount (任意) |
int |
カウントする一致行の予想最大数です。デフォルト値: |
ソート構成
request.searchQuery.sort のタイプは Sort です。次の表にそのパラメーターを示します。
|
名前 |
タイプ |
説明 |
|
sorters (必須) |
|
ソーターのリストです。リストの順序によって、複数レベルのソートの優先度が決まります。サポートされているソーターは |
|
disableDefaultPkSorter (任意) |
Boolean |
サーバーが自動的にプライマリキーソーターを追加するのを防ぐかどうかを指定します。デフォルト値: |
関連性スコアによるソート
ScoreSort は、BM25 アルゴリズムを使用して計算された関連性スコアで行をソートします。次の表にそのパラメーターを示します。
|
名前 |
タイプ |
説明 |
|
order (任意) |
SortOrder |
ソート順です。 |
関連性スコアでソートするには、明示的に ScoreSort を構成します。そうしない場合、インデックスソートが使用されます。
プライマリキーによるソート
PrimaryKeySort は、プライマリキーで行をソートします。次の表にそのパラメーターを示します。
|
名前 |
タイプ |
説明 |
|
order (任意) |
SortOrder |
ソート順です。デフォルト値: |
フィールドによるソート
FieldSort は、フィールド値で行をソートします。次の表にそのパラメーターを示します。
|
名前 |
タイプ |
説明 |
|
fieldName (必須) |
String |
ソートフィールドの名前です。フィールドに対してソートと集約を有効にする必要があります。 |
|
order (任意) |
SortOrder |
ソート順です。デフォルト値: |
|
mode (任意) |
SortMode |
複数値フィールドをソートするときに使用する値です。 |
|
missingFields (任意) |
|
フォールバックソートフィールドのリストです。現在のソートフィールドが欠落している場合、リスト内で値を持つ最初のフィールドが使用されます。フォールバックフィールドは、ソートフィールドと同じタイプである必要があります。 |
|
missingValue (任意) |
ColumnValue |
ソートフィールドとすべてのフォールバックフィールドが欠落している場合に使用するソート値です。このパラメーターを |
|
nestedFilter (任意) |
NestedFilter |
Nested パスとソートに参加する子行を指定する Nested ソート構成です。このパラメーターは、Nested サブフィールドをソートする場合にのみ構成します。 |
Nested フィルター
FieldSort.nestedFilter のタイプは NestedFilter です。次の表にそのパラメーターを示します。
|
名前 |
タイプ |
説明 |
|
path (必須) |
String |
Nested フィールドのパスです。 |
|
query (必須) |
Query |
ソートに参加する Nested 子行を選択するクエリ条件です。すべての子行を使用するには、パラメーターを |
地理的距離によるソート
GeoDistanceSort は、地理的なポイントフィールドとターゲットポイント間の距離で行をソートします。次の表にそのパラメーターを示します。
|
名前 |
タイプ |
説明 |
|
fieldName (必須) |
String |
Geopoint フィールドの名前です。 |
|
points (必須) |
|
ターゲットの地理的ポイントです。各ポイントは |
|
order (任意) |
SortOrder |
ソート順です。 |
|
mode (任意) |
SortMode |
複数の距離が存在する場合に使用する値です。サポートされている値は |
|
distanceType (任意) |
GeoDistanceType |
距離計算メソッドです。 |
|
nestedFilter (任意) |
NestedFilter |
Nested ソート構成です。このパラメーターは、Nested サブフィールドをソートする場合にのみ構成します。 |
返される列
request.columnsToGet のタイプは SearchRequest.ColumnsToGet です。返される列がデータテーブルから読み取られる必要があるかどうかは、limit の最大値に影響します。
|
名前 |
タイプ |
説明 |
|
columns (任意) |
|
返す属性列の名前です。指定されたすべての属性列がインデックス化され、ストアが有効になっている場合にのみ、データを多次元インデックスから直接読み取ることができます。 |
|
returnAll (任意) |
boolean |
データテーブル内のすべての属性列を返すかどうかを指定します。デフォルト値: |
|
returnAllFromIndex (任意) |
boolean |
検索インデックスに保存されているすべての属性列を返すかどうかを指定します。デフォルト値: |
応答
search メソッドは SearchResponse を返します。次の表では、ソートとページネーションに関連するフィールドについて説明します。
|
名前 |
タイプ |
説明 |
|
rows |
|
現在のクエリによって返された行です。 |
|
searchHits |
|
検索ヒットです。 |
|
totalCount |
long |
一致した行の数です。 |
|
nextToken |
byte[] |
次のページのトークンです。 |
|
isAllSuccess |
boolean |
すべてのインデックスパーティションがクエリされたかどうかを示します。 |
例
インデックスソートの構成
次の例では、多次元インデックスの作成時に score フィールドをインデックスソートフィールドとして構成します。クエリにソートが構成されていない場合、結果は score の昇順で返されます。
FieldSchema score = new FieldSchema("score", FieldType.LONG)
.setEnableSortAndAgg(true);
IndexSchema indexSchema = new IndexSchema();
indexSchema.setFieldSchemas(Collections.singletonList(score));
indexSchema.setIndexSort(new Sort(
Collections.<Sort.Sorter>singletonList(
new FieldSort("score", SortOrder.ASC))));
関連性スコアによるソート
次の例では、BM25 関連性スコアの降順で結果を返します。
TermQuery termQuery = new TermQuery();
termQuery.setFieldName("category");
termQuery.setTerm(ColumnValue.fromString("book"));
SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(termQuery);
searchQuery.setSort(new Sort(
Collections.<Sort.Sorter>singletonList(new ScoreSort())));
フィールド値の欠落の処理
次の例では、score フィールドで降順に行をソートします。行にこのフィールドが含まれていない場合、score_backup の値が使用されます。両方のフィールドが欠落している場合、その行は最後に配置されます。
FieldSort fieldSort = new FieldSort("score", SortOrder.DESC);
fieldSort.setMissingFields(Collections.singletonList("score_backup"));
fieldSort.setMissingValue(FieldSort.LAST_WHEN_MISSING);
SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(new MatchAllQuery());
searchQuery.setSort(new Sort(
Collections.<Sort.Sorter>singletonList(fieldSort)));
複数値フィールドと Nested フィールドのソート
配列やその他の複数値フィールドをソートする場合、mode を使用してソートに参加する値を指定します。次の例では、scores 配列の最大値で降順に行をソートします。
FieldSort fieldSort = new FieldSort("scores", SortOrder.DESC);
fieldSort.setMode(SortMode.MAX);
SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(new MatchAllQuery());
searchQuery.setSort(new Sort(
Collections.<Sort.Sorter>singletonList(fieldSort)));
Nested サブフィールドをソートする場合、Nested パスも構成し、ソートに参加する子行を選択します。次の例では、items.age が 1 の子行のみを使用し、items.name の最小値で昇順に行をソートします。
TermQuery ageQuery = new TermQuery();
ageQuery.setFieldName("items.age");
ageQuery.setTerm(ColumnValue.fromLong(1));
FieldSort fieldSort = new FieldSort("items.name", SortOrder.ASC);
fieldSort.setMode(SortMode.MIN);
fieldSort.setNestedFilter(new NestedFilter("items", ageQuery));
SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(new MatchAllQuery());
searchQuery.setSort(new Sort(
Collections.<Sort.Sorter>singletonList(fieldSort)));
地理的距離によるソート
次の例では、location フィールドと 30.23,120.19 の間の球面距離に基づいて、最も近いものから最も遠いものへと行を返します。
GeoDistanceSort geoSort = new GeoDistanceSort(
"location", Collections.singletonList("30.23,120.19"));
geoSort.setOrder(SortOrder.ASC);
geoSort.setDistanceType(GeoDistanceType.ARC);
SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(new MatchAllQuery());
searchQuery.setSort(new Sort(
Collections.<Sort.Sorter>singletonList(geoSort)));
limit と offset を使用したページネーション
次の例では、最初の 100 行をスキップし、次の 100 行を返します。このメソッドを使用する場合、limit と offset の合計は 100,000 を超えることはできません。
SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(new MatchAllQuery());
searchQuery.setLimit(100);
searchQuery.setOffset(100);
トークンを使用したページネーション
次の例では、ループですべての結果を読み取ります。最初のクエリではトークンは null です。後続の各クエリは、前の応答の nextToken を直接使用します。setToken が呼び出されると、トークンには前のページのソート条件が含まれているため、SDK はソートをクリアします。
List<Row> rows = new ArrayList<Row>();
byte[] nextToken = null;
do {
SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(new MatchAllQuery());
searchQuery.setLimit(100);
searchQuery.setToken(nextToken);
SearchRequest request =
new SearchRequest("example_table", "example_index", searchQuery);
SearchResponse response = client.search(request);
rows.addAll(response.getRows());
nextToken = response.getNextToken();
} while (nextToken != null);
トークンベースのページネーションでは、オフセットを構成できず、ページをスキップすることもできません。前のページに戻るには、各ページのリクエストに使用したトークンをキャッシュし、対象ページのトークンを使用して再度クエリを発行します。
Nested フィールドを含む多次元インデックスには、インデックスソートがありません。このタイプのインデックスでトークンベースのページネーションを使用するには、最初のクエリで明示的にソートを構成する必要があります。そうしないと、サーバーは nextToken を返しません。
同一プロセス内でのシーケンシャルなクエリでは、nextToken をバイト配列として直接渡します。Base64 エンコーディングは、トークンを永続化したり、プロセス間やフロントエンドとバックエンド間で転送したりする必要がある場合にのみ使用してください。new String(nextToken) を使用してトークンを変換しないでください。トークンが破損する原因となります。
String encodedToken = Base64.getEncoder().encodeToString(nextToken);
byte[] decodedToken = Base64.getDecoder().decode(encodedToken);