使用 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。仅在需要持久化或跨进程、前后端传输时,使用 Base64 编码;不能使用 new String(nextToken) 转换,否则会损坏 token。
String encodedToken = Base64.getEncoder().encodeToString(nextToken);
byte[] decodedToken = Base64.getDecoder().decode(encodedToken);