写点什么

SpringBoot3 集成 ElasticSearch

作者:知了一笑
  • 2023-08-16
    浙江
  • 本文字数:5050 字

    阅读完需:约 17 分钟

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、启动ESelasticsearch-8.8.2/bin/elasticsearch
本地访问:localhost:9200
2、启动Kibanakibana-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 查询一条测试数据;


@Servicepublic class ContentsIndexService {    private static final Logger log = LoggerFactory.getLogger(ContentsIndexService.class);
@Resource private ContentsService contentsService ; @Resource private 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 { @Autowired private ContentsIndexRepository contentsIndexRepository;
@Test public void testAdd (){ // 单个新增 contentsIndexRepository.save(buildOne()); // 批量新增 contentsIndexRepository.saveAll(buildList()) ; }
@Test public 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); } }
@Test public 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); } }
@Test public void testDelete (){ contentsIndexRepository.deleteById(15L); contentsIndexRepository.deleteById(16L); }}
复制代码

4、查询语法

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


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


public class ElasticsearchClientTest {
@Autowired private ElasticsearchClient client ;
@Test public 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); } }
@Test public 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); }
@Test public 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); }
@Test public 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); }
@Test public 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()); } }
@Test public void testSearch6 () throws IOException { // 查询最大的ID SearchResponse<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()); } }
@Test public 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
复制代码


发布于: 刚刚阅读数: 3
用户头像

知了一笑

关注

公众号:知了一笑 2020-04-08 加入

源码仓库:https://gitee.com/cicadasmile

评论

发布
暂无评论
SpringBoot3集成ElasticSearch_Java_知了一笑_InfoQ写作社区