使用SpringBoot整合ES搜索,elastic在java中体验-Document APIs(3)

 写文章不容易,请大家看的时候给阿丹点个赞吧!!!!!

 

系列目录

(1)介绍页面以及添加基本用法

使用SpringBoot整合ES搜索,elastic在java中体验-Document APIs(1)_艺舟先生的博客-CSDN博客

 (2)Spring-Boot聚合es的增删改查,以及多条件查询以及高亮

使用SpringBoot整合ES搜索,elastic在java中体验-Document APIs(2)_艺舟先生的博客-CSDN博客

前面总结和整合了简单的boot聚合的增删改查,今天给聚合查询和全量、增量同步整明白。就能使用在web项目里面了。感觉自己!泰酷辣!

 

 

 开始正文

同步需求:

        在项目中更多是从数据库中或者从redis缓存中拿到列表或者商品等等。。。信息。那么定时或者让项目启动的时候就将信息同步到es库中就就能满足了。

同步思路:

        1、代码实现。

                (1)在项目开始的时候就直接将数据从数据源哪里直接同步到es中

                (2)利用定时器来定时从数据源拿取数据同步到es中

        2、软件实现

                软件来自动定时更新数据到es中。比较方便。

同步分两种

        1、全局同步

        阿丹解读:就是给所有的数据全部同步到es中,其实就是全部添加

        2、增量同步

        阿丹解读:在数据源数据太多的情况下。同步的时候要一批一批同步,那么就需要追加来向数据库添加。如果在修改或者增加的情况下就需要对es中的数据进行追加。

代码实现全局同步

代码思路:

        1、因为全局同步其实就是对于es的多个添加,为了保证效率需要构建一个批量的请求对象

        2 、获取数据源中所有的数据

        3、通过循环来将每个添加数据的请求添加到批量请求对象中!

        4、注意在循环中要注意,因为id是要额外添加。不是跟着对象来添加的,所有在添加的时候要构建一个简单的过滤器来将id去掉,并且在构建添加请求的时候从对象中加上id。

上代码!

 在代码中我使用了Java 8 之后的 Lambda 表达式,如果不会的话其实使用for循环也一样。

要注意的是我写的是当项目开始的时候就进行全局的增量,个人觉得更适合逻辑,因为如果定时全局增量的话,如果数据较多对于数据源来说是个不小的压力。

StudentFeighService是我远程调用的其他服务中的接口。获取数据。因为我现在的环境是spring-boot-could有时间我会整理出来一个远程调用的文章。

项目开始时执行关键是实现了一个接口ApplicationRunner

注意在实现这个接口的时候run方法会自带一个抛出最大异常。阿丹建议删掉,因为如果不删掉的的话下面使用高级客户端发送批量请求的时候就无法try/catch了。有点难受。

重点解释一下过滤器的作用以及意义:

SimplePropertyPreFilter filter = new SimplePropertyPreFilter(); filter.getExcludes().add("id");

chatGPT的解释:

这两行代码用于定义一个序列化时的过滤器,用于控制在序列化 Java 对象转换为 JSON 对象时是否包含某些属性。在这里,SimplePropertyPreFilter 对象定义了一个 exclude 集合,其中添加了 "id" 属性,表示在序列化时排除 id 属性。在序列化过程中,将只包含在 exclude 集合之外的属性。通过这个机制,您可以方便地选择要序列化的属性,从而控制 JSON 对象的大小和内容。

SimplePropertyPreFilter 对象的作用是控制在将 Java 对象序列化为 JSON 对象时是否包含某些属性。在这里,它创建了一个 exclude 集合,并将 "id" 属性添加到该集合中,表示在序列化时排除 id 属性。这样,序列化后的 JSON 对象中就不会包含该属性,从而实现对部分属性的选择性序列化。

阿丹理解:

        说白了就是你不想要谁,你就add进去实体类中的属性名!它序列化的时候就不整这个就完事了。

@Component
@Log4j2
public class EsTogetherSql implements ApplicationRunner {
    @Autowired
    StudentFeignService service;
    @Autowired
    RestHighLevelClient client;
    @Override
    public void run(ApplicationArguments args){
        log.info("同步开始");
        BulkRequest student = new BulkRequest();
        List<Student> allstu = service.allstu();
        allstu.stream().forEach(item->{
            SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
            filter.getExcludes().add("studentId");
            student.add(
                    new IndexRequest("student")
                            .id(item.getStudentId()+"")
                            .source(JSON.toJSONString(item,filter), XContentType.JSON)
            );
        });
        try {
            BulkResponse responses = client.bulk(student, RequestOptions.DEFAULT);

        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("同步完成");
    }
}

给大家实现一下使用定时器来全局同步的内容

从开始到定时器其实就一点点区别从实现一个接口(ApplicationRunner),改成一个定时器的注解就行了。

上代码!

@Component
@Log4j2
public class EsTogetherSql{
    @Autowired
    StudentFeignService service;
    @Autowired
    RestHighLevelClient client;
    //这个是每分钟执行一次的七子表达式
    @Scheduled(cron = "* * * * * *")
    public void run(ApplicationArguments args){
        log.info("同步开始");
        BulkRequest student = new BulkRequest();
        List<Student> allstu = service.allstu();
        allstu.stream().forEach(item->{
            SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
            filter.getExcludes().add("studentId");
            student.add(
                    new IndexRequest("student")
                            .id(item.getStudentId()+"")
                            .source(JSON.toJSONString(item,filter), XContentType.JSON)
            );
        });
        try {
            BulkResponse responses = client.bulk(student, RequestOptions.DEFAULT);

        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("同步完成");
    }
}

 总体注意:

        在构建对IndexRequest对象的时候,在给数据属性的时候一定不要忘记。不仅要给每一个对象使用JSON的toJSONString方法时第一个数据为你要转换的对象,第二个参数是你构造好的过滤器。

代码实现增量同步

实现思路:

        阿丹解读:一个偷鸡一点的思路是写一个在es中添加的方法,在向数据库中添加的时候直接将相同的数据在es中进行添加。增删改查都是这个逻辑我就不一个一个展示了。有需要的看一下我在前面的文章中有说到es中的增删改查。

使用logstash-7.17.0来同步Mysql数据库数据到es中

我给大家放一个连接需要的可以自行下载!下载前你不给我点个赞?给我个关注?

百度网盘 请输入提取码

不用输入提取码可以直接下载!

 配置logstash

1、首先你先进入logstash的文件夹

2、进入bin文件夹

3、修改配置文件

 

 4、修改属于自己的信息

(1)配置数据库信息自己的ip地址以及表名称

        

 (2)修改属于自己的maven仓库

        

(3) 修改自己的mysql执行语句

:sql_last_value":是最后修改的值 (也就是增量同步)

注意要把时间改成用时间戳函数包裹

UNIX_TIMESTAMP  转换成 时间戳

 

 

 (4)字段映射命名要对应

前面是数据库中的字段名字,后面是实体类中的属性名

(5)指定要上传的es数据库
注意document_id是上面写的数据库的id,一定要对应!!!!

 全部修改完毕之后!启动启动启动!!!

启动logstash

1、还是在bin文件夹下面打开doc的小窗口

两个方法:

        一、 删除框框中的东西直接输入cmd,然后回车!就能在当前路径下面打开小黑了!

 

 

 

         二、使用cmder,直接打开

 

 2、使用logstash.bat启动

输入之后回车启动!

 

 

 ok!大功告成!

 

 es中的条件聚合查询

ChatGPT解释:

Elasticsearch 中的条件聚合查询是一种高级聚合查询方式,它允许您通过一个或多个查询条件来筛选文档,并对查询到的文档执行聚合操作。比如,您可以使用条件聚合查询来计算在某段时间内访问网站的独立访客数量,或者统计在特定地区购物的人数。在 Elasticsearch 中,条件聚合查询通常使用以下两个聚合操作:filter 聚合和 bool 聚合。

1、filter 聚合:filter 聚合允许您将一个或多个查询条件应用到文档集合之上,并过滤出符合条件的文档。在 filter 聚合中,您可以使用各种查询类型,包括 term 查询、range 查询、match 查询、bool 查询等

2、bool 聚合:bool 聚合和 filter 聚合类似,也允许您组合多个查询条件。不同的是,bool 聚合支持多种逻辑操作符,包括 must、must_not、should 和 filter 等,可以更灵活地控制查询条件的组合方式

阿丹解读:

        其实聚合查询可以类比mysql数据库中的聚合条件查询。比如计数呀什么的。

 spring-boot中整合的聚合函数

阿丹小贴士:

BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder(); 是等效的,都是构建相同类型的查询器。在第一段代码中用的是静态方法调用 QueryBuilders.boolQuery() 来创建 BoolQueryBuilder 对象,而在第二段代码中则是直接实例化一个 BoolQueryBuilder 对象。本质上,它们的功能和效果是相同的,只是实现方式不同
 

思路:

        1、构建一个条件查询的对象ResearchRequest

        2、聚合非常灵活,如果要先根据一个条件在进行聚合的话可以使用直接查找的用法

        3、重点:

        (1)指定聚合字段使用以下方法来指定聚合的字段

TermsAggregationBuilder field = AggregationBuilders.terms("聚合字段名字").field("依据聚合的属性");

        (2)将对象也构建进入searchSourceBuilder使用aggregation的方法参数为field

        (3)返回结果集的属性要根据下面的属性来如果是terms则结果的返回值类型要用Terms

如果是sum/avg则是对应的类型。

TermsAggregationBuilder field = AggregationBuilders.terms("stu_count").field("teacherId");
  SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
  Terms stu_count = response.getAggregations().get("stu_count");

        (4)获取结果集使用getBuckets方法,如果使用的是sum/avg则不需要结果集直接

        (5)获取分组依据使用结果的bucket.getKeyAsString()方法 

上代码!!!!

@Service
public class EsServiceImpl implements EsService {
    @Autowired
    RestHighLevelClient client;
    @Override
    public Result<List<StudentResType>> agg(String teacherId) {
        //提前定义一个结果结合
        List<StudentResType> list = new ArrayList<>();
        SearchRequest searchRequest = new SearchRequest("student");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(QueryBuilders.matchQuery("teacherId",teacherId));
        TermsAggregationBuilder field = AggregationBuilders.terms("stu_count").field("teacherId");
//        SumAggregationBuilder field1 = AggregationBuilders.sum("stu_count").field("teacherId");
        searchSourceBuilder.query(boolQueryBuilder);
        searchSourceBuilder.aggregation(field);
        searchRequest.source(searchSourceBuilder);
        try {
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            Terms stu_count = response.getAggregations().get("stu_count");
            List<? extends Terms.Bucket> buckets = stu_count.getBuckets();
            for (Terms.Bucket bucket : buckets) {
                StudentResType studentResType = new StudentResType();
                studentResType.setCount(bucket.getDocCount()+"");
                studentResType.setTeacherId(bucket.getKeyAsString());
                list.add(studentResType);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //处理结果
        return Result.success(list);
    }
}

 

@Override
    public Result<List<ResponseType>> showType() {
        long userId=0;
        long userRole=0;

        //先取出当前登入人
        String sum="";
        String avg="";
        String token = request.getHeader("token");
        String userKey = JwtUtils.getUserKey(token);
        if(redisTemplate.hasKey(TokenConstants.LOGIN_TOKEN_KEY+userKey)){
            //获取到当前登入人,做普通老师只能看到自己班同学的分组情况
            String s = redisTemplate.opsForValue().get(TokenConstants.LOGIN_TOKEN_KEY + userKey);
            ResponseUser user = JSON.parseObject(s, ResponseUser.class);
            userId=user.getUserId();
            userRole=user.getUserRole();
        }

        //定义分组的新集合
        List<ResponseType> responseTypes = new ArrayList<>();

        try {
            //创建搜索Search
            SearchRequest searchRequest = new SearchRequest(INDEX_NAME);

            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            //创建boolQueryBuilder进行多条件查询
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

            if(userRole==1){
                boolQueryBuilder.must(QueryBuilders.matchQuery("userId",userId));
            }

            searchSourceBuilder.query(boolQueryBuilder);
            //terms精准值查询
            TermsAggregationBuilder field1 = AggregationBuilders.terms("type_count").field("typeName");

            //.出来的sum方法是分组求和,只对数组类型有效
            SumAggregationBuilder field = AggregationBuilders.sum("sum_count").field("studentAge");
            //.avg是平均值 返回的对象则是AvgAggregationBuilder前面加的是Avg
            AvgAggregationBuilder field2 = AggregationBuilders.avg("avg_count").field("studentAge");
            //放入Builder
            searchSourceBuilder.aggregation(field);

            searchSourceBuilder.aggregation(field1);

            searchSourceBuilder.aggregation(field2);
            //将最大值Builder放入搜索里面
            searchRequest.source(searchSourceBuilder);

            //发送请求去查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            Terms type_count = searchResponse.getAggregations().get("type_count");

            List<? extends Terms.Bucket> buckets = type_count.getBuckets();
            for (Terms.Bucket bucket : buckets) {
                ResponseType responseType = new ResponseType();
                responseType.setCount(bucket.getDocCount());
                //获取分组的名称
                responseType.setKey(bucket.getKeyAsString());
                responseTypes.add(responseType);
            }
            //使用什么名字查找的就用什么名字找出来
            Sum sum_count = searchResponse.getAggregations().get("sum_count");
            Avg age_count = searchResponse.getAggregations().get("avg_count");
            double value1 = age_count.getValue();
            double value = sum_count.getValue();
            sum=value+"";
            avg=value1+"";
        } catch (IOException e) {
            e.printStackTrace();
        }


        return Result.success(responseTypes,avg);
    }

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值