需求分析,如上图

对基金经理数据进行分页查询,并可根据年化回报率,综合得分进行排序

Seach After 两点比较重要:排序字段 排序字段值

1.排序字段一定要包含唯一值字段,即不重复,可用es自带id,因为排序的原理,类似数据库深度分页,条件包含上一页最后一条数据ID,效率比较高,同理要把这个唯一值作为下一页查询条件

2.如果没有业务相关的排序字段(无上年化回报,综合得分)怎么查询

排序字段默认为es默认_id,且倒序,srollId作为下页查询条件

3.查询包含业务条件时,需要注意排序字段包含业务条件,且排序顺序,先根据业务条件排序,再根据_id排序,同时要传入上一页排序字段的值及srollId

4.首页查询时排序字段值怎么传,如果不传会有问题,可以根据排序是升序,降序传入固定值,

如果是desc,其他页排序字段值肯定比它小,排序字段值传最大,可传101

如果是asc,其他页排序字段值肯定比它大,排序字段值传最小,可传-1

其他需要注意的地方

对比

使用search_after 进行分页 相比from&size的方式要更加高效,而且在不断有新数据入库的时候仅仅使用from和size分页会有重复的情况
相比使用scroll分页,search_after可以进行实时的查询
不过search_after不适合跳跃式的分页

注意事项

1,类似mysql limit offset size,进行分页查询时,取上一页最后一条数据的id;

2,如果是多个字段排序的话,search_after值的顺序要与我们排序条件的顺序一致,此处ID倒序

3,使用search_after时 from设置为0,-1或者直接不加from

代码

    @Override
    public Page<FundManagerDTO> page(ManagerEsReqDto reqDto) {
	if (StringUtils.isBlank(reqDto.getOrderBy())) {
	    reqDto.setOrderBy("returnProportion");
	}
	if (StringUtils.isBlank(reqDto.getSort())) {
	    reqDto.setSort("desc");
	}
	/*首页可不传,不影响结果*/
	if (StringUtils.isBlank(reqDto.getScrollId())) {
	    log.info("manager The paging query failed, Param is empty. req: {}", JSON.toJSONString(reqDto));
	}
	/*回报率可能为0或负值,首页查询赋值,前端可不传*/
	if (1 == reqDto.getPage() && "desc".equalsIgnoreCase(reqDto.getSort())) {
	    reqDto.setOrderByValue(101);
	}
	if (1 == reqDto.getPage() && "asc".equalsIgnoreCase(reqDto.getSort())) {
	    reqDto.setOrderByValue(-1);
	}
	com.zgzt.common.util.Page<FundManagerDTO> page = new com.zgzt.common.util.Page<>();
	BoolQueryBuilder params = buildQueryBuilder();
	SearchRequest searchRequest = buildSearchRequest(EsOperateTables.manager, params, reqDto.getSize(),
			reqDto.getOrderBy(), reqDto.getOrderByValue(), reqDto.getSort(), reqDto.getScrollId());
	try {
	    List<FundManagerDTO> list = search(searchRequest, page);
	    page.setResult(list);
	    page.setPageNo(reqDto.getPage());
	    page.setPageSize(reqDto.getSize());
	    return page;
	} catch (Exception e) {
	    log.error("manager The paging query exception", e);
	    throw ExceptionUtils.throwException(MANAGER_QUERY_ERROR);
	}
    }
  private BoolQueryBuilder buildQueryBuilder() {
	BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
	return boolQueryBuilder;
    }

    private SearchRequest buildSearchRequest(EsOperateTables esOperateTables, QueryBuilder params, int pageSize,
		    String orderBy, double orderByValue, String sort, String scrollId) {
	if (pageSize > 10000) {
	    pageSize = 10000;
	}
	SearchRequest searchRequest = new SearchRequest(esOperateTables.getValue());
	SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
	sourceBuilder.query(params);
	sourceBuilder.from(0);
	sourceBuilder.size(pageSize);
	sourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));
	if (esOperateTables.getSelectCols() != null && esOperateTables.getSelectCols().length > 0) {
	    sourceBuilder.fetchSource(esOperateTables.getSelectCols(), new String[] {});
	}
	FieldSortBuilder fieldOderby;
	if (SortOrder.ASC.toString().equalsIgnoreCase(sort)) {
	    fieldOderby = SortBuilders.fieldSort(orderBy).order(SortOrder.ASC);
	} else {
	    fieldOderby = SortBuilders.fieldSort(orderBy).order(SortOrder.DESC);
	}
	FieldSortBuilder fieldId = SortBuilders.fieldSort("_id").order(SortOrder.DESC);
	sourceBuilder.sort(fieldOderby).sort(fieldId);
	sourceBuilder.searchAfter(new Object[] { orderByValue, scrollId });
	searchRequest.source(sourceBuilder);
	return searchRequest;
    }
@Data
public class ManagerEsReqDto extends BasePageDto {

    @ApiModelProperty("上一页最后数据_ID,必填字段,首页可不存")
    private String scrollId;

    @ApiModelProperty("上一页最后数据_业务排序字段值,必填字段,首页可不传")
    private double orderByValue;
}
@Data
public class BasePageDto {

    @ApiModelProperty("当前页数")
    private Integer page = 1;

    @ApiModelProperty("每页条数")
    private Integer size = 10;

    @ApiModelProperty("排序字段")
    private String orderBy;

    @ApiModelProperty("排序规则(升序:asc/倒序:desc)")
    private String sort;

}

Elasticsearch 深入理解search After 处理深度分页问题_wangxuelei036的博客-CSDN博客_search_after

这篇文章实现思路也可以,DSL语句写法,注意两点

1.排序字段,业务排序字段在前,文档_id在后

2.排序值,业务排序值在前,主键值在后

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐