当前位置: 首页 > news >正文

SpringBoot3集成ElasticSearch

标签:ElasticSearch8.Kibana8;

一、简介

Elasticsearch是一个分布式、RESTful风格的搜索和数据分析引擎,适用于各种数据类型,数字、文本、地理位置、结构化数据、非结构化数据;

在实际的工作中,历经过Elasticsearch从6.07.0的版本升级,而这次SpringBoot3和ES8.0的集成,虽然脚本的语法变化很小,但是Java客户端的API语法变化很大;

二、环境搭建

1、下载安装包

需要注意的是,这些安装包的版本要选择对应的,不然容易出问题;

软件包:elasticsearch-8.8.2-darwin-x86_64.tar.gz
分词器工具:elasticsearch-analysis-ik-8.8.2.zip
可视化工具:kibana-8.8.2-darwin-x86_64.tar.gz

2、服务启动

不论是ES还是Kibana,在首次启动后,会初始化很多配置文件,可以根据自己的需要做相关的配置调整,比如常见的端口调整,资源占用,安全校验等;

1、启动ES
elasticsearch-8.8.2/bin/elasticsearch本地访问:localhost:92002、启动Kibana
kibana-8.8.2/bin/kibana本地访问:http://localhost:5601# 3、查看安装的插件
http://localhost:9200/_cat/plugins  ->  analysis-ik 8.8.2

三、工程搭建

1、工程结构

2、依赖管理

starter-elasticsearch组件中,实际上依赖的是elasticsearch-java组件的8.7.1版本;

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId><version>${spring-boot.version}</version>
</dependency>

3、配置文件

在上面环境搭建的过程中,已经禁用了用户和密码的登录验证,配置ES服务地址即可;

spring:# ElasticSearch配置elasticsearch:uris: localhost:9200

四、基础用法

1、实体类

通过DocumentField注解描述ES索引结构的实体类,注意这里JsonIgnoreProperties注解,解决索引中字段和实体类非一一对应的而引起的JSON解析问题;

@JsonIgnoreProperties(ignoreUnknown = true)
@Document(indexName = "contents_index", createIndex = false)
public class ContentsIndex implements Serializable {private static final long serialVersionUID=1L;@Field(type= FieldType.Integer)private Integer id;@Field(type= FieldType.Keyword)private String title;@Field(type= FieldType.Keyword)private String intro;@Field(type= FieldType.Text)private String content;@Field(type= FieldType.Integer)private Integer createId;@Field(type= FieldType.Keyword)private String createName;@Field(type= FieldType.Date,format = DateFormat.date_hour_minute_second)private Date createTime;
}

2、初始化索引

基于ElasticsearchTemplate类和上述实体类,实现索引结构的初始化,并且将tb_contents表中的数据同步到索引中,最后通过ID查询一条测试数据;

@Service
public class ContentsIndexService {private static final Logger log = LoggerFactory.getLogger(ContentsIndexService.class);@Resourceprivate ContentsService contentsService ;@Resourceprivate ElasticsearchTemplate template ;/*** 初始化索引结构和数据*/public void initIndex (){// 处理索引结构IndexOperations indexOps = template.indexOps(ContentsIndex.class);if (indexOps.exists()){boolean delFlag = indexOps.delete();log.info("contents_index exists,delete:{}",delFlag);indexOps.createMapping(ContentsIndex.class);} else {log.info("contents_index not exists");indexOps.createMapping(ContentsIndex.class);}// 同步数据库表记录List<Contents> contentsList = contentsService.queryAll();if (contentsList.size() > 0){List<ContentsIndex> contentsIndexList = new ArrayList<>() ;contentsList.forEach(contents -> {ContentsIndex contentsIndex = new ContentsIndex() ;BeanUtils.copyProperties(contents,contentsIndex);contentsIndexList.add(contentsIndex);});template.save(contentsIndexList);}// ID查询ContentsIndex contentsIndex = template.get("10",ContentsIndex.class);log.info("contents-index-10:{}",contentsIndex);}
}

3、仓储接口

继承ElasticsearchRepository接口,可以对ES这种特定类型的存储库进行通用增删改查操作;在测试类中对该接口的方法进行测试;

// 1、接口定义
public interface ContentsIndexRepository extends ElasticsearchRepository<ContentsIndex,Long> {
}// 2、接口测试
public class ContentsIndexRepositoryTest {@Autowiredprivate ContentsIndexRepository contentsIndexRepository;@Testpublic void testAdd (){// 单个新增contentsIndexRepository.save(buildOne());// 批量新增contentsIndexRepository.saveAll(buildList()) ;}@Testpublic void testUpdate (){// 根据ID查询后再更新Optional<ContentsIndex> contentsOpt = contentsIndexRepository.findById(14L);if (contentsOpt.isPresent()){ContentsIndex contentsId = contentsOpt.get();System.out.println("id=14:"+contentsId);contentsId.setContent("update-content");contentsId.setCreateTime(new Date());contentsIndexRepository.save(contentsId);}}@Testpublic void testQuery (){// 单个ID查询Optional<ContentsIndex> contentsOpt = contentsIndexRepository.findById(1L);if (contentsOpt.isPresent()){ContentsIndex contentsId1 = contentsOpt.get();System.out.println("id=1:"+contentsId1);}// 批量ID查询Iterator<ContentsIndex> contentsIterator = contentsIndexRepository.findAllById(Arrays.asList(10L,12L)).iterator();while (contentsIterator.hasNext()){ContentsIndex contentsIndex = contentsIterator.next();System.out.println("id="+contentsIndex.getId()+":"+contentsIndex);}}@Testpublic void testDelete (){contentsIndexRepository.deleteById(15L);contentsIndexRepository.deleteById(16L);}
}

4、查询语法

无论是ElasticsearchTemplate类还是ElasticsearchRepository接口,都是对ES常用的简单功能进行封装,在实际使用时,复杂的查询语法还是依赖ElasticsearchClient和原生的API封装;

这里主要演示七个查询方法,主要涉及:ID查询,字段匹配,组合与范围查询,分页与排序,分组统计,最大值查询和模糊匹配;更多的查询API还是要多看文档中的案例才行;

public class ElasticsearchClientTest {@Autowiredprivate ElasticsearchClient client ;@Testpublic void testSearch1 () throws IOException {// ID查询GetResponse<ContentsIndex> resp = client.get(getReq ->getReq.index("contents_index").id("7"), ContentsIndex.class);if (resp.found()){ContentsIndex contentsIndex = resp.source() ;System.out.println("contentsIndex-7:"+contentsIndex);}}@Testpublic void testSearch2 () throws IOException {// 指定字段匹配SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index").query(query -> query.match(field -> field.field("createName").query("张三"))),ContentsIndex.class);printResp(resp);}@Testpublic void testSearch3 () throws IOException {// 组合查询:姓名和时间范围Query byName = MatchQuery.of(field -> field.field("createName").query("王五"))._toQuery();Query byTime = RangeQuery.of(field -> field.field("createTime").gte(JsonData.of("2023-07-10T00:00:00")).lte(JsonData.of("2023-07-12T00:00:00")))._toQuery();SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index").query(query -> query.bool(boolQuery -> boolQuery.must(byName).must(byTime))),ContentsIndex.class);printResp(resp);}@Testpublic void testSearch4 () throws IOException {// 排序和分页,在14条数据中,根据ID倒序排列,从第5条往后取4条数据SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index").from(5).size(4).sort(sort -> sort.field(sortField -> sortField.field("id").order(SortOrder.Desc))),ContentsIndex.class);printResp(resp);}@Testpublic void testSearch5 () throws IOException {// 根据createId分组统计SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index").aggregations("createIdGroup",agg -> agg.terms(term -> term.field("createId"))),ContentsIndex.class);Aggregate aggregate = resp.aggregations().get("createIdGroup");LongTermsAggregate termsAggregate = aggregate.lterms();Buckets<LongTermsBucket> buckets = termsAggregate.buckets();for (LongTermsBucket termsBucket : buckets.array()) {System.out.println(termsBucket.key() + " : " + termsBucket.docCount());}}@Testpublic void testSearch6 () throws IOException {// 查询最大的IDSearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index").aggregations("maxId",agg -> agg.max(field -> field.field("id"))),ContentsIndex.class);for (Map.Entry<String, Aggregate> entry : resp.aggregations().entrySet()){System.out.println(entry.getKey()+":"+entry.getValue().max().value());}}@Testpublic void testSearch7 () throws IOException {// 模糊查询title字段,允许1个误差Query byContent = FuzzyQuery.of(field -> field.field("title").value("设计").fuzziness("1"))._toQuery();SearchResponse<ContentsIndex> resp = client.search(searchReq -> searchReq.index("contents_index").query(byContent),ContentsIndex.class);printResp(resp);}private void printResp (SearchResponse<ContentsIndex> resp){TotalHits total = resp.hits().total();System.out.println("total:"+total);List<Hit<ContentsIndex>> hits = resp.hits().hits();for (Hit<ContentsIndex> hit: hits) {ContentsIndex contentsIndex = hit.source();System.out.println(hit.id()+":"+contentsIndex);}}
}

五、参考源码

文档仓库:
https://gitee.com/cicadasmile/butte-java-note源码仓库:
https://gitee.com/cicadasmile/butte-spring-parent
http://www.lryc.cn/news/130013.html

相关文章:

  • 详解23种设计模式优缺点以及解决方案
  • Oracle 数据库中删除表空间的详细步骤与示例
  • <kernel>kernel 6.4 笔记
  • 介绍一些编程语言— Perl 语言
  • 原型与继承
  • Flink流批一体计算(14):PyFlink Tabel API之SQL查询
  • JRebel插件扩展-mac版
  • C语言中常见的一些语法概念和功能
  • Python土力学与基础工程计算.PDF-钻探泥浆制备
  • 【机器学习】— 2 图神经网络GNN
  • QT的布局与间隔器介绍
  • 深入浅出Pytorch函数——torch.nn.Linear
  • Vue3.2+TS的defineExpose的应用
  • 牛客网Python入门103题练习|【08--元组】
  • Jenkins改造—nginx配置鉴权
  • (二)VisionOS平台概述
  • 菜单中的类似iOS中开关的样式
  • Vue 2 动态组件和异步组件
  • MongoDB升级经历(4.0.23至5.0.19)
  • iPhone上的个人热点丢失了怎么办?如何修复iPhone上不见的个人热点?
  • AI 媒人:为什么图形神经网络比 MLP 更好?
  • 信息学奥赛一本通 1984:【19CSPJ普及组】纪念品 | 洛谷 P5662 [CSP-J2019] 纪念品
  • JVM——JVM参数指南
  • 马上七夕到了,用各种编程语言实现10种浪漫表白方式
  • Spring Clould 注册中心 - Eureka,Nacos
  • 使用appuploader工具发布证书和描述性文件教程
  • 【面试八股文】每日一题:谈谈你对IO的理解
  • 200. 岛屿数量
  • 【LeetCode】581.最短无序连续子数组
  • 曲面(弧面、柱面)展平(拉直)瓶子标签识别ocr