排序和翻页

更新时间:
复制 MD 格式

使用 Tablestore Java SDK 查询多元索引时,可通过索引预排序或查询时排序控制结果顺序,并通过 offset 或 token 分页读取结果。

前提条件

安装Tablestore Java SDK并初始化客户端。

功能说明

多元索引支持以下排序方式:

  • 索引预排序:创建多元索引时,通过 IndexSchema.indexSort 设置默认返回顺序。未设置时,默认按照主键排序。索引预排序仅支持 PrimaryKeySortFieldSort,包含 Nested 字段的多元索引不支持索引预排序。

  • 查询时排序:通过 SearchQuery.sort 为单次查询设置返回顺序,支持按相关性得分、主键、字段值或地理距离排序,也支持组合多个排序器实现多级排序。除主键外,排序字段必须在创建多元索引时将 enableSortAndAgg 设置为 true

查询时指定非主键排序器后,服务端默认追加主键排序器,使排序值相同的数据保持确定的返回顺序。设置 Sort.disableDefaultPkSortertrue 可禁用该行为。

返回结果较多时,可使用以下方式分页:

方式

适用场景

特点

limit 和 offset

结果不超过 100000 行,需要跳转到指定位置。

可跳页,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 翻页时无需设置,调用 setToken 后 SDK 会清除已有的 sort 配置。

offset(可选)

Integer

本次查询的起始位置。默认值为 0。使用 token 翻页时不能设置。

limit(可选)

Integer

本次查询返回的最大行数。默认值为 10。仅从多元索引读取返回列时最大值为 1000;任一返回列需要从数据表读取时最大值为 100

token(可选)

byte[]

翻页凭证。将上一次响应的 nextToken 设置为该参数可继续读取后续数据。

trackTotalCount(可选)

int

期望统计的最大匹配行数。默认值为 TRACK_TOTAL_COUNT_DISABLED,表示不统计;设置为 TRACK_TOTAL_COUNT 时统计全部匹配行。值越小,查询性能越高。

排序配置

request.searchQuery.sort 的类型为 Sort,包含以下参数。

名称

类型

说明

sorters(必选)

List<Sort.Sorter>

排序器列表。列表顺序决定多级排序的优先级。支持 ScoreSortPrimaryKeySortFieldSortGeoDistanceSort

disableDefaultPkSorter(可选)

Boolean

是否禁止自动追加主键排序器。默认值为 false

相关性排序

ScoreSort 按照 BM25 算法计算的相关性得分排序,包含以下参数。

名称

类型

说明

order(可选)

SortOrder

排序方向。ASC 表示升序,DESC 表示降序。默认值为 DESC

如需按照相关性得分排序,必须显式设置 ScoreSort,否则按照索引预排序返回。

主键排序

PrimaryKeySort 按照主键排序,包含以下参数。

名称

类型

说明

order(可选)

SortOrder

排序方向。默认值为 ASC

字段排序

FieldSort 按照字段值排序,包含以下参数。

名称

类型

说明

fieldName(必选)

String

排序字段名称。字段必须启用排序与统计聚合。

order(可选)

SortOrder

排序方向。默认值为 ASC

mode(可选)

SortMode

多值字段参与排序的取值方式。MINMAXAVG 分别表示使用最小值、最大值和平均值。

missingFields(可选)

List<String>

备用排序字段列表。当前排序字段缺失时,按列表顺序使用首个存在的字段值参与排序。备用字段必须与排序字段类型相同。

missingValue(可选)

ColumnValue

排序字段及备用字段均缺失时使用的排序值。设置为 FIRST_WHEN_MISSINGLAST_WHEN_MISSING 可将缺失行固定在最前或最后,也可设置与字段类型相同的自定义值。未设置时缺失行排在最后。

nestedFilter(可选)

NestedFilter

Nested 子字段排序配置,用于指定 Nested 路径和参与排序的子行。仅对 Nested 子字段排序时设置。

Nested 过滤

FieldSort.nestedFilter 的类型为 NestedFilter,包含以下参数。

名称

类型

说明

path(必选)

String

Nested 字段路径。

query(必选)

Query

用于筛选参与排序的 Nested 子行的查询条件。设置为 MatchAllQuery 时使用全部子行。

地理距离排序

GeoDistanceSort 按照地理点与目标点之间的距离排序,包含以下参数。

名称

类型

说明

fieldName(必选)

String

Geopoint 排序字段名称。

points(必选)

List<String>

目标地理点列表。地理点使用 纬度,经度 格式。

order(可选)

SortOrder

排序方向。ASC 表示距离由近到远,DESC 表示由远到近。

mode(可选)

SortMode

存在多个距离时参与排序的取值方式。支持 MINMAXAVG

distanceType(可选)

GeoDistanceType

距离计算方式。ARC 按球面计算,精度较高;PLANE 按平面计算,计算量较小。默认值为 ARC

nestedFilter(可选)

NestedFilter

Nested 子字段排序配置。仅对 Nested 子字段排序时设置。

返回列

request.columnsToGet 的类型为 SearchRequest.ColumnsToGet。返回列是否需要从数据表读取会影响 limit 上限。

名称

类型

说明

columns(可选)

List<String>

要返回的属性列名称。仅返回已建立索引且开启 store 的属性列时,数据可直接从多元索引读取。

returnAll(可选)

boolean

是否返回数据表中的全部属性列。默认值为 false。设置为 true 时需要从数据表读取属性列,limit 最大值为 100。

returnAllFromIndex(可选)

boolean

是否返回多元索引中已存储的全部属性列。默认值为 false。设置为 true 时 limit 最大值为 1000。不能与 returnAll 同时设置为 true

返回值

search 方法返回 SearchResponse。与排序和翻页相关的核心字段如下。

名称

类型

说明

rows

List<Row>

本次查询返回的行数据,通过 getRows() 获取,数量不超过 limit。

searchHits

List<SearchHit>

查询命中结果,通过 getSearchHits() 获取。

totalCount

long

匹配行数,通过 getTotalCount() 获取。返回值取决于 trackTotalCount 配置,不是本页返回行数。

nextToken

byte[]

下一页凭证,通过 getNextToken() 获取。值为 null 表示没有更多数据,或当前查询没有确定的排序方式。

isAllSuccess

boolean

是否已成功查询全部索引分区,通过 isAllSuccess() 获取。值为 false 时返回的是部分结果。

场景示例

设置索引预排序

创建多元索引时,以下示例将 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);