【二十】springboot整合ElasticSearch实战(万字篇)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 【二十】springboot整合ElasticSearch实战(万字篇)


       本章开始学习springboot整合ElasticSearch 7.X版本并通过小demo实现基本的增删改查。实现如下案例:

1、当向数据新增一个商品信息时,同时向rabbitMQ发起消息(异步实现),让监听到消息的类去向ElasticSearch 也新增这个商品信息。

2、当去修改数据库的商品信息时,同时向rabbitMQ发起消息(异步实现),让监听到消息的类去向ElasticSearch 也去修改这个商品信息。

3、当删除数据库的商品信息时,同时也向rabbitMQ发起消息(异步实现),让监听到消息的类去向ElasticSearch 也去删除这个商品信息。

4、实现ElasticSearch的条件查询

5、实现ElasticSearch的分页查询

6、实现ElasticSearch的条件+分页查询。

7、实现ElasticSearch的高亮显示与查询条件相符合的值。

8、实现ElasticSearch的高亮显示与查询条件相符合的值并分页排序

最终效果:


      注意: 本章整合的ElasticSearch下载的客户端是windows版本,然后现在时间是2022.3.31。

官网提供的最新最新版本是8.1.1。最新版的使用跟以前7.X版本的完全不同,网上学习资料太少了,不得不降低版本学习7.X,本章节的学习下载的是elasticsearch-7.3.2,尝试了8.1.1的学习,问题实在是太多了,能力卑微,先不强求自己了。

注意:客户端下载的什么版本最好别去下最新版本,太难搞了。

!!!!提醒:本教程只适合elasticsearch-7.X版本,高版本不支持。

一、下载安装elasticsearch-7.3.2

下载本章节所用版本:

安装教程,转载自:


二、下载安装elasticsearch图像化插件(elasticsearch-head-master)

       安装完成elasticsearch之后,需要安装一个插件去图形化操作elasticsearch,教程上讲的需要先下载node,然后用node自带的npm下载grunt,随后去修改一下elasticsearch的配置文件即可通过9100端口访问图形界面,教程如下:

       执行命令运行head插件。

       ES双击elasticsearch-7.3.2\bin目录下的elasticsearch.bat即可。

       启动后长这个样子(启动时若会提示需要密码,这时需要去修改elasticsearch的配置文件)。


三、建立父子工程

       为了测试上述功能,先建立一个父子工程,在学习RabbitMQ和KafKa时都有建过,直接拿来使用,如下:


四、准备工作

       在开始之前,先做好准备工作,如下:

  • 构建好父子模块的关系
  • 在父工程引入所需依赖
  • 修改provider和consumer的配置文件
  • 创建mysql表
  • 安装rabbitMQ(默认已经安装好,前面章节有安装过)
  • 在common工程创建好公共模块

1、构建好父子模块的关系:

       此处不讲了,在前面的章节有讲过。

2、在父工程引入所需依赖

       此次学习需要用到mybatis-plus和rabbitMQ和elasticsearch,所有依赖情况如下:

       我的springboot版本是:2.6.4,其他依赖不加版本会根据springboot版本去自动匹配版本导入。

3、provider的配置文件

4、consumer的配置文件

       两者配置文件差不多,主要就是连接rabbitmq和elasticsearch,上图main:allow-bean-definition-overriding: true是我在测试时的一个报错,根据报错的一个bean的重复解决方案。

       注意:版本不一样,连接elasticsearch的写法不一样,高版本好像最基本的配置不需要了。

5、mysql表如下:

6、common模块代码如下:

       此处我的这个实体类有两个用处:

  • 用于作为mybatis-plus的实体类(TableName注解映射mysql表名、TableField注解映射mysql字段)
  • 用于作为elasticsearch的实体类(Document注解标记该实体类属于的索引和类型,类型默认是_doc类型,索引就相当于数据库的表名吧,若elasticsearch中不存在这个索引,会根据这个索引去创建一个、Field注解指定这个字段在elasticsearch是数值类型,但是在后面根据该字段排序时会使用。)
  • 此处采用序列化是因为在rabbitmq传输该实体类对象时,若不序列化传输会报错。

7、在消费者模块准备rabbitMQ的绑定配置类

@Configuration
public class TopicElasticSearchConfig {
 
    @Bean
    public TopicExchange saveExchange() {
        return new TopicExchange("es_save_exchange", true, false);
    }
 
    @Bean
    public TopicExchange deleteExchange() {
        return new TopicExchange("es_delete_exchange", true, false);
    }
 
    @Bean
    public Queue esSaveQueue() {
        return new Queue("es.save.queue", true);
    }
 
    @Bean
    public Queue esDeleteQueue() {
        return new Queue("es.delete.queue", true);
    }
 
    @Bean
    public Binding esSaveBinding() {
        return BindingBuilder.bind(esSaveQueue()).to(saveExchange()).with("es");
    }
 
    @Bean
    public Binding esDeleteBinding() {
        return BindingBuilder.bind(esDeleteQueue()).to(deleteExchange()).with("es");
    }
 
}

       建立一个接收修改和新增消息的交换机(因为对于elasticsearch来说,新增和修改方法一样),再建立一个接收删除消息的交换机,建立两个队列和绑定,如上图。

8、建立消费端的监听器

       监听上面建立的两个队列。

9、建立mybatis-plus所需要的mapper层

10、建立测试用controller

       准备工作完成。


五、实现ElasticSearch 新增商品信息

       要实现向mysql数据库新增数据的同时通过rabbitMQ接收消息然后向elasticsearch也插入数据,所以实现如下:

       先在controller编写新增接口

       向数据库新增数据,同时向rabbitmq发送新增的消息。

       然后再新增消息队列监听器编写向elasticsearch新增数据的逻辑。

       操作elasticsearch是通过继承Repository接口完成的,在消费者服务和生产者服务都新增如下类

       里面自定义的方法是后面所用到的,继承ElasticsearchRepository接口后默认提供了最基本的增删改查操作,类似Mybatis-plus的BaseMapper。

       ElasticsearchRepository可以直接调用save方法进行报错。

       结果如下:

       同步保存成功。


六、实现ElasticSearch 修改商品信息

       elasticsearch的修改操作也是调用Repository的save方法,若传入的id已存在则是执行修改,若传入的id不存在,则是新增。代码如下:

       同样在controller里加上修改接口,然后向数据修改,然后发送消息到MQ,然后再MQ的队列监听器进行处理。

       还是上面的新增逻辑,原因已经讲了。

       查看效果:

       这是原来的数据。

       调用接口后,这是现在的数据

       修改成功。


七、实现ElasticSearch 删除商品信息

       删除跟新增修改差不多,代码如下:

       controller新增删除接口。

       在删除队列监听器处理elasticsearch的删除方法。

       测试结果删除成功。


八、实现ElasticSearch 条件查询商品信息

       后面就只针对provider服务实现了,不通过MQ了。

       基本的增删改完了,然后一步一步的实现各种查询操作,指令实现基本的条件查询,如下。

       新增条件查询测试接口。

       根据name和content模糊查询,采用自定义查询语句的方式实现,这里要注意Repository的写法。

       根据什么查询就写findBy什么然后加字段名And或者Or之类的,然后写下一个字段名,这里只学习了最基本的使用,详细的就不展开了,后面再面向百度学习,然后方法传入的参数会根据参数位置一一匹配方法名前面写的字段名,Repository会自动生成查询语句。

       测试结果如下:

       条件查询成功。


九、实现ElasticSearch 分页查询商品信息

       然后实现一下分页查询,需要用到Pageable类,提供分页参数,Repository的方法支持这个类来查询,代码如下:

       依旧新增一个测试接口。

       测试结果:

       测试成功。注意:Pageable默认的入参是第0页,按每页20条查询。网上有说默认是(0,10),可能是该类的版本不同,我用的版本是默认0,20。对于elasticsearch来说是先从0开始的。还要注意传参是page和size,别写错了。返回的Page数据结构如下:


十、实现ElasticSearch 条件+分页查询商品信息

       当完成了分页查询和条件查询,下面写一个接口同时满足上面两个,代码如下。

       新增一个测试接口,然后同时传入查询条件和分页参数,重载一个刚才的方法,修改入参和返回值。

       测试结果如下。

       测试成功。


十一、实现ElasticSearch 高亮显示与查询条件相符合的值

       下面简单实现一个elasticsearch的特色,高亮显示。当我们在百度搜索时,例如搜索牛批。


       它能够高亮显示搜索到的信息中所匹配的值,下面通过elasticsearch简单实现一下,代码如下。

//高亮显示
    @CrossOrigin
    @GetMapping("getLightShopList")
    public List<Shop> getLightShopList(@RequestParam(required = false) String name) {
        //构建查询条件,只要name或者content其中一个满足传入的name的查询条件即可
//        QueryBuilder queryBuilder1 = new MatchQueryBuilder("name", name);
//        QueryBuilder queryBuilder2 = new MatchQueryBuilder("content", name);
        //上面这样写只能用到最后一个,要下面这样写,组合查询
        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .should(QueryBuilders.matchQuery("name", name))
                .should(QueryBuilders.matchQuery("content", name));
        //查询
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                //添加高亮显示字段
                .withHighlightFields(
                        new HighlightBuilder.Field("name")
                        , new HighlightBuilder.Field("content"))
                //自定义高亮显示颜色
                .withHighlightBuilder(new HighlightBuilder().preTags("<span style='color:yellow'>").postTags("</span>"))
                .build();
        //开始查询
        SearchHits<Shop> search = elasticsearchRestTemplate.search(searchQuery, Shop.class);
        //得到查询返回的内容
        List<SearchHit<Shop>> searchHits = search.getSearchHits();
        //设置一个最后需要返回的实体类集合
        List<Shop> shops = new ArrayList<>();
        //遍历返回的内容进行处理
        for (SearchHit<Shop> searchHit : searchHits) {
            //高亮的内容
            Map<String, List<String>> highlightFields = searchHit.getHighlightFields();
            //将高亮的内容填充到content中
            searchHit.getContent().setName(highlightFields.get("name") == null ? searchHit.getContent().getName() : highlightFields.get("name").get(0));
            searchHit.getContent().setContent(highlightFields.get("content") == null ? searchHit.getContent().getContent() : highlightFields.get("content").get(0));
            //放到实体类中
            shops.add(searchHit.getContent());
        }
        return shops;
    }

       上面的例子都是通过Repository提供的APi方法实现的,高亮显示通过它加上注解好像也可以实现,上面的方法是引入elasticsearchRestTemplate来实现的。

  • 先构建查询条件,通过第二种方式实现,第一种方式创建两个对象,在使用时第二个会覆盖第一个。
  • 整合查询条件以及高亮显示设置,自定义需要高亮显示的字段,并自定义高亮显示的字段的颜色。
  • 调用方法查询,并将返回的数据重新封装。

       测试结果如下:

       name和content字段中,相匹配的就会查询出来并高亮显示。具体的匹配规则和优先级都是可以设置的。


十二、实现ElasticSearch的高亮显示与查询条件相符合的值并分页和排序

       完成了最基本的高亮显示,最后再同时整点分页和排序。

       代码如下。

//高亮显示+分页+条件+排序
    @CrossOrigin
    @GetMapping("getLightShopByPageList")
    public Map getLightShopByPageList(@RequestParam(required = false) String name,Pageable pageable) {
        //组合查询
        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .should(QueryBuilders.matchQuery("name", name))
                .should(QueryBuilders.matchQuery("content", name));
        //构建排序,如果实体类上该字段没有加type,这里排序字段上面要加keyword,不然会报错。
//        FieldSortBuilder priceSortBuilder = SortBuilders.fieldSort("price.keyword").order(SortOrder.ASC);
        FieldSortBuilder priceSortBuilder = SortBuilders.fieldSort("price").order(SortOrder.ASC);
        //查询
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(queryBuilder)
                //添加高亮显示字段
                .withHighlightFields(new HighlightBuilder.Field("name"), new HighlightBuilder.Field("content"))
                //自定义高亮显示颜色
                .withHighlightBuilder(new HighlightBuilder().preTags("<span style='color:yellow'>").postTags("</span>"))
                .withPageable(pageable)
                .withSorts(priceSortBuilder)
                .build();
        //开始查询
        SearchHits<Shop> search = elasticsearchRestTemplate.search(searchQuery, Shop.class);
        //得到查询返回的内容
        List<SearchHit<Shop>> searchHits = search.getSearchHits();
        //设置一个最后需要返回的实体类集合
        List<Shop> shops = new ArrayList<>();
        //遍历返回的内容进行处理
        for (SearchHit<Shop> searchHit : searchHits) {
            //高亮的内容
            Map<String, List<String>> highlightFields = searchHit.getHighlightFields();
            //将高亮的内容填充到content中
            searchHit.getContent().setName(highlightFields.get("name") == null ? searchHit.getContent().getName() : highlightFields.get("name").get(0));
            searchHit.getContent().setContent(highlightFields.get("content") == null ? searchHit.getContent().getContent() : highlightFields.get("content").get(0));
            //放到实体类中
            shops.add(searchHit.getContent());
        }
        Map map = new HashMap();
        map.put("currentPageSIze",pageable.getPageSize());
        map.put("currentNumber",pageable.getPageNumber());
        map.put("content",shops);
        return map;
    }

       对比上一个就是增加了排序和分页,所有直接在上面改动了。

  • 通过FieldSortBuilder构建一个排序Builder通过withSorts方法加入到NativeSearchQuery中。
  • 新增传入对象Pageable,再通过withPageable方法加入到NativeSearchQuery。
  • 封装一下。

       注意:排序时,若实体类上price字段没加@Field(type = FieldType.Integer), 查询会报错,在查询时查询字段必须写成price.keyword,否则就必须在实体类上加@Field注解,不然请求接口测试会报elasticsearch的错。若一个分区的数据(不设置默认在一个分区),若存在price数据不是同种类型,排序也会失效,不知道对不对,反正我测试时遇到了这个问题,后面测试时把前面该索引(shop)直接删了重新来过的(为了保证price类型一致)。

       测试结果如下。

       若修改成SortOrder.DESC。

       查看排序成功。

       (2022.4.3新增)上面是通过elasticsearchRestTemplate的方法实现的,下面简单使用注解方式实现一下:

       修改Repository类:

       仍然借助Repository自动给我们生成查询语句,不同的是,我们自定义高亮显示字段,配合Highlight注解、HighlightField注解、HighlightParameters注解实现。

  1. Highlight:设置高亮显示参数。
  2. HighlightField:注明哪些参数需要高亮显示。
  3. HighlightParameters:自定义高亮的样式参数设置,类似elasticsearchRestTemplate里面的HighlightBuilder。

        controller层新增测试接口:

       然后写了一个简单的前端界面进行测试。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
  
  <body>
    <div class="row" style="margin-left: 30%;margin-top: 30px;">
      <div class="col-lg-6">
        <div class="input-group">
          <div class="input-group-prepend">
              <span class="input-group-text " id="basic-addon1">名称</span>
            </div>
        <input type="text" class="form-control name" placeholder="搜索">
        <div class="input-group-prepend" style="margin-left: 20px;">
            <span class="input-group-text " id="basic-addon1">内容</span>
          </div>
        <input type="text" class="form-control content" placeholder="搜索">
          <span class="input-group-btn">
            <button class="btn btn-primary" type="button" onclick="fun1()">搜索</button>
          </span>
        </div><!-- /input-group -->
      </div><!-- /.col-lg-6 -->
    </div><!-- /.row -->
    
    <div class="neirong" style="width: 100%;height: 500px;background-color: aliceblue;display: flex;justify-content: center;">
      
    </div>
  </body>
  
  <script>
    function fun1(){
      console.log("点击");
        
      $.ajax({
        url:"http://localhost:7778/shopController/hightLightShow2",
        data:{"page":"0","size":"10","name":$(".name").val(),"content":$(".content").val()},
        type:"get",
        dataType: "json",
        success: function(data) { 
          $(".neirong").html('');
          console.log(data);
          var html="";
          for(var i=0;i<data.length;i++){
            name = data[i].highlightFields.name==undefined?data[i].content.name:data[i].highlightFields.name
            content = data[i].highlightFields.content==undefined?data[i].content.content:data[i].highlightFields.content
            html = html+`
            <div class="card" style="width: 18rem;height:18rem;margin-right:10px">
              <div class="card-body">
                <h5 class="card-title">`+name+`</h5>
                <p class="card-text">`+content+`</p>
                <a href="#" class="btn btn-primary">购买</a>
              </div>
            </div>
            `
          }
          $(".neirong").append(html);
        }
      });
    }
  </script>
</html>

       最终效果就是下图这个样子:

       后面继续学习elasticsearch。

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
目录
相关文章
|
7天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
34 3
|
7天前
|
安全 Java 开发者
深入理解Spring Boot配置绑定及其实战应用
【4月更文挑战第10天】本文详细探讨了Spring Boot中配置绑定的核心概念,并结合实战示例,展示了如何在项目中有效地使用这些技术来管理和绑定配置属性。
17 1
|
7天前
|
Java 开发者 Spring
springboot DDD的概念以及实战
【5月更文挑战第15天】领域驱动设计(Domain-Driven Design,简称DDD)是一种软件设计方法论,它强调基于业务领域的复杂性来构建软件
18 2
|
7天前
|
开发框架 监控 Java
深入探索Spring Boot的监控、管理和测试功能及实战应用
【5月更文挑战第14天】Spring Boot是一个快速开发框架,提供了一系列的功能模块,包括监控、管理和测试等。本文将深入探讨Spring Boot中监控、管理和测试功能的原理与应用,并提供实际应用场景的示例。
19 2
|
7天前
|
搜索推荐 Java 数据库
springboot集成ElasticSearch的具体操作(系统全文检索)
springboot集成ElasticSearch的具体操作(系统全文检索)
|
7天前
|
Java Spring 容器
深入理解Spring Boot启动流程及其实战应用
【5月更文挑战第9天】本文详细解析了Spring Boot启动流程的概念和关键步骤,并结合实战示例,展示了如何在实际开发中运用这些知识。
23 2
|
7天前
|
人工智能 自然语言处理 开发者
Langchain 与 Elasticsearch:创新数据检索的融合实战
Langchain 与 Elasticsearch:创新数据检索的融合实战
33 10
|
7天前
|
canal 自然语言处理 关系型数据库
Elasticsearch 线上实战问题及解决方案探讨
Elasticsearch 线上实战问题及解决方案探讨
20 0
|
7天前
|
监控 API 索引
实战问题:Elasticsearch 2.X 数据如何迁移到 7.X?
实战问题:Elasticsearch 2.X 数据如何迁移到 7.X?
13 0
|
7天前
|
存储 Serverless 定位技术
深度探索 Elasticsearch 8.X:function_score 参数解读与实战案例分析
深度探索 Elasticsearch 8.X:function_score 参数解读与实战案例分析
12 0

热门文章

最新文章