SpringBoot集成 ElasticSearch
作者:mmseoamin日期:2024-01-18

Spring Boot 集成 ElasticSearch

对于ElasticSearch比较陌生的小伙伴可以先看看ElasticSearch的概述ElasticSearch安装、启动、操作及概念简介

好的开始啦~

1、基础操作

1.1、导入依赖


    org.springframework.boot
    spring-boot-starter-data-elasticsearch

  • 新版本配置方式(推荐使用)

    新的配置方式使用的是 High Level REST Client 的方式来替代之前的 Transport Client 方式,使用的是 HTTP 请求,和 Kibana 一样使用的是 Elasticsearch 的 9200 端口。

    1.2、自定义配置类

    这种配置方案中,你使用的不是配置文件,而是自定义配置类:

    /**
     * 你也可以不继承 AbstractElasticsearchConfiguration 类,而将 ESConfig 写成一般的配置类的型式。
     * 不过继承 AbstractElasticsearchConfiguration 好处在于,它已经帮我们配置好了 elasticsearchTemplate 直接使用。
     */
    @Configuration
    public class ESConfig extends AbstractElasticsearchConfiguration {
        @Override
        public RestHighLevelClient elasticsearchClient() {
            ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo("localhost:9200")
                .build();
            return RestClients.create(clientConfiguration).rest();
        }
    }
    

    1.3、实体类

    Elasticsearch 中的 PO 类:

    @Document(indexName = "books",shards = 1,replicas = 0)
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class ESBook {
        @Id
        @Field(type = FieldType.Keyword)
        private String id;
        @Field(type = FieldType.Text)
        private String title;
        @Field(type = FieldType.Keyword)
        private String language;
        @Field(type = FieldType.Keyword)
        private String author;
        @Field(type = FieldType.Float)
        private Float price;
        @Field(type = FieldType.Text)
        private String description;
    }
    
    • @Document :注解会对实体中的所有属性建立索引;
    • indexName = “books” :表示创建一个名称为 “books” 的索引;
    • shards = 1 : 表示只使用一个分片;
    • replicas = 0 : 表示不使用复制备份;
    • @Field(type = FieldType.Keyword) : 用以指定字段的数据类型。

      2、 创建操作的 Repository

      @Repository
      //看实体类Id索引是什么类型 我这里是String
      public interface ESBookRepstitory extends ElasticsearchRepository {
          
      }
      

      SpringBoot集成 ElasticSearch,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5wSibzg8-1681375145182)(assets\spring-boot-es-02.png)],第1张

      我们自定义的 CustomerRepository 接口,从它的祖先们那里继承了大量的现成的方法,除此之外,它还可以按 spring data 的规则定义特定的方法。

      3、 测试 CustomerRepository

      // 创建索引
      @Test
      public void indexList() {
         System.out.println("创建索引");
      }
      // 删除索引
      @Test
      public void indexList() {
      	restTemplate.indexOps(IndexCoordinates.of("books")).delete();
          System.out.println("删除索引");
      }
      

      SpringBoot集成 ElasticSearch,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2HQkNX8j-1681375145182)(C:\Users\lps\AppData\Roaming\Typora\typora-user-images\image-20230413111252346.png)],第2张

      4.、CRUD操作

      4.1、批量新增

      SpringBoot集成 ElasticSearch,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xP3YE5M7-1681375145183)(C:\Users\lps\AppData\Roaming\Typora\typora-user-images\image-20230413142657737.png)],第3张

         @Autowired
         private ESBookRepstitory  bookByESRepstitory;
        @Test
          public void indexList() {
              List lists = new ArrayList<>();
              lists.add(new ESBook("1", "Java 程序设计", "汉语", "盖伦",
                      18.88F, "哈哈嗨"));
              lists.add(new ESBook("2", "Python程序设计", "英语", "赵信",
                      66.88F, "陷阵之至有死无生"));
              lists.add(new ESBook("3", "PHP 程序设计", "俄语", "宝石",
                      88.88F, "我曾踏足山巅,也曾跌入低谷"));
              bookByESRepstitory.saveAll(lists);
          }
      

      id重复的话 会覆盖之前的~~~

      4.2、修改

      修改和新增是同一个接口,区分的依据就是id,这一点跟我们在页面发起PUT请求是类似的。

           ESBook ESBook = new ESBook("3", "宝石 程序设计", "俄语", "宝石",
                      88.88F, "我曾踏足山巅,也曾跌入低谷");
              bookByESRepstitory.save(ESBook);
      //由于上面的id = 3 已经存在,故再次save 就是修改
      

      4.3、删除

      @Test
      public void test2(){
          bookByESRepstitory.deleteById("1");
          bookByESRepstitory.deleteAll();
      }
      

      4.4、基本查询

      1、ElasticsearchRepository提供了一些基本的查询方法:
      @Test
      public void testQuery(){
           Optional optionalById = this.bookByESRepstitory.findById("1");
           System.out.println(optionalById.get());
      }
       @Test
          public void testFind(){
              // 查询全部,并按照价格降序排序
              //写法一: 
              Iterable items = this.bookByESRepstitory.findAll(Sort.by(Sort.Direction.DESC,
                      "price"));
              //写法二: 
              Iterable items1 = this.bookByESRepstitory.findAll(Sort.by(Sort.Order.desc("price")));
          }
      
      2、分页查询

      Spring Data 自带的分页方案:

          @Test
          public void testByPage(){
              Sort sort = Sort.by(Sort.Direction.DESC,"id");
              //分页
              PageRequest pageRequest = PageRequest.of(0, 2, sort);
              Page all = bookByESRepstitory.findAll(pageRequest);
              for (BookByES bookByES : all) {
                  System.out.println(bookByES);
              }
          }
      

      SpringBoot集成 ElasticSearch,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q7SZLIEI-1681375145183)(C:\Users\lps\AppData\Roaming\Typora\typora-user-images\image-20230413121434941.png)],第4张

      4.5、自定义方法查询

      Spring Data 的另一个强大功能,是根据方法名称自动实现功能。

      比如:你的方法名叫做:findByTitle,那么它就知道你是根据title查询,然后自动帮你完成,无需写实现类。

      当然,方法名称要符合一定的约定

      KeywordSampleElasticsearch Query String
      AndfindByNameAndPrice{"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
      OrfindByNameOrPrice{"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
      IsfindByName{"bool" : {"must" : {"field" : {"name" : "?"}}}}
      NotfindByNameNot{"bool" : {"must_not" : {"field" : {"name" : "?"}}}}
      BetweenfindByPriceBetween{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
      LessThanEqualfindByPriceLessThan{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
      GreaterThanEqualfindByPriceGreaterThan{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
      BeforefindByPriceBefore{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
      AfterfindByPriceAfter{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
      LikefindByNameLike{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
      StartingWithfindByNameStartingWith{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
      EndingWithfindByNameEndingWith{"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}}
      Contains/ContainingfindByNameContaining{"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}}
      InfindByNameIn(Collectionnames){"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}
      NotInfindByNameNotIn(Collectionnames){"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}}
      NearfindByStoreNearNot Supported Yet !
      TruefindByAvailableTrue{"bool" : {"must" : {"field" : {"available" : true}}}}
      FalsefindByAvailableFalse{"bool" : {"must" : {"field" : {"available" : false}}}}
      OrderByfindByAvailableTrueOrderByNameDesc{"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}}

      如:

      import com.springsecurity.domain.ESBook;
      import org.springframework.data.domain.Pageable;
      import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
      import org.springframework.stereotype.Repository;
      import java.util.List;
      /**
       * @author 阿水
       * @create 2023-04-13 11:05
       */
      @Repository
      //看实体类Id索引是什么类型 我这里是String
      public interface ESBookRepstitory extends ElasticsearchRepository {
          /**
           * 根据描述查询书籍,带分页的
           * @param description
           * @return
           */
          List findESBookByDescription(String description, Pageable variable);
          /**
           * 根据作者和描述和标题查
           */
          List queryESBookByAuthorAndDescriptionOrTitle(String author,String description,String title);
          /**
           * 根据书的价格范围查询
           */
          List queryESBookByPriceBetween(Float price1,Float price2);
      }
      
      @Test
      void esQueryCondition() {
          Sort sort = Sort.by(Sort.Order.desc("id"));
          PageRequest pageRequest = PageRequest.of(0, 2, sort);
          List EBooks = bookByESRepstitory.findESBookByDescription("我",pageRequest);
          for (ESBook bookByE : EBooks) {
              System.out.println(bookByE);
          }
      }
      @Test
      void esQueryCondition2() {
          List esBooks = bookByESRepstitory.queryESBookByAuthorAndDescriptionOrTitle("盖伦", "哈嗨", "程序");
          for (ESBook book : esBooks) {
              System.out.println(book);
          }
      }
      @Test
      void esQueryCondition3() {
          List esBooks = bookByESRepstitory.queryESBookByPriceBetween(18.88F,77.88F);
          for (ESBook book : esBooks) {
              System.out.println(book);
          }
      }
      

      4.6、使用NativeSearchQuery

      @Autowired
      private ElasticsearchRestTemplate restTemplate;
      
      QueryBuilders.queryStringQuery() #指定字符串作为关键词查询,关键词支持分词
      QueryBuilders.queryStringQuery("华为手机").defaultField("description");
      //不指定feild,查询范围为所有feild
      QueryBuilders.queryStringQuery("华为手机");
      //指定多个feild
      QueryBuilders.queryStringQuery("华为手机").field("title").field("description");
      QueryBuilders.boolQuery          #子方法must可多条件联查
      QueryBuilders.termQuery          #精确查询指定字段不支持分词
      QueryBuilders.termQuery("description", "华为手机")
      QueryBuilders.matchQuery         #按分词器进行模糊查询支持分词
      QueryBuilders.matchQuery("description", "华为手机")    
      QueryBuilders.rangeQuery         #按指定字段进行区间范围查询
      - `QueryBuilders.boolQuery()`
      - `QueryBuilders.boolQuery().must()`:相当于 and
      - `QueryBuilders.boolQuery().should()`:相当于 or
      - `QueryBuilders.boolQuery().mustNot()`:相当于 not 
      - ——————————————————————————————————————————————————————————————————————————————————————————————————————
        @Test
          void naticeQuery() {
              NativeSearchQuery nativeSearchQuery =
                      new NativeSearchQueryBuilder()
                              //.withQuery(QueryBuilders.queryStringQuery("山巅哈哈").defaultField("description"))
                              //多条件查询
                              .withQuery(QueryBuilders.boolQuery()
                                      .must(QueryBuilders.queryStringQuery("山巅哈哈").defaultField("description"))
                                      .should(QueryBuilders.queryStringQuery("宝石").defaultField("title"))
                              )
                              .withPageable(PageRequest.of(0, 2))
                              .build();
              SearchHits search = restTemplate.search(nativeSearchQuery, ESBook.class);
              List> searchHits = search.toList();
              for (SearchHit searchHit : searchHits) {
                  System.out.println(searchHit);
              }
          }
      

      举个例子:

      SpringBoot集成 ElasticSearch,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zbyhHOcz-1681375145184)(C:\Users\lps\AppData\Roaming\Typora\typora-user-images\image-20230413160753038.png)],第5张

      5、es场景

      场景一:对外暴露的数据(数据量大的)的用es,如果不需要对外暴露,不需要全文检索的话,那么直接从数据查,所以做项目分析数据分成2块(哪些数据需要放es,从es查,哪些不需要)

      场景二:作为mysql的外置索引,把作为数据库查询条件的列数据放到es里面,这样在查询的时候,先从es查询出符合条件的id,然后根据id去数据库查,数据维护大,一旦es宕机,就麻烦了