使用 Tablestore Java SDK 查詢多元索引時,可通過索引預排序或查詢時排序控制結果順序,並通過 offset 或 token 分頁讀取結果。
前提條件
安裝Tablestore Java SDK並初始化用戶端。
功能說明
多元索引支援以下排序方式:
索引預排序:建立多元索引時,通過
IndexSchema.indexSort設定預設返回順序。未設定時,預設按照主鍵排序。索引預排序僅支援PrimaryKeySort和FieldSort,包含 Nested 欄位的多元索引不支援索引預排序。查詢時排序:通過
SearchQuery.sort為單次查詢設定返回順序,支援按相關性得分、主鍵、欄位值或地理距離排序,也支援組合多個排序器實現多級排序。除主鍵外,排序欄位必須在建立多元索引時將enableSortAndAgg設定為true。
查詢時指定非主鍵排序器後,服務端預設追加主鍵排序器,使排序值相同的資料保持確定的返回順序。設定 Sort.disableDefaultPkSorter 為 true 可禁用該行為。
返回結果較多時,可使用以下方式分頁:
|
方式 |
適用情境 |
特點 |
|
limit 和 offset |
結果不超過 100000 行,需要跳轉到指定位置。 |
可跳頁, |
|
token |
深度分頁或順序讀取全部結果。 |
翻頁深度不受 100000 行限制,但只能按順序讀取。 |
調用 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 |
查詢時排序配置。未設定時按照索引預排序返回。使用 token 翻頁時無需設定,調用 |
|
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(可選) |
|
要返回的屬性列名稱。僅返回已建立索引且開啟 store 的屬性列時,資料可直接從多元索引讀取。 |
|
returnAll(可選) |
boolean |
是否返回資料表中的全部屬性列。預設值為 |
|
returnAllFromIndex(可選) |
boolean |
是否返回多元索引中已儲存的全部屬性列。預設值為 |
傳回值
search 方法返回 SearchResponse。與排序和翻頁相關的核心欄位如下。
|
名稱 |
類型 |
說明 |
|
rows |
|
本次查詢返回的行資料,通過 |
|
searchHits |
|
查詢命中結果,通過 |
|
totalCount |
long |
匹配行數,通過 |
|
nextToken |
byte[] |
下一頁憑證,通過 |
|
isAllSuccess |
boolean |
是否已成功查詢全部索引分割區,通過 |
情境樣本
設定索引預排序
建立多元索引時,以下樣本將 score 欄位設定為索引預排序欄位。未在查詢中指定 sort 時,結果預設按照 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 不能超過 100000。
SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(new MatchAllQuery());
searchQuery.setLimit(100);
searchQuery.setOffset(100);
使用 token 翻頁
以下樣本迴圈讀取全部結果。首次查詢將 token 設定為 null;後續查詢直接使用上一次響應中的 nextToken。調用 setToken 後,SDK 會清除 sort,因為 token 已包含上一頁的排序條件。
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);
使用 token 翻頁時不能設定 offset,也不能跳頁。需要向前翻頁時,可緩衝各頁請求所用的 token,並使用目標頁對應的 token 重新發起查詢。
包含 Nested 欄位的多元索引沒有索引預排序。使用此類索引進行 token 翻頁時,必須在首次查詢中顯式設定 sort,否則服務端不返回 nextToken。
在同一進程中連續查詢時,直接傳遞 byte[] 類型的 nextToken。僅在需要持久化或跨進程、前後端傳輸時,使用 Base 64 編碼;不能使用 new String(nextToken) 轉換,否則會損壞 token。
String encodedToken = Base64.getEncoder().encodeToString(nextToken);
byte[] decodedToken = Base64.getDecoder().decode(encodedToken);