1、深度分页问题
之前已经总结过了,具体的可以参考下之前的博客:ES深度分页
2、ES默认分页问题
准确的说,并不是 ElasticSearch的问题,而是其客户端 JestClient
的问题。
默认情况下,SearchSourceBuilder
不设置分页信息的话,默认查询 10条数据。试想,若符合条件的结果集大于 10的且没有显式设置分页信息,那么必然会出现结果集数据丢失的问题,进而造成 BUG!
如果不明确本次分页的大小(页面分页查询是可以明确的),那么建议大家查询数据前,先进行 count,然后再根据 count值进行分页设置。
searchSourceBuilder.size(pageSize);
searchSourceBuilder.from(dataFrom);
2.1、思考:
是否存在一种机制,可以在开发者没有设置分页信息的情况下,默认为其赋予一个合理的分页信息,尽可能保证符合条件的结果集完整性?
可以看到查询的语句是:
SearchResult result = jestMultiThreadClient.execute(search);
这种需求,很适合使用 AOP
来解决。
具体的逻辑如下:
/**
* @author tly
* 带有分页的语句:{"from":1,"size":10,"query":{"bool":{"must":[{"terms":{"id":[1,2],"boost":1.0}}],"adjust_pure_negative":true,"boost":1.0}},"sort":[{"id":{"order":"desc"}}]}
* 不带有分页的语句:{"query":{"bool":{"must":[{"terms":{"id":[1,2],"boost":1.0}}],"adjust_pure_negative":true,"boost":1.0}},"sort":[{"id":{"order":"desc"}}]}
*/
@Aspect
@Component
@Slf4j
public class JestExecuteAspect {
@Autowired
@Qualifier("jestMultiThreadClient")
private JestClient jestMultiThreadClient;
@Pointcut("execution(* io.searchbox.client.JestClient.execute(..))")
public void jestExecutePoint(){
}
@Around("jestExecutePoint()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
// 只处理方法参数是 Action<T>
if (args.length != 1) {
return joinPoint.proceed();
}
Action action = (Action) args[0];
// 若不是 Search类型,则放行
if (!(action instanceof Search)) {
return joinPoint.proceed();
}
Search search = (Search) action;
try {
// 获取到 Search中的的 query(查询的 es语句)
Field queryField = Search.class.getDeclaredField("query");
queryField.setAccessible(true);
Object obj = queryField.get(search);
// 判断是否已经存在分页信息了
SearchSourceBuilderTemp builder = JsonTools.defaultMapper().fromJson(obj.toString(), SearchSourceBuilderTemp.class);
if (builder.getFrom() >= 0 || builder.getSize() >= 0) {
// 存在分页信息,则放行
return joinPoint.proceed();
}
log.error("JestExecuteAspect检测到没有配置分页信息!");
String str = obj.toString();
// 默认先进行 count统计出数量
Count count = new Count.Builder()
.addIndex(search.getIndex())
.query(str)
.build();
CountResult result = jestMultiThreadClient.execute(count);
Double counts = result.getCount();
// 若没有统计出来,则默认 size为 10。考虑到深度分页的问题,默认最大值为 60000
int size = counts == null ? 10 : Math.min(counts.intValue(), 60000);
// 设置分页信息
str = str.charAt(0) + "\"from\":0,\"size\":" + size + "," + str.substring(1);
queryField.set(search, str);
return joinPoint.proceed();
} catch (Exception e) {
log.info("jestExecutePoint出现异常,走原逻辑. {}", e);
}
return joinPoint.proceed();
}
@Data
public static class SearchSourceBuilderTemp {
private int from = -1;
private int size = -1;
}
}
单元测试结果:
debug结果