SpringBoot2.7.0整合Elastic Search7.17.3及常规应用
我们基于一个发表文章的案例来说明SpringBoot如何elasticsearch集成。elasticsearch本身可以是一个独立的服务,也可以嵌入我们的web应用中,在本案例中,我们讲解如何将elasticsearch嵌入我们的应用中。
Elasticsearch官网参考文档:https://www.elastic.co/guide/index.html
Elasticsearch官方下载地址:https://www.elastic.co/cn/downloads/elasticsearch
不管你有没有ES,最好是没有,因为一定要知道一点,一定要去官网查一下你当前用的spring boot data es的版本是不是和你自己ES服务器所匹配的,这一点简直是天坑,spring boot提供的es封装API对es的版本要求相当苛刻,对不上就用不了,很多人折在版本问题,奉劝大家一句,除非正式的项目开发上,团队会给你提供需要版本的jar,正式开发本身版本都是经过架构师仔细考虑并且版本方面问题都解决了,自己学习自己开发,你最好不要再jar上做坚持,直接去改你的es服务版本,也不要轻信网上说的改pom版本,会连带着出很多不必要的问题,官网如下https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#preface.requirements
比如,我在写这篇博文的时候spring boot沿用的前面知识点项目,spring boot版本是2.7.0,自动导入的spring boot data es版本是4.4.0,我的ES就要用7.17.3的,但是我本地先前用的es是6.6.2的,含泪重装。
创建SpringBoot项目并引入Elasticsearch依赖
本文使用的SpringBoot版本为2.7.0
对应的Elasticsearch版本为7.17.3
对应的spring-boot-starter-data-elasticsearch版本是4.4.0
引入Spring-Data-Elasticsearch依赖,Spring 团队将Elasticsearch归到“Data”的范畴,所以依赖是以Spring-Data开头。
二、整合SpringBoot与ElasticSearch
1、引入相应的依赖
pom.xml
<parent> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-parent </artifactId> <version> 1.3.0.RELEASE </version> </parent> <dependencies> <!-- 添加 web 应用的依赖 --> <dependency> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-web </artifactId> </dependency> <!-- 添加 spring-data-elasticsearch的依赖 --> <dependency> <groupId> org.springframework.boot </groupId> <artifactId> spring-boot-starter-data-elasticsearch </artifactId> </dependency> <dependency> <groupId> org.springframework.boot</groupId> <artifactId> spring-boot-starter-test </artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId&gjakarta.json</groupId> <artifactId>jakarta.json-api</artifactId> <version>2.0.1</version> <scope>compile</scope> </dependency> </dependencies>
2、修改配置文件
application.yml
spring: data: elasticsearch: acluster-name: #默认为elasticsearch acluster-nodes: #配置es节点信息,逗号分隔,如果没有指定,则启动ClientNode properties: path: logs: ./elasticsearch/log #elasticsearch日志存储目录 data: ./elasticsearch/data #elasticsearch数据存储目录
这些配置的属性,最终会设置到ElasticsearchProperties这个实体中。
新增链接类及返回类
package com.example.esapi.common; import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.transport.ElasticsearchTransport; import lombok.Data; import org.elasticsearch.client.RestClient; @Data public class ElasticSearchResult { private RestClient restClient; private ElasticsearchTransport elasticsearchTransport; private ElasticsearchClient elasticsearchClient; public ElasticSearchResult(RestClient restClient, ElasticsearchTransport elasticsearchTransport, ElasticsearchClient elasticsearchClient) { this.restClient = restClient; this.elasticsearchTransport = elasticsearchTransport; this.elasticsearchClient = elasticsearchClient; } }
package com.example.esapi.common; import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.rest_client.RestClientTransport; import org.apache.http.HttpHost; import org.elasticsearch.client.RestClient; import org.springframework.stereotype.Component; @Component public class ElasticSearchClientConnect { public ElasticSearchResult restClient(){ RestClient restClient = RestClient.builder( new HttpHost("localhost", 9200,"http")).build(); ElasticsearchTransport transport = new RestClientTransport( restClient, new JacksonJsonpMapper()); ElasticsearchClient client = new ElasticsearchClient(transport); ElasticSearchResult elasticSearchResult=new ElasticSearchResult(restClient,transport,client); return elasticSearchResult; } }
自此springboot集成elasticSearch已经完成,下边是应用操作。
elasticSearch的api应用测试(以上算是集成完成,下边是常规操作,亲测跑通)
新建User对象
package com.example.esapi.dto; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class User { private String name; private String sex; private Integer age; }
应用实例
package com.example.esapi.controller; import co.elastic.clients.elasticsearch._types.SortOrder; import co.elastic.clients.elasticsearch.core.*; import co.elastic.clients.elasticsearch.core.bulk.BulkOperation; import co.elastic.clients.elasticsearch.core.search.Hit; import co.elastic.clients.elasticsearch.core.search.HitsMetadata; import co.elastic.clients.elasticsearch.indices.CreateIndexResponse; import co.elastic.clients.elasticsearch.indices.DeleteIndexResponse; import co.elastic.clients.elasticsearch.indices.GetIndexResponse; import co.elastic.clients.json.JsonData; import com.example.esapi.common.ElasticSearchClientConnect; import com.example.esapi.dto.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController public class Testcontroller { @Autowired private ElasticSearchClientConnect elasticSearchClientConfig; /** * 新建jing_index索引 * @return * @throws IOException */ @GetMapping("/getConnect") public Boolean withdrawQueue() throws IOException { CreateIndexResponse createIndexResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().indices().create(c -> c.index("jing_index")); // 打印结果 System.out.println(createIndexResponse.acknowledged()); // 关闭连接 elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); return createIndexResponse.acknowledged(); } /** * 查询索引 * @throws IOException */ @GetMapping("/select") public void select() throws IOException { GetIndexResponse getIndexResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().indices().get(e -> e.index("jing_index")); // 打印结果 System.out.println("getIndexResponse.result() = " + getIndexResponse.result()); System.out.println("getIndexResponse.result().keySet() = " + getIndexResponse.result().keySet()); // 关闭连接 elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 删除索引 * @return * @throws IOException */ @GetMapping("/delete") public Boolean delete() throws IOException { // 删除索引 DeleteIndexResponse deleteIndexResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().indices().delete(e -> e.index("jing_index")); System.out.println("删除操作 = " + deleteIndexResponse.acknowledged()); // 关闭连接 elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); return deleteIndexResponse.acknowledged(); } /** * 添加document * @throws IOException */ @GetMapping("/addDocument") public void addDocument() throws IOException { // 向user对象中添加数据 User user = new User("java客户端", "男", 18); // 向索引中添加数据 CreateResponse createResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().create(e -> e.index("jing_index").id("1001").document(user)); System.out.println("createResponse.result() = " + createResponse.result()); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 查询document * @throws IOException */ @GetMapping("/queryDocument") public void queryDocument() throws IOException { // 构建请求 GetResponsegetResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().get(e -> e.index("jing_index").id("1001"), User.class); System.out.println("getResponse.source().toString() = " + getResponse.source().toString()); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 修改document * @throws IOException */ @GetMapping("/modifyDocument") public void modifyDocument() throws IOException { // 使用map集合封装需要修改的内容 Map map = new HashMap<>(); map.put("name", "java客户端aaa"); // 构建请求 UpdateResponse updateResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().update(e -> e.index("jing_index").id("1001").doc(map), User.class); System.out.println("updateResponse.result() = " + updateResponse.result()); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 删除document * @throws IOException */ @GetMapping("/removeDocument") public void removeDocument() throws IOException { // 构建请求 DeleteResponse deleteResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().delete(e -> e.index("jing_index").id("1001")); System.out.println("deleteResponse.result() = " + deleteResponse.result()); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 批量添加document * @throws IOException */ @GetMapping("/batchAddDocument") public void batchAddDocument() throws IOException { // 构建一个批量数据集合 List list = new ArrayList<>(); list.add(new BulkOperation.Builder().create( d -> d.document(new User("test2", "男", 19)).id("1002").index("user_test")).build()); list.add(new BulkOperation.Builder().create( d -> d.document(new User("test3", "男", 20)).id("1003").index("user_test")).build()); list.add(new BulkOperation.Builder().create( d -> d.document(new User("test4", "女", 21)).id("1004").index("user_test")).build()); // 调用bulk方法执行批量插入操作 BulkResponse bulkResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().bulk(e -> e.index("user_test").operations(list)); System.out.println("bulkResponse.items() = " + bulkResponse.items()); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 批量删除document * @throws IOException */ @GetMapping("/batchDeleteDocument") public void batchDeleteDocument() throws IOException { // 构建一个批量数据集合 List list = new ArrayList<>(); list.add(new BulkOperation.Builder().delete( d -> d.id("1002").index("user_test")).build()); list.add(new BulkOperation.Builder().delete( d -> d.id("1003").index("user_test")).build()); // 调用bulk方法执行批量插入操作 BulkResponse bulkResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().bulk(e -> e.index("user_test").operations(list)); System.out.println("bulkResponse.items() = " + bulkResponse.items()); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 全量查询document * @throws IOException */ @GetMapping("/queryAllDocument") public void queryAllDocument() throws IOException { // 全量查询 SearchResponse searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(e -> e.index("user_test").query(q -> q.matchAll(m -> m)), User.class); HitsMetadata hits = searchResponse.hits(); for (Hit hit : hits.hits()) { System.out.println("user = " + hit.source().toString()); } System.out.println("searchResponse.hits().total().value() = " + searchResponse.hits().total().value()); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 分页查询document * @throws IOException */ @GetMapping("/pagingQueryDocument") public void pagingQueryDocument() throws IOException { // 分页查询 SearchResponse searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search( s -> s.index("user_test") .query(q -> q.matchAll(m -> m)) .from(0) .size(2) , User.class); searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString())); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 排序查询document * @throws IOException */ @GetMapping("/sortQueryDocument") public void sortQueryDocument() throws IOException { // 排序查询 SearchResponse searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search( s -> s.index("user_test") .query(q -> q.matchAll(m -> m)) .sort(o -> o.field(f -> f.field("age").order(SortOrder.Asc))) , User.class); searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString())); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 条件查询document * @throws IOException */ @GetMapping("/conditionQueryDocument") public void conditionQueryDocument() throws IOException { // 条件查询 SearchResponse searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search( s -> s.index("user_test").query(q -> q.matchAll(m -> m)) .sort(o -> o.field(f -> f.field("age").order(SortOrder.Asc))) .source(r -> r.filter(f -> f.includes("name", "age").excludes(""))) , User.class); searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString())); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 组合查询 must是必须满足所有条件,should只要满足一个就行 * @throws IOException */ @GetMapping("/combinationQueryDocument") public void combinationQueryDocument() throws IOException { // 组合查询 SearchResponse searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search( s -> s.index("user_test").query(q -> q.bool(b -> b .must(m -> m.match(u -> u.field("age").query(21))) .must(m -> m.match(u -> u.field("sex").query("男"))) .mustNot(m -> m.match(u -> u.field("sex").query("女"))) )) , User.class); //SearchResponse searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search( // s -> s.index("user_test").query(q -> q.bool(b -> b // .should(h -> h.match(u -> u.field("age").query(19))) // .should(h -> h.match(u -> u.field("sex").query("男"))) // )) // , User.class); searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString())); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 范围查询 * @throws IOException */ @GetMapping("/scopeQueryDocument2") public void scopeQueryDocument2() throws IOException { // 范围查询,gte()表示取大于等于,gt()表示大于,lte()表示小于等于 SearchResponse searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(s -> s.index("user_test").query(q -> q .range(r -> r.field("age").gte(JsonData.of(20)).lt(JsonData.of(21)))) , User.class); searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString())); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 模糊查询 * @throws IOException */ @GetMapping("/fuzzyQueryDocument2") public void fuzzyQueryDocument2() throws IOException { // 模糊查询,fuzziness表示差几个可以查询出来 SearchResponse searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(s -> s.index("user_test").query(q -> q .fuzzy(f -> f.field("name").value("tst").fuzziness("2"))) , User.class); searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString())); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 高亮查询 * @throws IOException */ @GetMapping("/highlightQueryDocument2") public void highlightQueryDocument2() throws IOException { // 高亮查询 SearchResponse searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(s -> s.index("user_test").query(q -> q .term(t -> t.field("name").value("test4"))) .highlight(h -> h.fields("name", f -> f.preTags("").postTags(""))) , User.class); searchResponse.hits().hits().forEach(h -> System.out.println(h.source().toString())); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 聚合查询 * @throws IOException */ @GetMapping("/aggregateQueryDocument2") public void aggregateQueryDocument2() throws IOException { // 聚合查询,取最大年龄 SearchResponse searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(s -> s.index("user_test").aggregations("maxAge", a ->a.max(m -> m.field("age"))) , User.class); searchResponse.aggregations().entrySet().forEach(f -> System.out.println(f.getKey() + ":" + f.getValue().max().value())); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } /** * 分组查询 * @throws IOException */ @GetMapping("/groupQueryDocument2") public void groupQueryDocument2() throws IOException { // 分组查询 SearchResponse searchResponse = elasticSearchClientConfig.restClient().getElasticsearchClient().search(s -> s.index("user_test") .aggregations("ageGroup", a ->a.terms(t -> t.field("age"))) , User.class); searchResponse.aggregations().get("ageGroup").lterms().buckets().array().forEach(f -> System.out.println(f.key() + ":" + f.docCount())); elasticSearchClientConfig.restClient().getElasticsearchTransport().close(); elasticSearchClientConfig.restClient().getRestClient().close(); } }