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
,结果如下:
7.5.1
public final class SearchHits {
private final SearchHit[] hits;
private final TotalHits totalHits; //看这里
private final float maxScore;
}
6.7.1
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
,问题解决。
rest_total_hits_as_int
来让totalHits
字段,仍然以int
格式返回。结束语
如果胖友发现文章有不对或者不妥之处,还望在评论中不吝指出^ v ^。