背景

在做elasticsearch集群从原来的2.x版本升级到更新版本如6.x过程中,由于需要在原来的应用中,同时连接2.x的集群以及6.x的集群来做在线动态灰度切流量,保证流量平滑切换,有问题可随时回切;一般在应用侧比较常规的做法是使用elasticsearch提供rest的sdk:Java High Level REST Client,可以最大程度的保证多版本的兼容性;但在切换过程中也遇到了比较一些兼容性的问题,今天主要来讨论一下关于聚合结果处理的问题;

直方图统计2.x集群返回结果无法解决的问题

在应用中有一个根据价格区间的直方图统计服务,统计查询语句如下:

//POST /my-index/my-type/_search
{
"from": 0,
"size": 0,
"timeout": "10000ms",
"aggregations": {
"statPrice": {
"histogram": {
"field": "price",
"interval": 10000,
"min_doc_count": 1
}
}
}
}

正常的出参数据如下:

{
"_shards":{
"total":16,
"failed":0,
"successful":16,
"skipped":0
},
"hits":{
"hits":[ ],
"total":243399987,
"max_score":0
},
"took":539,
"timed_out":false,
"aggregations":{
"statPrice":{
"buckets":[
{
"doc_count":123,
"key":0.0
},
{
"doc_count":345,
"key":1000.0
},
{
"doc_count":780,
"key":2000.0
}
]
}
}
}

在应用中通过rest api调用,6.x集群可以正常的获取数据,没有任务问题,这里因为我们使用的elasticsearch-rest-high-level-client的版本就是6.x的,这个跟es服务的版本是一样;但是在2.x的老集群上调用时,返回异常; 提示Could not parse aggregation keyed as [statPrice],详细异常如下

Caused by: org.elasticsearch.common.ParsingException: Could not parse aggregation keyed as [statPrice]
at org.elasticsearch.search.aggregations.Aggregations.fromXContent(Aggregations.java:146) ~[elasticsearch-6.3.2.jar:6.3.2]
at org.elasticsearch.action.search.SearchResponse.innerFromXContent(SearchResponse.java:289) ~[elasticsearch-6.3.2.jar:6.3.2]
at org.elasticsearch.action.search.SearchResponse.fromXContent(SearchResponse.java:248) ~[elasticsearch-6.3.2.jar:6.3.2]
at org.elasticsearch.client.RestHighLevelClient.parseEntity(RestHighLevelClient.java:653) ~[elasticsearch-rest-high-level-client-6.3.2.jar:6.3.2]
at org.elasticsearch.client.RestHighLevelClient.lambda$performRequestAndParseEntity$2(RestHighLevelClient.java:508) ~[elasticsearch-rest-high-level-client-6.3.2.jar:6.3.2]
at org.elasticsearch.client.RestHighLevelClient.performRequest(RestHighLevelClient.java:539) ~[elasticsearch-rest-high-level-client-6.3.2.jar:6.3.2]
at org.elasticsearch.client.RestHighLevelClient.performRequestAndParseEntity(RestHighLevelClient.java:508) ~[elasticsearch-rest-high-level-client-6.3.2.jar:6.3.2]
at org.elasticsearch.client.RestHighLevelClient.search(RestHighLevelClient.java:404) ~[elasticsearch-rest-high-level-client-6.3.2.jar:6.3.2]
at com.jd.b2b.ware.admin.compare.core.support.IncrementSyncHandler.statCount(IncrementSyncHandler.java:133) ~[b2b-ware-admin-compare-0.0.1-SNAPSHOT.jar:?]
... 48 more

类:org.elasticsearch.search.aggregations.Aggregations.fromXContent的部分代码

 public static Aggregations fromXContent(XContentParser parser) throws IOException {
final List<Aggregation> aggregations = new ArrayList<>();
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.START_OBJECT) {
SetOnce<Aggregation> typedAgg = new SetOnce<>();
String currentField = parser.currentName();
// TYPED_KEYS_DELIMITER为#, 使用 typed_keys 参数在聚合名称前面加上聚合名称类型前缀时使用的分隔符
parseTypedKeysObject(parser, Aggregation.TYPED_KEYS_DELIMITER, Aggregation.class, typedAgg::set);
if (typedAgg.get() != null) {
aggregations.add(typedAgg.get());
} else {
throw new ParsingException(parser.getTokenLocation(),
String.format(Locale.ROOT, "Could not parse aggregation keyed as [%s]", currentField));
}
}
}
return new Aggregations(aggregations);
}

通过对报错处的代码debug以及分析,这里会对结果的聚合名称根据#号,取到对应的聚合类型的名称;先是翻看了官方文档,了解了一下typed_keys Aggregations 参数的说明;该参数就是指定返回聚合类型;官方文档最早出现是在7.x的版本,但我们在6.x的集群上同样支持该参数;并且增加该参数与比2.x,出参结果除了在集合名称中有聚合类型外,其他都是一样的;

POST /my-index/my-type/_search?typed_keys  (或则typed_keys=true 都可以)

这里又仔细回去检查了我们自己的代码,并没有设置typed_keys参数;本着刨根问底的精神,进行了详细debug,在org.elasticsearch.client.Request#search找到了该参数的设置;这个参数是固定给入的,并且没有参数可以进行覆盖修改;

static Request search(SearchRequest searchRequest) throws IOException {
String endpoint = endpoint(searchRequest.indices(), searchRequest.types(), "_search");
Params params = Request.Params.builder();
params.putParam("typed_keys", "true");
params.withRouting(searchRequest.routing());
........

到此问题原因基本以找到,解决方案就比较如搞;

方案一:使用LowLevelClient

使用LowLevelClient应该是解决这类兼容性问题最通用的办法,因为这个LowLevelClient就是个httpClient,通过给服务端送查询DSL,再获取到出参字符串,基本简单的json字符串解析即可;代码片段如下:

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(0).size(0).timeout(TimeValue.timeValueMillis(10000L)); //数据聚合
searchSourceBuilder.query(queryBuilder);
AggregationBuilder aggregationBuilder = AggregationBuilders
.histogram("statPrice")
.field("price")
.interval(1000)
.minDocCount(1);
searchSourceBuilder.aggregation(aggregationBuilder);
log.info("入参:{}", searchSourceBuilder.toString());
//从highLevelClient中获取到LowLevelClient
RestClient client = highLevelClient.getLowLevelClient();
Map<String, String> params = new HashMap<>();
HttpEntity entity = new NStringEntity(searchSourceBuilder.toString(), ContentType.APPLICATION_JSON);
Response response = client.performRequest("POST","/my-index/my-type/_search",
params, entity);
String responseBody = EntityUtils.toString(response.getEntity());
//后续的业务处理

方案二:聚合名称前缀聚合类型

针对这个场景,还有一个比较简便的办法;在访问2.x集群时,查询的DSL语句的聚合名字前加上他们需要的前缀即可;查询入参的原来聚合名称是statPrice,现在改成histogram#statPrice即可;

// 2.x集群
//POST /my-index/my-type/_search
{
"from": 0,
"size": 0,
"timeout": "10000ms",
"aggregations": {
"histogram#statPrice": {
"histogram": {
"field": "price",
"interval": 10000,
"min_doc_count": 1
}
}
}
}

后续的处理就跟6.x集群一样的,也是通过statPrice去取聚合的结果;具体还有哪么前缀可以参考 org.elasticsearch.client.RestHighLevelClient#getDefaultNamedXContents这里的注册信息,可以在6.x等更高的版本中加入typed_keys=true参数,执行一下查询,查看返回的前缘是什么,即可;

参考

【解决】elasticsearch:Could not parse aggregation keyed as [%s]问题的更多相关文章

  1. 解决 Elasticsearch 超过 10000 条无法查询的问题

    解决 Elasticsearch 超过 10000 条无法查询的问题 问题描述 分页查询场景,当查询记录数超过 10000 条时,会报错. 使用 Kibana 的 Dev Tools 工具查询 从第 ...

  2. 解决Elasticsearch问题的一些心得体会

    在开始前先来介绍下背景:我的日志采集系统采用ELK(logstash(收集).elasticsearch(存储+搜索).kibana(展示)三个软件的简称)开源架构,在elasticsearch搭建了 ...

  3. 解决离线Could not parse configuration:hibernate.cfg.xml错误

    离线使用hibernate tool 生成反向工程,在配置 配置文件完,生成配置文件后,会报出org.hibernate.HibernateException: Could not parse con ...

  4. 解决Elasticsearch索引只读

    今天添加索引时发现kibana添加索引不生效,页面也没有报错,没有创建成功只是一闪而过. 另外发现各项目日志与当前时间差异很大,filebeat一直报错io timeout 具体报错如下: fileb ...

  5. Elasticsearch:significant terms aggregation

    在本文中,我们将重点关注significant terms和significant text聚合.这些聚合旨在搜索数据集中有趣和/或不寻常的术语,这些术语可以告诉您有关数据的隐藏属性的更多信息.此功能 ...

  6. elasticsearch in docker/ and aggregation,,performance tune ;throughout

    Docker环境中Elasticsearch的安装 ]https://wenchao.ren/archives/category/elasticsearch/page/2 [ElasticSearch ...

  7. Java代码解决ElasticSearch的Result window is too large问题

    调用ElasticSearch做分页查询时报错: QueryPhaseExecutionException[Result window is too large, from + size must b ...

  8. elasticsearch 深入 —— Top Hits Aggregation

    Top Hits Aggregation top_hits指标聚合器跟踪正在聚合的最相关文档. 此聚合器旨在用作子聚合器,以便可以按桶聚合最匹配的文档. top_hits聚合器可以有效地用于通过桶聚合 ...

  9. [待解决]报错:JSON parse error: Unexpected character

    {"code":"9999","message":"JSON parse error: Unexpected character ...

  10. 速看,ElasticSearch如何处理空值

    大家好,我是咔咔 不期速成,日拱一卒 在MySQL中,十分不建议大家给表的默认值设置为Null,这个后期咔咔也会单独出一期文章来说明这个事情. 但你进入一家新公司之前的业务中存在大量的字段默认值为Nu ...

随机推荐

  1. 国产开源流批统一的数据同步工具Chunjun入门实战

    @ 目录 概述 定义 特性 部署 安装 版本对应关系 通用配置详解 整体配置 Content 配置 Setting 配置 Local提交 Standalone提交 Json方式使用 SQL方式使用 M ...

  2. pytest7.4版本的一个变更,可能会影响你的项目

    pytest7.4版本的一个变更,可能会影响你的项目 本文撰写于 2023.7.10 准备工作 项目结构如下 D:\Gitee\DemoRepo (17.97MB) +-- testCases (1. ...

  3. 【环境搭建】多版本的jdk共存

    问题来源 burpsuite 2021需要使用Java 9及以上版本,而其他工具需要Java 8,因此需要两个版本共存,并且做到除了burpsuite以外的工具默认使用Java 8打开. 需要的软件 ...

  4. 快速切换 nodejs 的版本

    最近在开发一个常驻进程.定时任务统一调度系统,以应对开发在进程管理方面遇到的各种复杂问题. 组里开发项目,一般来说是一个人承包整个项目,包括调度器设计,还有后台系统.我还有一部分工作,是队列相关的信息 ...

  5. Linux 问题:网络相关

    防火墙 同网段双网卡 双网关 看服务日志

  6. 文件上传的multipart/form-data属性,你理解了吗

    form表单经常用于前端发送请求,比如:用户填写信息.选择数据.上传文件,对于不同的场景,上传数据的格式也会有些区别. action action 表示该请求的 url 地址,定义在form上,请求的 ...

  7. 1. 通俗易懂的Redis基础

    通俗易懂的Redis基础教程(基于CentOS 7) 目录 通俗易懂的Redis基础教程(基于CentOS 7) 1 Redis是什么 1.1 NoSQL概念 1.2 NoSQL与SQL比较 1.3 ...

  8. LeetCode 周赛上分之旅 # 37 多源 BFS 与连通性问题

    ️ 本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 和 BaguTree Pro 知识星球提问. 学习数据结构与算法的关键在于掌握问题背后的算法思维框架,你的思考越 ...

  9. dimp V8:[WARNING]login fail, check your username and password, and check the server status

    在进行某个项目的性能测试时,我们选择了达梦8作为使用的数据库.因前期的网络安全问题和考虑到节省成本,我们首先在公司本地服务器上搭建了相应的环境,并生成了用于压力测试的业务数据. 然而,在将数据库迁移到 ...

  10. Vu3+Element-Plus根据路由配置生成菜单导航栏

    先看效果,整体界面结构如下 点击左侧菜单栏,右侧切换显示不同页面内容. Vue3使用路由–南河小站 1 路由配置 路由配置如下: const routes = [ { path: "&quo ...