本文共 3567 字,大约阅读时间需要 11 分钟。
ES分页查询的代码如下:
SearchResponse searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);long totalNum = searchResponse.getHits().getTotalHits(); //返回的是long型的SearchHit[] searchHits = searchResponse.getHits().getHits();
随着ES server 集群升级以后(从5.5.1->7.5.1),自动化脚步跑失败了,因为ES集群升级后totalNum的值为0了,哪怕searchHits 明明有数据
既然使用High Level Client的Java api 查询有问题,那么直接通过http rest接口从ES 集群查询,结果如下,发现是有total是有数据而且大于0的
细细一看,发现Java客户端中getHits().getTotalHits()的返回类型是long型的,但是上面的截图中,还有relation这样的额外字段。searchResponse.getHits()的返回类型为SearchHits。于是确认本地ES 客户端版本为6.7.1后,对比这两个版本的SearchHits,结果如下:public final class SearchHits { private final SearchHit[] hits; private final TotalHits totalHits; //看这里 private final float maxScore;}
public final class SearchHits { private SearchHit[] hits; public long totalHits; //看这里 private float maxScore;}
果然两个版本的SearchHits 类的totalHits 字段类型不一样了,那么6.7.1的客户端在Json转对象的时候,当然不能拿正常赋值,所以totalHits 就是默认的0值
既然问题的原因找到了,那么怎么解决呢,我的第一想法就是客户端也升级为何集群一样的版本。正准备去尝试的时候,发现了一个问题。请各位胖友仔细观察前面Http rest 直接查询的结果和SearchHits对象的字段名字,把它们对比一下.有如下现象:
total -> totalHits
max_score -> maxScore基于我们对Json 反序列,和反射的理解,Json字符串的key应该和对象字段名字一一对应才对,那么到底是哪儿在帮着转化处理了呢?
通过搜索在7.5.1的SearchHits这个类的toXContent方法中我找到了答案。图中提示的地方做了字段名字的转换。 各位胖友在仔细看看toXContent方法中的这段代码:boolean totalHitAsInt = params.paramAsBoolean(RestSearchAction.TOTAL_HITS_AS_INT_PARAM, false); if (totalHitAsInt) { //按照老的方式处理,直接是long型的totalHits long total = totalHits == null ? -1 : totalHits.value; builder.field(Fields.TOTAL, total); } else if (totalHits != null) { builder.startObject(Fields.TOTAL); builder.field("value", totalHits.value); builder.field("relation", totalHits.relation == Relation.EQUAL_TO ? "eq" : "gte"); builder.endObject(); }
这段代码也比较明了,基于RestSearchAction.TOTAL_HITS_AS_INT_PARAM(rest_total_hits_as_int)这个参数来控制,当值为true时totalHits以int类型返回。
在Http rest接口我加这个参数试了试,还真可以解决问题,结果如下: 当我以为不用升级客户端版本了,只需要在High Level Client的api调用中加入上面的那个参数,就能解决问题的时候,“屁颠屁颠”的在High Level Client的api中找添加Http参数调用的接口,好一阵才发现似乎并没有接口来添加Http参数。看来此路不通,继续想办法。一路进入highLevelClient.search的源码,我发现ES其实是处理了Http参数的添加的,如下:
//org.elasticsearch.client.RequestConverters#addSearchRequestParams private static void addSearchRequestParams(RequestConverters.Params params, SearchRequest searchRequest) { params.putParam("typed_keys", "true"); params.withRouting(searchRequest.routing()); params.withPreference(searchRequest.preference()); params.withIndicesOptions(searchRequest.indicesOptions()); params.putParam("search_type", searchRequest.searchType().name().toLowerCase(Locale.ROOT)); if (searchRequest.requestCache() != null) { params.putParam("request_cache", Boolean.toString(searchRequest.requestCache())); } if (searchRequest.allowPartialSearchResults() != null) { params.putParam("allow_partial_search_results", Boolean.toString(searchRequest.allowPartialSearchResults())); } .......等等
因为ES客户端在请求的时候,会把SearchRequest转化为Request,而这个Request中是可以放参数的。
但是它只是自己加了一些参数,而且似乎并没有给我们预留接口呀。最终一番查找资料之后,在ES的github Pull Request中找到了说明。详情点击这里
截图如下: 意思就是在6.8版本后,把rest_total_hits_as_int参数加入请求中。怎么加的呢,如下: 到了这里,整个过程已经很明了了,那么解决办法也呼之欲出,我本地把客户端版本升到6.8.4,问题解决。总结
高版本的ES集群,可以使用参数rest_total_hits_as_int来让totalHits字段,仍然以int格式返回。 ES更新很快,一定要注意版本问题带来的坑,最好让集群和客户端使用官方推荐的匹配版本。 结束语 如果胖友发现文章有不对或者不妥之处,还望在评论中不吝指出^ v ^。 ———————————————— 版权声明:本文为CSDN博主「two_penguin」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/daofengsuoxiang/article/details/104776085/