SpringBoot3集成ElasticSearch

打印 上一主题 下一主题

主题 1050|帖子 1050|积分 3150

目录

标签:ElasticSearch8.Kibana8;
一、简介

Elasticsearch是一个分布式、RESTful风格的搜索和数据分析引擎,适用于各种数据类型,数字、文本、地理位置、结构化数据、非结构化数据;
在实际的工作中,历经过Elasticsearch从6.0到7.0的版本升级,而这次SpringBoot3和ES8.0的集成,虽然脚本的语法变化很小,但是Java客户端的API语法变化很大;
二、环境搭建

1、下载安装包

需要注意的是,这些安装包的版本要选择对应的,不然容易出问题;
  1. 软件包:elasticsearch-8.8.2-darwin-x86_64.tar.gz
  2. 分词器工具:elasticsearch-analysis-ik-8.8.2.zip
  3. 可视化工具:kibana-8.8.2-darwin-x86_64.tar.gz
复制代码
2、服务启动

不论是ES还是Kibana,在首次启动后,会初始化很多配置文件,可以根据自己的需要做相关的配置调整,比如常见的端口调整,资源占用,安全校验等;
  1. 1、启动ES
  2. elasticsearch-8.8.2/bin/elasticsearch
  3. 本地访问:localhost:9200
  4. 2、启动Kibana
  5. kibana-8.8.2/bin/kibana
  6. 本地访问:http://localhost:5601
  7. # 3、查看安装的插件
  8. http://localhost:9200/_cat/plugins  ->  analysis-ik 8.8.2
复制代码
三、工程搭建

1、工程结构


2、依赖管理

在starter-elasticsearch组件中,实际上依赖的是elasticsearch-java组件的8.7.1版本;
  1. <dependency>
  2.     <groupId>org.springframework.boot</groupId>
  3.     <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  4.     <version>${spring-boot.version}</version>
  5. </dependency>
复制代码
3、配置文件

在上面环境搭建的过程中,已经禁用了用户和密码的登录验证,配置ES服务地址即可;
  1. spring:
  2.   # ElasticSearch配置
  3.   elasticsearch:
  4.     uris: localhost:9200
复制代码
四、基础用法

1、实体类

通过Document和Field注解描述ES索引结构的实体类,注意这里JsonIgnoreProperties注解,解决索引中字段和实体类非一一对应的而引起的JSON解析问题;
  1. @JsonIgnoreProperties(ignoreUnknown = true)
  2. @Document(indexName = "contents_index", createIndex = false)
  3. public class ContentsIndex implements Serializable {
  4.     private static final long serialVersionUID=1L;
  5.     @Field(type= FieldType.Integer)
  6.     private Integer id;
  7.     @Field(type= FieldType.Keyword)
  8.     private String title;
  9.     @Field(type= FieldType.Keyword)
  10.     private String intro;
  11.     @Field(type= FieldType.Text)
  12.     private String content;
  13.     @Field(type= FieldType.Integer)
  14.     private Integer createId;
  15.     @Field(type= FieldType.Keyword)
  16.     private String createName;
  17.     @Field(type= FieldType.Date,format = DateFormat.date_hour_minute_second)
  18.     private Date createTime;
  19. }
复制代码
2、初始化索引

基于ElasticsearchTemplate类和上述实体类,实现索引结构的初始化,并且将tb_contents表中的数据同步到索引中,最后通过ID查询一条测试数据;
  1. @Service
  2. public class ContentsIndexService {
  3.     private static final Logger log = LoggerFactory.getLogger(ContentsIndexService.class);
  4.     @Resource
  5.     private ContentsService contentsService ;
  6.     @Resource
  7.     private ElasticsearchTemplate template ;
  8.     /**
  9.      * 初始化索引结构和数据
  10.      */
  11.     public void initIndex (){
  12.         // 处理索引结构
  13.         IndexOperations indexOps = template.indexOps(ContentsIndex.class);
  14.         if (indexOps.exists()){
  15.             boolean delFlag = indexOps.delete();
  16.             log.info("contents_index exists,delete:{}",delFlag);
  17.             indexOps.createMapping(ContentsIndex.class);
  18.         } else {
  19.             log.info("contents_index not exists");
  20.             indexOps.createMapping(ContentsIndex.class);
  21.         }
  22.         // 同步数据库表记录
  23.         List<Contents> contentsList = contentsService.queryAll();
  24.         if (contentsList.size() > 0){
  25.             List<ContentsIndex> contentsIndexList = new ArrayList<>() ;
  26.             contentsList.forEach(contents -> {
  27.                 ContentsIndex contentsIndex = new ContentsIndex() ;
  28.                 BeanUtils.copyProperties(contents,contentsIndex);
  29.                 contentsIndexList.add(contentsIndex);
  30.             });
  31.             template.save(contentsIndexList);
  32.         }
  33.         // ID查询
  34.         ContentsIndex contentsIndex = template.get("10",ContentsIndex.class);
  35.         log.info("contents-index-10:{}",contentsIndex);
  36.     }
  37. }
复制代码
3、仓储接口

继承ElasticsearchRepository接口,可以对ES这种特定类型的存储库进行通用增删改查操作;在测试类中对该接口的方法进行测试;
  1. // 1、接口定义
  2. public interface ContentsIndexRepository extends ElasticsearchRepository<ContentsIndex,Long> {
  3. }
  4. // 2、接口测试
  5. public class ContentsIndexRepositoryTest {
  6.     @Autowired
  7.     private ContentsIndexRepository contentsIndexRepository;
  8.     @Test
  9.     public void testAdd (){
  10.         // 单个新增
  11.         contentsIndexRepository.save(buildOne());
  12.         // 批量新增
  13.         contentsIndexRepository.saveAll(buildList()) ;
  14.     }
  15.     @Test
  16.     public void testUpdate (){
  17.         // 根据ID查询后再更新
  18.         Optional<ContentsIndex> contentsOpt = contentsIndexRepository.findById(14L);
  19.         if (contentsOpt.isPresent()){
  20.             ContentsIndex contentsId = contentsOpt.get();
  21.             System.out.println("id=14:"+contentsId);
  22.             contentsId.setContent("update-content");
  23.             contentsId.setCreateTime(new Date());
  24.             contentsIndexRepository.save(contentsId);
  25.         }
  26.     }
  27.     @Test
  28.     public void testQuery (){
  29.         // 单个ID查询
  30.         Optional<ContentsIndex> contentsOpt = contentsIndexRepository.findById(1L);
  31.         if (contentsOpt.isPresent()){
  32.             ContentsIndex contentsId1 = contentsOpt.get();
  33.             System.out.println("id=1:"+contentsId1);
  34.         }
  35.         // 批量ID查询
  36.         Iterator<ContentsIndex> contentsIterator = contentsIndexRepository
  37.                                         .findAllById(Arrays.asList(10L,12L)).iterator();
  38.         while (contentsIterator.hasNext()){
  39.             ContentsIndex contentsIndex = contentsIterator.next();
  40.             System.out.println("id="+contentsIndex.getId()+":"+contentsIndex);
  41.         }
  42.     }
  43.     @Test
  44.     public void testDelete (){
  45.         contentsIndexRepository.deleteById(15L);
  46.         contentsIndexRepository.deleteById(16L);
  47.     }
  48. }
复制代码
4、查询语法

无论是ElasticsearchTemplate类还是ElasticsearchRepository接口,都是对ES常用的简单功能进行封装,在实际使用时,复杂的查询语法还是依赖ElasticsearchClient和原生的API封装;
这里主要演示七个查询方法,主要涉及:ID查询,字段匹配,组合与范围查询,分页与排序,分组统计,最大值查询和模糊匹配;更多的查询API还是要多看文档中的案例才行;
  1. public class ElasticsearchClientTest {
  2.     @Autowired
  3.     private ElasticsearchClient client ;
  4.     @Test
  5.     public void testSearch1 () throws IOException {
  6.         // ID查询
  7.         GetResponse<ContentsIndex> resp = client.get(
  8.                 getReq ->getReq.index("contents_index").id("7"), ContentsIndex.class);
  9.         if (resp.found()){
  10.             ContentsIndex contentsIndex = resp.source() ;
  11.             System.out.println("contentsIndex-7:"+contentsIndex);
  12.         }
  13.     }
  14.     @Test
  15.     public void testSearch2 () throws IOException {
  16.         // 指定字段匹配
  17.         SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
  18.                         .query(query -> query.match(field -> field
  19.                         .field("createName").query("张三"))),ContentsIndex.class);
  20.         printResp(resp);
  21.     }
  22.     @Test
  23.     public void testSearch3 () throws IOException {
  24.         // 组合查询:姓名和时间范围
  25.         Query byName = MatchQuery.of(field -> field.field("createName").query("王五"))._toQuery();
  26.         Query byTime = RangeQuery.of(field -> field.field("createTime")
  27.                         .gte(JsonData.of("2023-07-10T00:00:00"))
  28.                         .lte(JsonData.of("2023-07-12T00:00:00")))._toQuery();
  29.         SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
  30.                         .query(query -> query.bool(boolQuery -> boolQuery.must(byName).must(byTime))),ContentsIndex.class);
  31.         printResp(resp);
  32.     }
  33.     @Test
  34.     public void testSearch4 () throws IOException {
  35.         // 排序和分页,在14条数据中,根据ID倒序排列,从第5条往后取4条数据
  36.         SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
  37.                 .from(5).size(4)
  38.                 .sort(sort -> sort.field(sortField -> sortField.field("id").order(SortOrder.Desc))),ContentsIndex.class);
  39.         printResp(resp);
  40.     }
  41.     @Test
  42.     public void testSearch5 () throws IOException {
  43.         // 根据createId分组统计
  44.         SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
  45.                 .aggregations("createIdGroup",agg -> agg.terms(term -> term.field("createId"))),ContentsIndex.class);
  46.         Aggregate aggregate = resp.aggregations().get("createIdGroup");
  47.         LongTermsAggregate termsAggregate = aggregate.lterms();
  48.         Buckets<LongTermsBucket> buckets = termsAggregate.buckets();
  49.         for (LongTermsBucket termsBucket : buckets.array()) {
  50.             System.out.println(termsBucket.key() + " : " + termsBucket.docCount());
  51.         }
  52.     }
  53.     @Test
  54.     public void testSearch6 () throws IOException {
  55.         // 查询最大的ID
  56.         SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index")
  57.                 .aggregations("maxId",agg -> agg.max(field -> field.field("id"))),ContentsIndex.class);
  58.         for (Map.Entry<String, Aggregate> entry : resp.aggregations().entrySet()){
  59.             System.out.println(entry.getKey()+":"+entry.getValue().max().value());
  60.         }
  61.     }
  62.     @Test
  63.     public void testSearch7 () throws IOException {
  64.         // 模糊查询title字段,允许1个误差
  65.         Query byContent = FuzzyQuery.of(field -> field.field("title").value("设计").fuzziness("1"))._toQuery();
  66.         SearchResponse<ContentsIndex> resp = client.search(
  67.                 searchReq -> searchReq.index("contents_index").query(byContent),ContentsIndex.class);
  68.         printResp(resp);
  69.     }
  70.     private void printResp (SearchResponse<ContentsIndex> resp){
  71.         TotalHits total = resp.hits().total();
  72.         System.out.println("total:"+total);
  73.         List<Hit<ContentsIndex>> hits = resp.hits().hits();
  74.         for (Hit<ContentsIndex> hit: hits) {
  75.             ContentsIndex contentsIndex = hit.source();
  76.             System.out.println(hit.id()+":"+contentsIndex);
  77.         }
  78.     }
  79. }
复制代码
五、参考源码
  1. 文档仓库:
  2. https://gitee.com/cicadasmile/butte-java-note
  3. 源码仓库:
  4. https://gitee.com/cicadasmile/butte-spring-parent
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

北冰洋以北

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表