最近公司使用es查询数据,各种聚合,图表。使用
RestHighLevelClient
java客户端。这里记录一下es的聚合函数使用。
springboot 版本 2.3.0.RELEASE
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
dependency>
客户端的版本 7.6.2
package com.huize.titan.mobile.server.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import java.util.ArrayList;
import java.util.List;
/**
* @Author aiyuan
* @Date 2021/7/6 9:12
**/
@Configuration
@Slf4j
public class ElasticSearchConfig {
/** 集群地址,如果有多个用“,”隔开 */
@Value("${elasticsearch.web.address}")
private String webAddress;
/** 集群地址,如果有多个用“,”隔开 */
@Value("${elasticsearch.app.address}")
private String appAddress;
/** 连接超时时间 */
@Value("${elasticsearch.connectTimeout}")
private int connectTimeout;
/** Socket 连接超时时间 */
@Value("${elasticsearch.socketTimeout}")
private int socketTimeout;
/** 获取连接的超时时间 */
@Value("${elasticsearch.connectionRequestTimeout}")
private int connectionRequestTimeout;
/** 最大连接数 */
@Value("${elasticsearch.maxConnectNum}")
private int maxConnectNum;
/** 最大路由连接数 */
@Value("${elasticsearch.maxConnectPerRoute}")
private int maxConnectPerRoute;
@Primary
@Bean(name = "webRestHighLevelClient")
public RestHighLevelClient webRestHighLevelClient() {
log.info("webRestHighLevelClient注入--:{}",webAddress);
return restHighLevelClient(webAddress);
}
@Bean(name = "appRestHighLevelClient")
public RestHighLevelClient appRestHighLevelClient() {
log.info("appRestHighLevelClient注入--:{}",appAddress);
return restHighLevelClient(appAddress);
}
public RestHighLevelClient restHighLevelClient(String address) {
// 拆分地址
List<HttpHost> hostLists = new ArrayList<>();
String[] hostList = address.split(",");
for (String addr : hostList) {
String host = addr.split(":")[0];
String port = addr.split(":")[1];
hostLists.add(new HttpHost(host, Integer.parseInt(port)));
}
// 转换成 HttpHost 数组
HttpHost[] httpHost = hostLists.toArray(new HttpHost[]{
});
// 构建连接对象
RestClientBuilder builder = RestClient.builder(httpHost);
// 异步连接延时配置
builder.setRequestConfigCallback(requestConfigBuilder -> {
requestConfigBuilder.setConnectTimeout(connectTimeout);
requestConfigBuilder.setSocketTimeout(socketTimeout);
requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeout);
return requestConfigBuilder;
});
// 异步连接数配置
builder.setHttpClientConfigCallback(httpClientBuilder -> {
httpClientBuilder.setMaxConnTotal(maxConnectNum);
httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
return httpClientBuilder;
});
return new RestHighLevelClient(builder);
}
}
因为这里用到了不同的es集群,所以配置了两个客户端。
/**
* @param queryBuilder 查询条件
* @date 2021/1/7 16:44
* @author aiyuan
*/
public List<ApplicationLogBO> commonAppLogRanking(QueryBuilder queryBuilder){
log.info("查询应用日志排行---> start");
List<ApplicationLogBO> resultData = new ArrayList<>();
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//添加查询条件
sourceBuilder.query(queryBuilder);
//添加查询聚合 采用的是terms 聚合函数
AggregationBuilder aggregation = AggregationBuilders
.terms(ESConstant.POD_NAME) //别名 再返回之中取出Bucket名字
.field(ESConstant.NAMESPACE) //es中 需要分组的字段 可以采用 “.keyword” 完全匹配
//在上一次聚合后 再次聚合
.subAggregation(AggregationBuilders
.terms(ESConstant.APP_NAME) //别名 再返回之中取出Bucket名字
.field(ESConstant.APP_AGGS) //es中 需要分组的字段 可以采用 “.keyword” 完全匹配
.size(Integer.MAX_VALUE))
/**
public static BucketOrder count(boolean asc) {
return asc ? InternalOrder.COUNT_ASC : InternalOrder.COUNT_DESC;
}
*/
.order(BucketOrder.count(false))//排序方式
.size(Integer.MAX_VALUE);
sourceBuilder.aggregation(aggregation);//将分组函数加入查询构建起中
SearchRequest request = new SearchRequest();
//索引名称 前缀匹配 可以使用通配符 * 例如:“error-log*”
request.indices(ERROR_LOG);
request.source(sourceBuilder);
SearchResponse search;
try {
search = appRestHighLevelClient.search(request, RequestOptions.DEFAULT);
Terms podTerms = search.getAggregations().get(ESConstant.POD_NAME);//上边AggregationBuilders中 取得别名
for (Terms.Bucket podTerm : podTerms.getBuckets()) {
Terms appTerms = podTerm.getAggregations().get(ESConstant.APP_NAME);//上边AggregationBuilders中 取得别名
for (Terms.Bucket appTerm : appTerms.getBuckets()) {
ApplicationLogBO applicationLogBO = ApplicationLogBO.builder()
.podName(podTerm.getKeyAsString())
.appName(appTerm.getKeyAsString())
.count((int) appTerm.getDocCount())
.build();
resultData.add(applicationLogBO);
}
}
log.info("查询应用日志排行---> end");
return resultData;
} catch (IOException e) {
log.error("查询应用日志排行失败--->{}",e.getMessage(),e);
return resultData;
}
}
但是有一个图表,需要根据时间,分成指定的时间段分组统计次数。我一想不都是根据每分钟、每小时、每天… 这样来的吗?
然后开始百度。。。。。
然后就看到了这个
GET /cars/transactions/_search
{
"size" : 0,
"aggs": {
"sales": {
"date_histogram": {
"field": "sold",
"interval": "month",
"format": "yyyy-MM-dd"
"extended_bounds" : {
"min" : "2014-01-01",
"max" : "2014-12-31"
}
}
}
}
}
一看AggregationBuilder
聚合构建器中看到一叫DateHistogramAggregationBuilder
之后就根据dsl语句,写出如下 聚合构建器
AggregationBuilders.dateHistogram("time") //别名 和之前的一样
.field(ESConstant.TIME_AGGS)
.fixedInterval(DateHistogramInterval.MINUTE)//聚合时间
.format("HH:mm:ss"); //时间格式
猛地一看还是没有指定时间段呀 这可咋整!!!
再一看DateHistogramInterval
是常量 但不是枚举定义的 进去一看问题解决 收工下班!
AggregationBuilders.dateHistogram("time") //别名 和之前的一样
.field(ESConstant.TIME_AGGS)
.fixedInterval(DateHistogramInterval.seconds(30))//聚合时间
.format("HH:mm:ss"); //时间格式
返回值跟之前获取就行
不过从SearchResponse 获取 接受类型是 Histogram
Histogram timeTerms = search.getAggregations().get("time");