springboot + elasticsearch 实现聚合查询

2 篇文章 0 订阅

需求背景:

终端上报表读数 记录在elasticsearch

统计每天 最大值最小值

springboot版本:2.2.6   默认的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"
}

根据springdata官网

Spring Data Elasticsearch - Reference Documentation

 版本符合

这里为了方便 采用的是 api创建索引

切记这里没有采用spring-data 去创建索引 而是采用的是 api 接口创建 原因后面再提

    @PutMapping("/createIndex")
    @ApiOperation(value = "创建索引")
    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”) 表示该字段是一个文本,并作最大程度拆分,默认建立索引
 * @Field(type=FieldType.Text,index=false) 表示该字段是一个文本,不建立索引
 * @Field(type=FieldType.Date) 表示该字段是一个文本,日期类型,默认不建立索引
 * @Field(type=FieldType.Long) 表示该字段是一个长整型,默认建立索引
 * @Field(type=FieldType.Keyword) 表示该字段内容是一个文本并作为一个整体不可分,默认建立索引
 * @Field(type=FieldType.Float) 表示该字段内容是一个浮点类型并作为一个整体不可分,默认建立索引
 * <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 = "枪号", example = "1")
    @Field(type = FieldType.Keyword )
    private String gunNo;

    @ApiModelProperty(value = "桩号", 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 = "剩余时间(分钟)", 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 = "电表读数 单位kwh 保留三位小数,启动成功时才有", 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 = "负极温度", example = "83")
    @Field(type = FieldType.Integer, index = false)
    private Integer gunNegativeTemperature;

    @ApiModelProperty(value = "电量上报时间", example = "1648646486000")
    @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis")
    private Long createTime;

}

这里是创建 映射的Java文件 注意配置   createIndex = false

这里不自动创建索引

我们调用上面的rest 接口创建 索引

查看_mapping 会发现

{
-"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类型会增加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 = "创建索引")
    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;
}

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个开源的Java框架,用于构建独立的、可扩展的、基于微服务的应用程序。它提供了一种快速、简单的方式来构建和部署应用程序,也简化了与各种数据库、消息队列和其他外部系统进行集成的过程。同时,Spring Boot提供了一套强大的开发工具和功能,方便开发人员进行开发、测试和部署。 Elasticsearch是一个基于Lucene的分布式搜索和分析引擎,它被广泛应用于构建实时搜索、数据分析和数据存储的解决方案。Elasticsearch具有高性能、可扩展、可靠和易于使用的特点,可以处理大规模的数据,并提供全文搜索、聚合分析和实时监控等功能。 Oracle是一种关系型数据库管理系统(RDBMS),它是全球领先的企业级数据库解决方案之一。Oracle提供了高度可靠和安全的数据管理功能,同时支持事务处理、并发性控制、数据恢复和备份等重要特性。它还提供了丰富的管理和开发工具,方便开发人员进行数据库的设计、开发和管理。 在使用Spring Boot开发应用程序时,可以通过集成Elasticsearch和Oracle来满足不同的需求。使用Elasticsearch,可以轻松地实现全文搜索、聚合分析和实时监控等功能。而Oracle数据库可以用于存储结构化数据,并提供事务处理、数据完整性和安全性等特性。通过使用这两个技术,可以构建出高效、可靠和安全的应用程序,满足不同场景下的各种需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值