springboot + elasticsearch 實(shí)現(xiàn)聚合查詢的詳細(xì)代碼
需求背景:
終端上報(bào)表讀數(shù) 記錄在elasticsearch
統(tǒng)計(jì)每天 最大值最小值
springboot版本:2.2.6
默認(rèn)的elasticsearch
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>3.2.6.RELEASE</version>版本 elasticsearch
{
"name" : "node1",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "Lg0bD-E-Thuaw4cDN5uQrQ",
"version" : {
"number" : "7.4.2",
"build_flavor" : "default",
"build_type" : "tar",
"build_hash" : "2f90bbf7b93631e52bafb59b3b049cb44ec25e96",
"build_date" : "2019-10-28T20:40:44.881551Z",
"build_snapshot" : false,
"lucene_version" : "8.2.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}根據(jù)springdata官網(wǎng)
Spring Data Elasticsearch - Reference Documentation

版本符合
這里為了方便 采用的是 api創(chuàng)建索引
切記這里沒有采用spring-data 去創(chuàng)建索引 而是采用的是 api 接口創(chuàng)建 原因后面再提
@PutMapping("/createIndex")
@ApiOperation(value = "創(chuàng)建索引")
public R<Object> createIndex() {
boolean index = elasticsearchRestTemplate.createIndex(DataUploadInfo.class);
elasticsearchRestTemplate.putMapping(DataUploadInfo.class);
return R.success();
}
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
/**
* {
* "busId": "1000010814",
* "createTime": 1649408879000,
* "deviceNum": "AE0007A1GMBC00047P",
* "gunNo": "1",
* "rdChargeCurrent": 5.617,
* "rdChargingPower": 1220.0,
* "rdChargingVoltage": 225.11,
* "totalElectricalPower": 270305.0
* }
*
*
*
* @Field(type=FieldType.Text, analyzer=“ik_max_word”) 表示該字段是一個(gè)文本,并作最大程度拆分,默認(rèn)建立索引
* @Field(type=FieldType.Text,index=false) 表示該字段是一個(gè)文本,不建立索引
* @Field(type=FieldType.Date) 表示該字段是一個(gè)文本,日期類型,默認(rèn)不建立索引
* @Field(type=FieldType.Long) 表示該字段是一個(gè)長整型,默認(rèn)建立索引
* @Field(type=FieldType.Keyword) 表示該字段內(nèi)容是一個(gè)文本并作為一個(gè)整體不可分,默認(rèn)建立索引
* @Field(type=FieldType.Float) 表示該字段內(nèi)容是一個(gè)浮點(diǎn)類型并作為一個(gè)整體不可分,默認(rèn)建立索引
* <p>
* date 、float、long都是不能夠被拆分的
*/
@Data
@Document(indexName = "charging-monitor-data", indexStoreType = "_doc", useServerConfiguration = true, createIndex = false)
@TypeAlias("_doc")
public class DataUploadInfo {
/**
* 主鍵
*/
@Id
@ApiModelProperty(value = "主鍵", example = "11", hidden = true)
private String id;
@ApiModelProperty(value = "槍號(hào)", example = "1")
@Field(type = FieldType.Keyword )
private String gunNo;
@ApiModelProperty(value = "樁號(hào)", example = "DG1120B1CN1C000125")
@Field(type = FieldType.Keyword)
private String deviceNum;
@ApiModelProperty(value = "流水ID", example = "AU22188888888888")
@Field(type = FieldType.Keyword )
private String busId;
@ApiModelProperty(value = "充電電流(毫安)", example = "21.21")
@Field(type = FieldType.Double, index = false)
private Double rdChargeCurrent;
@ApiModelProperty(value = "充電電壓(毫伏)", example = "212.21")
@Field(type = FieldType.Double, index = false)
private Double rdChargingVoltage;
@ApiModelProperty(value = "充電電能(瓦)", example = "212.21")
@Field(type = FieldType.Double, index = false)
private Double rdChargingPower;
@ApiModelProperty(value = "剩余時(shí)間(分鐘)", example = "21")
@Field(type = FieldType.Integer, index = false)
private Integer rdTimeLeft;
@ApiModelProperty(value = "電量百分比(soc)", example = "29")
@Field(type = FieldType.Integer, index = false)
private Integer rdCurrentSoc;
@ApiModelProperty(value = "電表讀數(shù) 單位kwh 保留三位小數(shù),啟動(dòng)成功時(shí)才有", example = "2.292")
@Field(type = FieldType.Double, index = false)
private Double totalElectricalPower;
@ApiModelProperty(value = "正極溫度", example = "22")
@Field(type = FieldType.Integer, index = false)
private Integer gunPositiveTemperature;
@ApiModelProperty(value = "負(fù)極溫度", example = "83")
@Field(type = FieldType.Integer, index = false)
private Integer gunNegativeTemperature;
@ApiModelProperty(value = "電量上報(bào)時(shí)間", example = "1648646486000")
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis")
private Long createTime;
}這里是創(chuàng)建 映射的Java文件 注意配置 createIndex = false
這里不自動(dòng)創(chuàng)建索引
我們調(diào)用上面的rest 接口創(chuàng)建 索引
查看_mapping 會(huì)發(fā)現(xiàn)
{
-"charging-monitor-data": {
-"mappings": {
-"properties": {
-"busId": {
"type": "keyword"
},
-"createTime": {
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis",
"type": "date"
},
-"deviceNum": {
"type": "keyword"
},
-"gunNegativeTemperature": {
"index": false,
"type": "integer"
},
-"gunNo": {
"type": "keyword"
},
-"gunPositiveTemperature": {
"index": false,
"type": "integer"
},
-"id": {
-"fields": {
-"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"type": "text"
},
-"rdChargeCurrent": {
"index": false,
"type": "double"
},
-"rdChargingPower": {
"index": false,
"type": "double"
},
-"rdChargingVoltage": {
"index": false,
"type": "double"
},
-"rdCurrentSoc": {
"index": false,
"type": "integer"
},
-"rdTimeLeft": {
"index": false,
"type": "integer"
},
-"totalElectricalPower": {
"index": false,
"type": "double"
}
}
}
}
}keyword類型會(huì)增加keyword屬性 而不是直接增加到我定義gunNegativeTemperature的屬性下面
"pojo里面定義的屬性": {
-"fields": {
-"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"type": "text"
}下面按照樁槍做每日電表的最大最小值
上代碼
import com.haoran.cloud.app.monitor.entity.DataUploadInfo;
import com.haoran.cloud.app.monitor.ocpp.OcppResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.max.MaxAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.max.ParsedMax;
import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.min.ParsedMin;
import org.joda.time.DateTime;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author wenhaoran
* @version 1.0
*/
@RestController
@RequestMapping("/test")
@Api(value = "test", tags = "test")
@Log4j2
@Validated
public class PileMonitorController1 {
/**
* @return
*/
@GetMapping("/dailySummaryEnergy")
@ApiOperation(value = "每日電力匯總")
public OcppResult<Object> dailySummaryEnergy() throws ParseException {
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
DateTime now = DateTime.now();
DateTime plus = now.plusDays(1);
String fromDateStr = df.format(now.toDate());
Date fromDate = df.parse(fromDateStr);
String toDateStr = df.format(plus.toDate());
Date toDate = df.parse(toDateStr);
queryBuilder.must(QueryBuilders.rangeQuery("createTime").gte(fromDate.getTime()).lt(toDate.getTime()));
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder().withQuery(queryBuilder);
TermsAggregationBuilder pile = AggregationBuilders.terms("group_deviceNum").field("deviceNum");
TermsAggregationBuilder gun = AggregationBuilders.terms("group_gunNo").field("gunNo");
MinAggregationBuilder minNumber = AggregationBuilders.min("minNumber").field("totalElectricalPower");
MaxAggregationBuilder maxNumber = AggregationBuilders.max("maxNumber").field("totalElectricalPower");
gun.subAggregation(minNumber).subAggregation(maxNumber);
pile.subAggregation(gun);
nativeSearchQueryBuilder.addAggregation(pile);
NativeSearchQuery nativeSearchQuery = nativeSearchQueryBuilder.build();
Aggregations query = elasticsearchRestTemplate.query(nativeSearchQuery, searchResponse -> searchResponse.getAggregations());
Map<String, Aggregation> stringAggregationMap = query.asMap();
ParsedStringTerms stringTerms = (ParsedStringTerms) stringAggregationMap.get("group_deviceNum");
List<? extends Terms.Bucket> buckets = stringTerms.getBuckets();
for (Terms.Bucket bucket : buckets) {
Map<String, Aggregation> pileMap = bucket.getAggregations().asMap();
ParsedStringTerms gunAgg = (ParsedStringTerms) pileMap.get("group_gunNo");
List<? extends Terms.Bucket> gunBucketList = gunAgg.getBuckets();
for (Terms.Bucket gunBucket : gunBucketList) {
Aggregations aggregations = gunBucket.getAggregations();
Map<String, Aggregation> asMap = aggregations.getAsMap();
if (asMap.containsKey("minNumber")) {
ParsedMin aggregation = (ParsedMin) asMap.get("minNumber");
System.out.println("minNumber=" + aggregation.getValue());
}
if (asMap.containsKey("maxNumber")) {
ParsedMax aggregation = (ParsedMax) asMap.get("maxNumber");
System.out.println("maxNumber=" + aggregation.getValue());
}
}
}
return OcppResult.success("success");
}
@DeleteMapping("/deleteIndex")
@ApiOperation(value = "刪除索引")
public OcppResult<Object> deleteIndex() {
elasticsearchRestTemplate.deleteIndex(DataUploadInfo.class);
return OcppResult.success();
}
@PutMapping("/createIndex")
@ApiOperation(value = "創(chuàng)建索引")
public OcppResult<Object> createIndex() {
boolean index = elasticsearchRestTemplate.createIndex(DataUploadInfo.class);
elasticsearchRestTemplate.putMapping(DataUploadInfo.class);
return OcppResult.success();
}
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
@Resource
private ElasticsearchRestTemplate elasticsearchRestTemplate;
}到此這篇關(guān)于springboot + elasticsearch 實(shí)現(xiàn)聚合查詢的文章就介紹到這了,更多相關(guān)springboot elasticsearch聚合查詢內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Flask接口如何返回JSON格式數(shù)據(jù)自動(dòng)解析
這篇文章主要介紹了Flask接口如何返回JSON格式數(shù)據(jù)自動(dòng)解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11
通過java字節(jié)碼分析學(xué)習(xí)對(duì)象初始化順序
今天用了jmock對(duì)進(jìn)行單元測(cè)試編碼,發(fā)現(xiàn)一個(gè)比較奇怪的語法,static使用方法,見下面例子2013-11-11
Java下載遠(yuǎn)程服務(wù)器文件到本地(基于http協(xié)議和ssh2協(xié)議)
這篇文章主要介紹了Java下載遠(yuǎn)程服務(wù)器文件到本地的方法(基于http協(xié)議和ssh2協(xié)議),幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2021-01-01
Java自定義數(shù)組列表的實(shí)現(xiàn)操作
這篇文章主要介紹了Java自定義數(shù)組列表的實(shí)現(xiàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09
springboot后端存儲(chǔ)富文本內(nèi)容的思路與步驟(含圖片內(nèi)容)
在所有的編輯器中,大概最受歡迎的就是富文本編輯器和MarkDown編輯器了,下面這篇文章主要給大家介紹了關(guān)于springboot后端存儲(chǔ)富文本內(nèi)容的思路與步驟的相關(guān)資料,需要的朋友可以參考下2023-04-04
Spring 框架中注入或替換方法實(shí)現(xiàn)
這篇文章主要介紹了Spring 框架中注入或替換方法實(shí)現(xiàn),非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧2018-05-05

