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

分布式专题——49 SpringBoot整合ElasticSearch8.x实战

1 Spring Data ElasticSearch 介绍

  • Spring Data Elasticsearch 是基于 Spring Data API 构建的框架,它的核心作用是简化 Elasticsearch 的操作——将 Elasticsearch 原始的客户端 API 进行封装,为 Elasticsearch 项目提供集成搜索引擎的能力;

  • 它的关键功能包括:

    • 以**POJO(Plain Old Java Object,普通 Java 对象)**为中心的模型,实现与 Elasticsearch 交互文档的便捷操作;

    • 帮助开发者轻松编写存储索引库的数据访问层,降低 Elasticsearch 数据操作的技术门槛;

  • 官网:Spring Data Elasticsearch;

    在这里插入图片描述

2 SpringBoot 整合 Spring Data ElasticSearch

2.1 版本选型

  • ElasticSearch 8.14.x 对应依赖 Spring Data Elasticsearch 5.3.x,对应Spring6.1.x,Spring Boot版本可以选择3.3.x;

    在这里插入图片描述

2.2 引入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
  • 如果 SpringBoot 版本选择的是3.3.2,那么对应的 Spring Data ElasticSearch 为5.3.2;

    在这里插入图片描述

2.3 配置 ElasticSearch

  • SpringBoot中有两种配置 ElasticSearch 的方式,选择一种即可;

  • 方式1:yaml文件配置

    spring:elasticsearch:uris: http://localhost:9200connection-timeout: 3s
    
  • 方式2:@Configuration配置

    @Configuration
    public class MyESClientConfig extends ElasticsearchConfiguration {@Overridepublic ClientConfiguration clientConfiguration() {return ClientConfiguration.builder().connectedTo("localhost:9200").build();}
    }
    

2.4 Java代码实现

2.4.1 方式1:ElasticsearchRepository

  • ElasticsearchRepository 是 Spring Data Elasticsearch 中的核心接口,用于简化 Elasticsearch 集群的CRUD(增删改查)操作及高级搜索功能集成。它采用声明式编程模型,让开发者无需直接编写复杂的 REST API 调用代码,即可完成数据持久化操作;

  • 创建实体类:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Document(indexName = "employees")
    public class Employee {@Idprivate Long id;@Field(type= FieldType.Keyword)private String name;private int sex;private int age;@Field(type= FieldType.Text,analyzer="ik_max_word")private String address;private String remark;
    }
    
  • 实现 ElasticsearchRepository 接口(EmployeeRepository)

    @Repository // 标记该接口为数据访问层组件,让 Spring 能够扫描并管理
    public interface EmployeeRepository extends ElasticsearchRepository<Employee, Long> {// 自定义查询方法。Spring Data支持方法名语义解析,只需按规则命名(如findBy+字段名),框架会自动生成对应的查询逻辑List<Employee> findByName(String name);
    }
    
  • 测试:

    @Autowired
    EmployeeRepository employeeRepository;@Test
    public void testDocument() {Employee employee = new Employee(10L, "fox666", 1, 32, "长沙麓谷", "java architect");//插入文档employeeRepository.save(employee);//根据id查询Optional<Employee> result = employeeRepository.findById(10L);if (!result.isEmpty()){log.info(String.valueOf(result.get()));}//根据name查询List<Employee> list = employeeRepository.findByName("fox666");if(!list.isEmpty()){log.info(String.valueOf(list.get(0)));}}
    
  • 更多实现:Query methods :: Spring Data Elasticsearch。

2.4.2 方式2:ElasticsearchTemplate

2.4.2.1 概述
  • ElasticsearchTemplate 是 Spring Data Elasticsearch 提供的模板类,封装了操作 Elasticsearch 的便捷方法,覆盖索引管理、文档CRUD、高级查询等底层操作和高级功能,让开发者无需直接处理 Elasticsearch 的 REST API 细节;

    @Autowired
    ElasticsearchTemplate elasticsearchTemplate;
    
  • ElasticSearch 客户端演进历史

    • Transport 客户端:基于 Elasticsearch 0.9 版本(2010年),是早期的客户端方案,后来因性能和兼容性问题被弃用;

    • REST 客户端

      • Low Level REST 客户端:Elasticsearch 5.0 版本(2016年)推出,提供基础的 HTTP 级 REST 通信;
      • High Level REST 客户端(RestHighLevelClient):Elasticsearch 5.6.0 版本(2017年)推出,对 Low Level 客户端封装,支持更易用的 API,但从 Java Rest Client 7.15.0 版本开始被官方标记为废弃
    • Java API 客户端(ElasticsearchClient:Elasticsearch 7.16 版本(2021年)推出,是官方推荐的新客户端。Spring Data Elasticsearch 对其进一步封装,形成了新的 ElasticsearchTemplate

    在这里插入图片描述

2.4.2.1 索引操作
@Autowired
ElasticsearchTemplate elasticsearchTemplate;@Test
public void testCreateIndex(){// 检查索引是否存在boolean exist = elasticsearchTemplate.indexOps(Employee.class).exists();// 若索引存在,则删除if(exist){elasticsearchTemplate.indexOps(Employee.class).delete();}// 创建索引// 配置settingsMap<String, Object> settings = new HashMap<>();settings.put("number_of_shards",1); // 配置索引的分片数,影响索引的性能settings.put("number_of_replicas",1); // 配置索引的副本数,影响索引的高可用性// 配置mappingString json = "{\n" +"      \"properties\": {\n" +"        \"_class\": {\n" +"          \"type\": \"text\",\n" +"          \"fields\": {\n" +"            \"keyword\": {\n" +"              \"type\": \"keyword\",\n" +"              \"ignore_above\": 256\n" +"            }\n" +"          }\n" +"        },\n" +"        \"address\": {\n" +"          \"type\": \"text\",\n" +"          \"fields\": {\n" +"            \"keyword\": {\n" +"              \"type\": \"keyword\"\n" +"            }\n" +"          },\n" +"          \"analyzer\": \"ik_max_word\"\n" +"        },\n" +"        \"age\": {\n" +"          \"type\": \"integer\"\n" +"        },\n" +"        \"id\": {\n" +"          \"type\": \"long\"\n" +"        },\n" +"        \"name\": {\n" +"          \"type\": \"keyword\"\n" +"        },\n" +"        \"remark\": {\n" +"          \"type\": \"text\",\n" +"          \"fields\": {\n" +"            \"keyword\": {\n" +"              \"type\": \"keyword\"\n" +"            }\n" +"          },\n" +"          \"analyzer\": \"ik_smart\"\n" +"        },\n" +"        \"sex\": {\n" +"          \"type\": \"integer\"\n" +"        }\n" +"      }\n" +"    }";Document mapping = Document.parse(json);// 创建索引elasticsearchTemplate.indexOps(Employee.class).create(settings,mapping);// 查看索引mappings信息Map<String, Object> mappings = elasticsearchTemplate.indexOps(Employee.class).getMapping();log.info(mappings.toString());
}
2.4.2.3 批量文档插入
@Autowired
ElasticsearchTemplate elasticsearchTemplate;@Test
public void testBulkBatchInsert(){// 构建 Employee 数据列表List<Employee> employees = new ArrayList<>();employees.add(new Employee(2L,"张三",1,25,"广州天河公园","java developer"));employees.add(new Employee(3L,"李四",1,28,"广州荔湾大厦","java assistant"));employees.add(new Employee(4L,"小红",0,26,"广州白云山公园","php developer"));// 通过 IndexQuery 封装每条数据的 ID 和 JSON 内容List<IndexQuery> bulkInsert = new ArrayList<>();for (Employee employee : employees) {IndexQuery indexQuery = new IndexQuery();indexQuery.setId(String.valueOf(employee.getId()));String json = JSONObject.toJSONString(employee);indexQuery.setSource(json);bulkInsert.add(indexQuery);}// 调用 elasticsearchTemplate.bulkIndex() 实现批量插入,提升大数据量下的写入效率elasticsearchTemplate.bulkIndex(bulkInsert,Employee.class);
}
2.4.2.4 文档CRUD
@Autowired
ElasticsearchTemplate elasticsearchTemplate;@Test
public void testDocument(){// 删除文档:调用 delete() 根据 ID 删除指定文档elasticsearchTemplate.delete(String.valueOf(12L),Employee.class);// 插入文档:调用 save() 插入单条 Employee 文档Employee employee = new Employee(12L,"张三三",1,25,"广州天河公园","java developer");elasticsearchTemplate.save(employee);// 查询文档:调用 get() 根据 ID 查询文档并打印结果Employee emp = elasticsearchTemplate.get(String.valueOf(12L),Employee.class);log.info(String.valueOf(emp));
}
2.4.2.5 精确查询
@Autowired
ElasticsearchTemplate elasticsearchTemplate;@Test
public void testQueryDocument(){// 条件查询:查询姓名为张三的员工信息/* GET /employee/_search{"query": {"term": {"name": {"value": "张三"}}}}*/// 方式1:直接编写 JSON 格式的查询语句//        Query query = new StringQuery("{\n" +//                "            \"term\": {\n" +//                "                \"name\": {\n" +//                "                    \"value\": \"张三\"\n" +//                "                }\n" +//                "            }\n" +//                "        }");// 方式2:通过编程式 API 构建查询,可读性更强Query query = NativeQuery.builder().withQuery(q -> q.term(t -> t.field("name").value("张三"))).build();SearchHits<Employee> search = elasticsearchTemplate.search(query, Employee.class);// 调用 search() 执行查询List<SearchHit<Employee>> searchHits = search.getSearchHits();// 解析返回的 SearchHits 结果for (SearchHit hit: searchHits){log.info("返回结果:"+hit.toString());}
}
2.4.2.6 分词匹配查询
@Autowired
ElasticsearchTemplate elasticsearchTemplate;@Test
public void testMatchQueryDocument(){// 条件查询:查询地址中至少匹配“广州”“公园”两个词的员工/*GET /employee/_search{"query": {"match": {"address": {"query": "广州公园","minimum_should_match": 2}}}}*/// 方式1:直接编写 JSON 格式的查询语句//        Query query = new StringQuery("{\n" +//                "            \"match\": {\n" +//                "                \"address\": {\n" +//                "                    \"query\": \"广州公园\",\n" +//                "                     \"minimum_should_match\": 2\n" +//                "                }\n" +//                "            }\n" +//                "        }");// 方式2:通过编程式 API 构建查询,可读性更强Query query = NativeQuery.builder().withQuery(q -> q.match(m -> m.field("address").query("广州公园").minimumShouldMatch("2"))) // 通过 minimumShouldMatch 配置最少匹配词数,满足模糊查询的业务场景.build();SearchHits<Employee> search = elasticsearchTemplate.search(query, Employee.class);List<SearchHit<Employee>> searchHits = search.getSearchHits();for (SearchHit hit: searchHits){log.info("返回结果:"+hit.toString());}
}
2.4.2.7 分页、排序、高亮查询
@Autowired
ElasticsearchTemplate elasticsearchTemplate;@Test
public void testQueryDocument3(){// 分页排序高亮:结合分页、排序、高亮实现复杂查询,查询 remark 中含 “JAVA” 的员工,按年龄倒序取前 3 条,并对匹配内容高亮显示/*GET /employee/_search{"from": 0,"size": 3,"query": {"match": {"remark": {"query": "JAVA"}}},"highlight": {"pre_tags": ["<font color='red'>"],"post_tags": ["<font/>"],"require_field_match": "false","fields": {"*":{}}},"sort": [{"age": {"order": "desc"}}]}*///第一步:构建查询语句Query query = new StringQuery("{\n" +"        \"match\": {\n" +"          \"remark\": {\n" +"            \"query\": \"JAVA\"\n" +"          }\n" +"        }\n" +"      }");// 分页:通过 PageRequest.of(0, 3) 配置页码和每页条数query.setPageable(PageRequest.of(0, 3));// 排序:通过 Sort.by(Order.desc("age")) 按年龄倒序query.addSort(Sort.by(Order.desc("age")));// 高亮:通过 HighlightParameters 配置高亮标签(如 <font color='red'>)和作用字段,让匹配结果更直观HighlightField highlightField = new HighlightField("*");HighlightParameters highlightParameters = new HighlightParameters.HighlightParametersBuilder().withPreTags("<font color='red'>").withPostTags("<font/>").withRequireFieldMatch(false).build();Highlight highlight = new Highlight(highlightParameters,Arrays.asList(highlightField));HighlightQuery highlightQuery = new HighlightQuery(highlight,Employee.class);query.setHighlightQuery(highlightQuery);SearchHits<Employee> search = elasticsearchTemplate.search(query, Employee.class);List<SearchHit<Employee>> searchHits = search.getSearchHits();for (SearchHit hit: searchHits){log.info("返回结果:"+hit.toString());}
}
2.4.2.8 布尔查询
@Autowired
ElasticsearchTemplate elasticsearchTemplate;@Test
public void testBoolQueryDocument(){// 实现 bool 查询(多条件组合),查询地址含 “广州” 且 remark 含 “java” 的员工// must 子句表示“必须同时满足”,同样支持 StringQuery 和 NativeQuery 两种构建方式,满足多条件组合的复杂业务查询场景/*GET /employee/_search{"query": {"bool": {"must": [{"match": {"address": "广州"}},{"match": {"remark": "java"}}]}}}*/// 方式1:直接编写 JSON 格式的查询语句//        Query query = new StringQuery("{\n" +//                "            \"bool\": {\n" +//                "              \"must\": [\n" +//                "                {\n" +//                "                  \"match\": {\n" +//                "                    \"address\": \"广州\"\n" +//                "                  }\n" +//                "                },{\n" +//                "                  \"match\": {\n" +//                "                    \"remark\": \"java\"\n" +//                "                  }\n" +//                "                }\n" +//                "              ]\n" +//                "            }\n" +//                "          }");// 方式2:通过编程式 API 构建查询,可读性更强Query query = NativeQuery.builder().withQuery(q -> q.bool(m -> m.must(QueryBuilders.match( q1 -> q1.field("address").query("广州")),QueryBuilders.match( q2 -> q2.field("remark").query("java"))))).build();SearchHits<Employee> search = elasticsearchTemplate.search(query, Employee.class);List<SearchHit<Employee>> searchHits = search.getSearchHits();for (SearchHit hit: searchHits){log.info("返回结果:"+hit.toString());}
}

2.4.3 使用ElasticsearchClient

2.4.3.1 概述
  • ElasticsearchClient 是 Elasticsearch 7.16+ 版本推出的官方推荐客户端,替代了 deprecated 的 RestHighLevelClient,提供类型安全的 API 设计,支持链式调用构建请求,更贴合 Elasticsearch 的 DSL 语法;

    @Autowired
    ElasticsearchTemplate elasticsearchTemplate;
    
2.4.3.2 索引操作
@Autowired
ElasticsearchClient elasticsearchClient;String indexName = "employee_demo";@Test
public void testCreateIndex() throws IOException {// 检查索引是否存在BooleanResponse exist = elasticsearchClient.indices().exists(e->e.index(indexName));// 若索引存在,则删除if(exist.value()){elasticsearchClient.indices().delete(d->d.index(indexName));}// 创建索引elasticsearchClient.indices().create(c->c.index(indexName) // 指定索引名// 配置索引的分片数和副本数.settings(s->s.numberOfShards("1").numberOfReplicas("1"))// name 字段为 keyword 类型(不分词,适合精确匹配).mappings(m-> m.properties("name", p->p.keyword(k->k))// sex 字段为 long 类型(整数存储).properties("sex", p->p.long_(l->l))// address字段为text 类型,使用ik_max_word中文分词器(最大化分词,适合全文检索).properties("address", p->p.text(t->t.analyzer("ik_max_word")))));// 查询索引GetIndexResponse getIndexResponse = elasticsearchClient.indices().get(g -> g.index(indexName));log.info(getIndexResponse.result().toString());
}
2.4.3.3 批量插入文档
// 使用 BulkOperation 封装多条文档操作,通过 elasticsearchClient.bulk(...) 一次性提交,提升大量数据写入效率
@Autowired
ElasticsearchClient elasticsearchClient;String indexName = "employee_demo";@Test
public void testBulkBatchInsert() throws IOException {// 构建 Employee 实体列表(含 id、name、sex 等字段)List<Employee> employees = new ArrayList<>();employees.add(new Employee(2L,"张三",1,25,"广州天河公园","java developer"));employees.add(new Employee(3L,"李四",1,28,"广州荔湾大厦","java assistant"));employees.add(new Employee(4L,"小红",0,26,"广州白云山公园","php developer"));// 为每个实体创建 BulkOperation:// 通过 BulkOperation.Builder().create(...) 定义 “创建文档” 操作// 指定文档 ID 和实体对象(document(employee) 自动序列化实体为 JSONList<IndexQuery> bulkInsert = new ArrayList<>();for (Employee employee : employees) {IndexQuery indexQuery = new IndexQuery();indexQuery.setId(String.valueOf(employee.getId()));String json = JSONObject.toJSONString(employee);indexQuery.setSource(json);bulkInsert.add(indexQuery);}List<BulkOperation> list = new ArrayList<>();for (Employee employee : employees) {BulkOperation bulkOperation = new BulkOperation.Builder().create(c->c.id(String.valueOf(employee.getId())).document(employee)).build();list.add(bulkOperation);}// 执行批量插入,operations(list) 传入批量操作列表。elasticsearchClient.bulk(b->b.index(indexName).operations(list));
}
2.4.3.4 单文档插入
@Autowired
ElasticsearchClient elasticsearchClient;String indexName = "employee_demo";@Test
public void testDocument() throws IOException {Employee employee = new Employee(12L,"张三三",1,25,"广州天河公园","java developer");// 通过 IndexRequest 构建插入请求,指定索引名、文档 ID 和实体对象,调用 elasticsearchClient.index(request) 执行插入IndexRequest<Employee> request = // 链式配置请求参数,document(employee) 自动将实体序列化为 Elasticsearch 文档IndexRequest.of(i -> i.index(indexName).id(employee.getId().toString()).document(employee));IndexResponse response = elasticsearchClient.index(request);log.info("response:"+response);
}
2.4.3.5 单条件查询
@Autowired
ElasticsearchClient elasticsearchClient;String indexName = "employee_demo";@Test
public void testQuery() throws IOException {// 构建 SearchRequest:通过 SearchRequest.of(s -> s.index(indexName).query(...)) 配置查询索引和查询条件SearchRequest searchRequest = SearchRequest.of(s -> s.index(indexName)// 定义查询逻辑:对 name 字段执行 match 匹配(因 name 是 keyword 类型,实际等价于精确匹配).query(q -> q.match(m -> m.field("name").query("张三三"))));log.info("构建的DSL语句:"+ searchRequest.toString());SearchResponse<Employee> searchResponse = elasticsearchClient.search(searchRequest, Employee.class);// 通过 hits().hits() 获取命中的文档列表,Hit::source 提取文档源数据(自动反序列化为 Employee 对象)List<Hit<Employee>> hits = searchResponse.hits().hits();hits.stream().map(Hit::source).forEach(employee -> {log.info("员工信息:"+employee);});
}
2.4.3.6 布尔组合查询
  • 实现 bool 多条件查询:查询 address 含“广州”且 remark 含“java”的文档,对应 Elasticsearch 的 bool-must 逻辑(所有条件必须满足)。
  • 步骤解析:
    1. 构建 BoolQuery:通过 BoolQuery.Builder() 创建布尔查询构建器,调用 must(...) 方法添加子查询(每个 must 对应一个条件):
      • must(m->m.match(q->q.field("address").query("广州")))address 字段匹配“广州”。
      • must(m->m.match(q->q.field("remark").query("java")))remark 字段匹配“java”。
    2. 构建 SearchRequest:将布尔查询设置为查询条件(query(q->q.bool(boolQueryBuilder.build())))。
    3. 执行查询并解析:同单条件查询,提取 hit.source() 得到匹配的 Employee 实体。
@Autowired
ElasticsearchClient elasticsearchClient;@Test
public void testBoolQueryDocument() throws IOException {// 条件查询:查询 address 含 “广州” 且 remark 含 “java” 的文档,对应 Elasticsearch 的 bool-must 逻辑(所有条件必须满足)/*GET /employee/_search{"query": {"bool": {"must": [{"match": {"address": "广州"}},{"match": {"remark": "java"}}]}}}*/// 构建 BoolQuery:通过 BoolQuery.Builder() 创建布尔查询构建器,调用 must(...) 方法添加子查询(每个 must 对应一个条件)BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();boolQueryBuilder.must(m->m.match(q->q.field("address").query("广州"))) // address 字段匹配 “广州”.must(m->m.match(q->q.field("remark").query("java"))); // remark 字段匹配 “java”// 构建 SearchRequestSearchRequest searchRequest = new SearchRequest.Builder().index("employee").query(q->q.bool(boolQueryBuilder.build())) // 将布尔查询设置为查询条件.build();// 执行查询SearchResponse<Employee> searchResponse = elasticsearchClient.search(searchRequest, Employee.class);// 解析List<Hit<Employee>> list = searchResponse.hits().hits();for(Hit<Employee> hit: list){log.info(String.valueOf(hit.source()));}
}
http://www.dtcms.com/a/588805.html

相关文章:

  • 18_FastMCP 2.x 中文文档之FastMCP服务端高级功能:后端存储详解
  • 基于Spring Boot的社团服务系统的设计与实现
  • Spring Boot配置文件加载顺序详解(含Nacos配置中心机制)
  • 基于React+Flask前后端分离的文件搜索系统
  • K8s 集群部署中间件 - yaml 版本(二)
  • zmaiFy音频转录介绍
  • 学校资源网站建设目标关于做电商网站导流项目
  • 【论文阅读与项目复现】Hypothesis Generation with Large Language Models
  • win7下asp.net网站发布软件开发文档编写
  • socket编程——使用UDP实现的一个回显功能
  • 侠客行・iOS 26 Liquid Glass TabBar 破阵记
  • G882磁力仪方向调整
  • 站长友情链接网上卖货的平台有哪些
  • 弱函数:嵌入式回调的最佳实践
  • 如何在实验室服务器上搭建python虚拟环境?安装conda并配置虚拟环境
  • 【开发者导航】轻量可微调且开源的大语言模型家族:LLaMA
  • 北京网站建立公司创意包装设计网站
  • INSERT INTO … SELECT … 常见问答(含样例)
  • 做图素材的网站有哪些昆明做网站公司有哪些
  • 移动端网站定制搞笑网站模板
  • 网站后台的数据库怎么做工业产品设计要学什么
  • 你去湖北省住房城乡建设厅网站查软件开发好学吗
  • 北京手机网站设计公司益阳建设公司网站
  • 网站建设ip微信小程序 做网站
  • 单位网站建设的必要性网站如何被收录
  • 狗贩子怎么做网站卖狗成都网站建设餐饮
  • 如何开发网站自己做站长哪个网站可以做编程题
  • 深圳制作公司网站wordpress 显示微信二维码
  • 微信网站建设费记什么科目中山网站搭建
  • 有关小城镇建设的网站网站是怎么建设的