SpringBoot3.0.7整合Elasticsearch8.7.1

这里的案例是围绕着查询诗词来进行练习的

主页下载里有数据库,自取!!!!!!!

1.添加依赖

添加springboot整合es的依赖

 <dependencies>
         <!--redis的依赖-->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-redis</artifactId>
         </dependency>
         <!--es的依赖-->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
 ​
         <dependency>
             <groupId>com.mysql</groupId>
             <artifactId>mysql-connector-j</artifactId>
             <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
         </dependency>
         
         <!--MyBatis Plus依赖-->
         <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-boot-starter</artifactId>
             <version>3.5.3.1</version>
         </dependency>
 </dependencies>

2.nacos配置中心

必须在nacos提前配置好datasource.yml和redis.yml

datasource.yml的配置:

Data ID:datasource.yml

配置格式:选择YAML

 
spring:
   datasource:
     url: jdbc:mysql://192.168.*****:3306/${db.name}?serverTimezone=Asia/Shanghai
     driver-class-name: com.mysql.cj.jdbc.Driver
     username: root
     password: 123456

redis.yml:

Data ID:redis.yml

配置格式:选择YAML

 spring:
   data:
     redis:
       password: 123456
       host: 192.168.****
       port: 6379

3.添加配置

application.yml文件:

 spring:
   #服务名称
   application:
     name: poetry
   #链接es的配置
   elasticsearch:
     uris: http://192.168.*****:9200
     username: jipiao
     password: 123456
 #要链接数据库的名
 db:
   name: poetry

bootstrap.yml文件:

 spring:
   cloud:
     nacos:
       #nacos访问链接
       server-addr: 192.168.******:8848   #访问路径
       #从nacos配置中心里读取配置文件
       config:
         file-extension: yaml   #配置中心配置的类型
         shared-configs:
           - data-id: datasource.yml     #配置中心中要访使用的配置名称
             refresh: true               #开启热启动(动态刷新)
           - data-id: redis.yml
             refresh: true

4.编写启动类

 @SpringBootApplication
 @MapperScan("com.axjy.poetry")
 public class GushiApplication {
 ​
     public static void main(String[] args) {
         SpringApplication.run(GushiApplication.class, args);
     }
 ​
 }

5.添加pojo实体类

 package com.axjy.poetry.pojo;
 ​
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
 import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
 import lombok.Data;
 ​
 import java.io.Serializable;
 import java.time.LocalDateTime;
 ​
 @Data
 public class Poetry implements Serializable {
 ​
     private String _class;
 ​
     /**
      * 诗词编号
      */
     private Integer id;
     /**
      * 标题
      */
     private String title;
     /**
      * 内容
      */
     private String content;
     /**
      * 朝代
      */
     private String dynasty;
     /**
      * 作者
      */
     private String name;
     /**
      * 作者描述
      */
     private String intro;
 ​
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
     @JsonSerialize(using = LocalDateTimeSerializer.class)
     @JsonDeserialize(using = LocalDateTimeDeserializer.class)
     private LocalDateTime updateTime;
 }
 ​

6.添加mapper

 
package com.axjy.poetry.mapper;
 ​
 import com.axjy.poetry.pojo.Poetry;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Select;
 ​
 import java.util.List;
 ​
 @Mapper
 public interface PoetryMapper {
     @Select("select t1.id, name,title,content,updateTime,t1.dynasty from poetry t1 join poetry_author t2 on t1.author_id=t2.id")
     List<Poetry> searchAll();
 }
 ​

7.添加一个测试类

创建测试类的原因是要进行创建索引和初始化数据

 package com.axjy.poetry;
 ​
 import com.axjy.poetry.elastic.DocSource;
 import com.axjy.poetry.mapper.PoetryMapper;
 import com.axjy.poetry.pojo.Poetry;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import jakarta.annotation.Resource;
 import lombok.SneakyThrows;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
 import org.springframework.data.elasticsearch.core.IndexOperations;
 import org.springframework.data.elasticsearch.core.query.IndexQuery;
 ​
 import java.util.ArrayList;
 import java.util.List;
 ​
 @SpringBootTest
 public class PoetryApplicationTest {
     @Resource
     private ElasticsearchTemplate elasticsearchTemplate;
 ​
     @Resource
     private PoetryMapper poetryMapper;
 ​
     /**
      * 创建索引
      */
     @Test
     void createIndex(){
         //用于创建es索引
         IndexOperations opt = elasticsearchTemplate.indexOps(DocSource.class);
         //判断索引是否存在,存在就先删除再创建,不存在就直接创建
         if (opt.exists()){
             //删除索引
             opt.delete();
         }
         //创建索引
         opt.createWithMapping();
         System.out.println("创建索引映射成功!");
     }
 ​
     /**
      * 初始化数据源
      */
     @SneakyThrows
     @Test
     void initData(){
         List<Poetry> list = poetryMapper.searchAll();
         List<IndexQuery> queries = new ArrayList<>();
         int count=0;
         for (int i=0;i < list.size();i++) {
             Poetry poetry = list.get(i);
             IndexQuery indexQuery = new IndexQuery();
             indexQuery.setId(poetry.getId()+"");
             ObjectMapper objectMapper = new ObjectMapper();
             String json = objectMapper.writeValueAsString(poetry);
             indexQuery.setSource(json);
             queries.add(indexQuery);
             if(i % 2000 == 0) {
                 elasticsearchTemplate.bulkIndex(queries, DocSource.class);
                 queries.clear();
                 System.out.println("提交了《--"+(++count)+"--》次");
             }
 ​
         }
 ​
         if (queries.size()>0){
             elasticsearchTemplate.bulkIndex(queries, DocSource.class);
             queries.clear();
             System.out.println("提交了《--"+(++count)+"--》次");
         }
         System.out.println("\n-------添加完毕!-------");
     }
 }
 ​

8.添加查询的测试类

 package com.axjy.poetry;
 ​
 import co.elastic.clients.elasticsearch.ElasticsearchClient;
 import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
 import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
 import co.elastic.clients.elasticsearch._types.query_dsl.RangeQuery;
 import co.elastic.clients.elasticsearch.core.SearchResponse;
 import co.elastic.clients.elasticsearch.core.search.Highlight;
 import co.elastic.clients.elasticsearch.core.search.Hit;
 import co.elastic.clients.json.JsonData;
 import com.axjy.poetry.pojo.Poetry;
 import jakarta.annotation.Resource;
 import lombok.SneakyThrows;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 ​
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.util.List;
 ​
 @SpringBootTest
 public class SearchTest {
 ​
     @Resource
     private ElasticsearchClient client;
 ​
     @SneakyThrows
     @Test
     void testMatch(){
         LocalDate endDate2 = LocalDate.now();
         DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
         String endDate = endDate2.format(fmt);
 ​
         //查询条件
         MatchQuery query1 = MatchQuery.of(builder -> builder.field("content").query("三峽"));
         RangeQuery query2 = RangeQuery.of(builder -> builder.field("updateTime").lte(JsonData.of(endDate)));
 ​
         BoolQuery bool = BoolQuery.of(builder -> builder.must(query1._toQuery(),query2._toQuery()));
 ​
         //构建查询构造器
         Highlight highlight = Highlight.of(builder -> builder.fields("content",h->h.preTags("\33[31;1m").postTags("\33[30;2m")));
 ​
         //查询分页
         SearchResponse<Poetry> resp = client.search(builder -> builder.index("poetry").highlight(highlight).query(bool._toQuery()).from(0).size(10), Poetry.class);
 ​
         System.out.println("-----------"+resp.hits().total().value());
 ​
         //得到命中
         List<Hit<Poetry>> list = resp.hits().hits();
 ​
         //高亮
             List<Poetry> list2 = list.stream().map(hit->{
                 Poetry poetry = hit.source();
             //得到高亮的片段
             String segment = hit.highlight().get("content").get(0);
             //内容的片段
             poetry.setContent(segment);
             return poetry;
         }).toList();
 ​
         list2.forEach(System.out::println);
     }
 }

9.将数据存入redis里面

添加一个测试文件

 package com.axjy.poetry;
 ​
 import com.axjy.poetry.pojo.Poetry;
 import jakarta.annotation.Resource;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.data.redis.core.ValueOperations;
 ​
 import java.time.LocalDateTime;
 ​
 @SpringBootTest
 public class RedisTest {
     @Resource
     private RedisTemplate redisTemplate;
 ​
     //将数据存储到redis当中
     @Test
     void save(){
         Poetry poetry = new Poetry();
         poetry.setName("李白");
         poetry.setUpdateTime(LocalDateTime.now());
         ValueOperations valueOperations = redisTemplate.opsForValue();
         valueOperations.set("jp:1001",poetry);
     }
 ​
     //取出redis里的数据
     @Test
     void get(){
         ValueOperations valueOperations = redisTemplate.opsForValue();
         Poetry poetry = (Poetry) valueOperations.get("jp:1001");
         System.out.println(poetry);
     }
 }
 
 

编写后台程序了

1.添加PoetryVo类、Pager分页类、DocSource文档类

PoetryVo类:

 package com.axjy.poetry.elastic;
 ​
 import lombok.Data;
 ​
 import java.time.LocalDate;
 ​
 @Data
 public class PoetryVo {
     private String keyword;
     private String dynasty;
     private LocalDate startDate;
     private LocalDate endDate;
 ​
     //排序
     private String sort; //updateTime
 ​
     private int pageNo;
     private int pageSize;
 }
 ​

Pager分页类:

 package com.axjy.poetry.elastic;
 import lombok.Data;
 ​
 import java.util.List;
 ​
 @Data
 public class Pager {
     private List rows;
 ​
     private long total;
 ​
 }
DocSource文档类:

 package com.axjy.poetry.elastic;
 ​
 import co.elastic.clients.util.DateTime;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
 import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
 import org.springframework.data.elasticsearch.annotations.Document;
 import org.springframework.data.elasticsearch.annotations.Field;
 import org.springframework.data.elasticsearch.annotations.FieldType;
 ​
 import java.time.LocalDateTime;
 ​
 @Document(indexName = "poetry")
 public class DocSource {
     /**
      * 标题
      */
     @Field(type = FieldType.Text,analyzer = "ik_max_word")
     private String title;
     /**
      * 内容
      */
     @Field(type = FieldType.Text,analyzer = "ik_max_word")
     private String content;
     /**
      * 朝代
      */
     @Field(type = FieldType.Keyword)
     private String dynasty;
     /**
      * 作者
      */
     @Field(type = FieldType.Text,analyzer = "ik_smart")
     private String name;
     /**
      * 作者描述
      */
     @Field(type = FieldType.Text,analyzer = "ik_max_word")
     private String intro;
 ​
     @Field(type = FieldType.Date,pattern = "uuuu-MM-dd||uuuu-MM-dd HH:mm")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
     @JsonSerialize(using = LocalDateTimeSerializer.class)
     @JsonDeserialize(using = LocalDateTimeDeserializer.class)
     private LocalDateTime updateTime;
 }
 ​

2.添加service

 package com.axjy.poetry.service;
 ​
 import com.axjy.poetry.elastic.Pager;
 import com.axjy.poetry.elastic.PoetryVo;
 ​
 public interface PoetryService {
 ​
     Pager query(PoetryVo vo);
 }
 ​

3.添加实现service接口的impl类

 package com.axjy.poetry.service;
 ​
 import co.elastic.clients.elasticsearch.ElasticsearchClient;
 import co.elastic.clients.elasticsearch._types.ScoreSort;
 import co.elastic.clients.elasticsearch._types.SortOptions;
 import co.elastic.clients.elasticsearch._types.SortOrder;
 import co.elastic.clients.elasticsearch._types.query_dsl.*;
 import co.elastic.clients.elasticsearch.core.SearchResponse;
 import co.elastic.clients.elasticsearch.core.search.Highlight;
 import co.elastic.clients.elasticsearch.core.search.HighlightField;
 import co.elastic.clients.json.JsonData;
 import com.axjy.poetry.elastic.Pager;
 import com.axjy.poetry.elastic.PoetryVo;
 import com.axjy.poetry.mapper.PoetryMapper;
 import com.axjy.poetry.pojo.Poetry;
 import jakarta.annotation.Resource;
 import lombok.SneakyThrows;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 ​
 import java.time.format.DateTimeFormatter;
 import java.util.List;
 ​
 @Service("poetryService")
 public class PoetryServiceImpl implements PoetryService{
 ​
     @Resource
     private ElasticsearchClient client;
 ​
     @SneakyThrows
     @Override
     public Pager query(PoetryVo vo) {
         //动态bool构造器
         BoolQuery.Builder boolBuilder = new BoolQuery.Builder();
 ​
         if (StringUtils.hasLength(vo.getDynasty())){
             TermQuery query = TermQuery.of(builder -> builder.field("dynasty").value(vo.getDynasty()));
             boolBuilder.must(query._toQuery());
         }
         DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
         if (vo.getStartDate()!=null){
             RangeQuery query = RangeQuery.of(builder -> builder.field("updateTime").gte(JsonData.of(vo.getStartDate().format(fmt))));
             boolBuilder.must(query._toQuery());
         }
         if (vo.getEndDate()!=null){
             RangeQuery query = RangeQuery.of(builder -> builder.field("updateTime").lt(JsonData.of(vo.getEndDate().plusDays(1).format(fmt))));
             boolBuilder.must(query._toQuery());
         }
 ​
         if (StringUtils.hasLength(vo.getKeyword())){
             MultiMatchQuery query = MultiMatchQuery.of(builder -> builder.fields("name^4","title^3","content").query(vo.getKeyword()));
         }
         Highlight.Builder hb = new Highlight.Builder();
         HighlightField hf = new HighlightField.Builder().build();
 ​
         hb.preTags("<strong style=\"color: red;\">").postTags("</strong>");
         hb.fields("name",hf);
         hb.fields("content",hf);
         hb.fields("title",hf);
 ​
         Highlight highlight = hb.build();
 ​
         int offset = (vo.getPageNo()-1)* vo.getPageSize();
 ​
         //处理排序
         SortOptions.Builder sort = new SortOptions.Builder();
         if (StringUtils.hasLength(vo.getSort())){
             if ("updateTime".equals(vo.getSort())){
                 sort.field(builder -> builder.field("updateTime").order(SortOrder.Desc));
             }else if (vo.getSort().equals("tuijian")){
                 sort.score(ScoreSort.of(builder -> builder.order(SortOrder.Desc)));
             }
         }else {
             sort.score(ScoreSort.of(builder -> builder.order(SortOrder.Desc)));
         }
 ​
         //处理高亮、分页、排序
         SearchResponse<Poetry> resp = client.search(builder ->
                 builder.index("poetry")
                         .highlight(highlight)
                         .query(boolBuilder.build()._toQuery())
                         .from(offset)
                         .size(vo.getPageSize())
                         .sort(sort.build())
                 ,Poetry.class);
 ​
         long total = resp.hits().total().value();
         System.out.println();
         Pager pager = new Pager();
         pager.setTotal(total);
         List<Poetry> rows = resp.hits().hits().stream().map(hit->{
             Poetry poetry = hit.source();
             //处理分页高亮
             //是否存在高亮状态(如果分页结果包含该文本时再进行处理高亮)
             if (hit.highlight().containsKey("name")){
                 poetry.setName(hit.highlight().get("name").get(0));
             }
             if (hit.highlight().containsKey("title")){
                 poetry.setTitle(hit.highlight().get("title").get(0));
             }
             if (hit.highlight().containsKey("content")){
                 poetry.setContent(hit.highlight().get("content").get(0));
             }
             return poetry;
         }).toList();
 ​
         pager.setRows(rows);
         return pager;
     }
 }

4.添加Result工具类

 package com.axjy.poetry.controller;
 ​
 import lombok.Data;
 ​
 @Data
 public class Result {
     private boolean success;
     private Object data;
     private String code;
     private String img;
 ​
     public static Result success(Object data){
         Result result = new Result();
         result.setSuccess(true);
         result.setData(data);
         return result;
     }
 }

5.最后一步,添加Controller类

 package com.axjy.poetry.controller;
 ​
 import com.axjy.poetry.elastic.Pager;
 import com.axjy.poetry.elastic.PoetryVo;
 import com.axjy.poetry.service.PoetryService;
 import jakarta.annotation.Resource;
 import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 ​
 @CrossOrigin
 @RestController
 public class PoetryController {
     @Resource
     private PoetryService poetryService;
 ​
     @RequestMapping("/poetry/query")
     private Result query(@RequestBody PoetryVo vo){
         Pager pager = poetryService.query(vo);
         return Result.success(pager);
     }
 }

最后,前端访问/poetry/query路径就可以直接访问查询了(记得传入要进行搜索的词语)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值