ElasticSearch8 - SpringBoot整合ElasticSearch

张春  金牌会员 | 2024-5-15 07:35:48 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 903|帖子 903|积分 2709

媒介

springboot 整合 ES 有两种方案,ES 官方提供的 Elasticsearch Java API Client 和 spring 提供的 [Spring Data Elasticsearch](Spring Data Elasticsearch)
两种方案各有优劣
Spring:高度封装,用着舒服。缺点是更新不及时,有可能无法使用 ES 的新 API
ES 官方:更新及时,灵活,缺点是太灵活了,基本是一比一复制 REST APIs,项目中使用需要二次封装。
Elasticsearch Java API Client

目前最新版本 ES8.12,要求 jdk8 以上,API 里面使用了大量的 builder 和 lambda
官方也提供了 测试用例
兼容

翻了不少博客,大部分都是使用 High Level Rest Client,这是旧版本的 api,新版本使用 Elasticsearch Java API Client,如何兼容旧版本,官方也提供了解决方案)
下文描述的均是新版 API
添加 jar 包

官方文档:[installation](安装| Elasticsearch Java API 客户端 [8.12] |松紧带 --- Installation | Elasticsearch Java API Client [8.12] | Elastic)
使用的是 maven,在 pom.xml 中添加
  1. <dependency>  
  2.     <groupId>co.elastic.clients</groupId>  
  3.     <artifactId>elasticsearch-java</artifactId>  
  4.     <version>8.12.2</version>  
  5. </dependency>
  6. <dependency>  
  7.     <groupId>com.fasterxml.jackson.core</groupId>  
  8.     <artifactId>jackson-databind</artifactId>  
  9.     <version>2.12.3</version>  
  10. </dependency>
复制代码
假如报错 ClassNotFoundException: jakarta.json.spi.JsonProvider,则还需要添加
  1. <dependency>
  2.     <groupId>jakarta.json</groupId>
  3.     <artifactId>jakarta.json-api</artifactId>
  4.     <version>2.0.1</version>
  5. </dependency>
复制代码
打印请求

在 application. yml 中添加配置,打印 es 的 http 请求(建议在开辟调试时使用)
  1. logging:  
  2.   level:  
  3.     tracer: TRACE
复制代码
连接 ES

配置文件如下,后续全部 ES 操作都通过 ElasticsearchClient 对象
更多配置请看 Common configuration
  1. @Configuration  
  2. public class ElasticSearchConfig {  
  3.   
  4.     @Bean  
  5.     public ElasticsearchClient esClient() {  
  6.         // ES服务器URL  
  7.         String serverUrl = "http://127.0.0.1:9200";  
  8.         // ES用户名和密码  
  9.         String userName = "xxx";  
  10.         String password = "xxx";  
  11.   
  12.         BasicCredentialsProvider credsProv = new BasicCredentialsProvider();  
  13.         credsProv.setCredentials(  
  14.                 AuthScope.ANY, new UsernamePasswordCredentials(userName, password)  
  15.         );  
  16.   
  17.         RestClient restClient = RestClient  
  18.                 .builder(HttpHost.create(serverUrl))  
  19.                 .setHttpClientConfigCallback(hc -> hc.setDefaultCredentialsProvider(credsProv))  
  20.                 .build();  
  21.   
  22.         ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());  
  23.         return new ElasticsearchClient(transport);  
  24.     }  
  25.   
  26. }
复制代码
索引操作

代码中的 esClient 就是 ElasticsearchClient,请自行注入 bean
  1. // 索引名字  
  2. String indexName = "student";  
  3.   
  4. // 索引是否存在  
  5. BooleanResponse books = esClient.indices().exists(e -> e.index(indexName));  
  6. System.out.println("索引是否存在:" + books.value());  
  7.   
  8. // 创建索引  
  9. esClient.indices().create(c -> c  
  10.         .index(indexName)  
  11.         .mappings(mappings -> mappings  // 映射  
  12.                 .properties("name", p -> p  
  13.                         .text(t -> t // text类型,index=false  
  14.                                 .index(false)  
  15.                         )  
  16.                 )  
  17.                 .properties("age", p -> p  
  18.                         .long_(t -> t) // long类型  
  19.                 )  
  20.         )  
  21. );  
  22.   
  23. // 删除索引  
  24. esClient.indices().delete(d -> d.index(indexName));
复制代码
文档操作 (CRUD)

下文以官方测试数据 account. json 为例
实体类

起首定义实体类,用于 ES 中的字段
  1. public class Account {
  2.         private String id;
  3.         // 解决ES中字段与实体类字段不一致的问题  
  4.         @JsonProperty("account_number")
  5.     private Long account_number;  
  6.     private String address;  
  7.     private Integer age;  
  8.     private Long balance;  
  9.     private String city;  
  10.     private String email;  
  11.     private String employer;  
  12.     private String firstname;  
  13.     private String lastname;  
  14.     private String gender;  
  15.     private String state;  
  16.         ... 省略get、set方法
  17. }
复制代码
新增
  1. String indexName = "account";  // 索引名字
  2. Account account = new Account();
  3. account.setId("1");
  4. account.setLastname("guyu");   
  5. // 新增
  6. CreateResponse createResponse = esClient.create(c -> c  
  7.         .index(indexName) // 索引名字  
  8.         .id(account.getId()) // id  
  9.         .document(account) // 实体类  
  10. );
复制代码
修改
  1. UpdateResponse<Account> updateResp = esClient.update(u -> u  
  2.                 .index(indexName)  
  3.                 .id(account.getId())  
  4.                 .doc(account),  
  5.         Account.class  
  6. );
复制代码
删除
  1. DeleteResponse deleteResp = esClient.delete(d -> d.index(indexName).id("1"));
复制代码
批量新增

批量操作需要使用到 bulk
  1. List<Account> accountList = ...
  2. BulkRequest.Builder br = new BulkRequest.Builder();  
  3. for (Account acc : accountList) {  
  4.     br.operations(op -> op  
  5.             .create(c -> c  
  6.                     .index(indexName)  
  7.                     .id(acc.getId())  
  8.                     .document(acc)  
  9.             )  
  10.     );  
  11. }  
  12. BulkResponse bulkResp = esClient.bulk(br.build());
复制代码
有没有觉得批量新增的 .create () 里面的参数很眼熟,批量删除和更新请举一反三
根据 id 查询
  1. // 定义实体类
  2. GetResponse<Account> getResp = esClient.get(g -> g.index(indexName).id("1"), Account.class);  
  3. if (getResp.found()) {  
  4.     Account source = getResp.source();  // 这就是得到的实体类
  5.     source.setId(getResp.id());  
  6. }
  7. // 不定义实体类
  8. GetResponse<ObjectNode> getResp = esClient.get(g -> g  
  9.                 .index(indexName)  
  10.                 .id("1"),  
  11.         ObjectNode.class  
  12. );  
  13. if (getResp.found()) {  
  14.     ObjectNode json = getResp.source();  
  15.     String firstname = json.get("firstname").asText();  
  16.     System.out.println(firstname);  
  17. }
复制代码
搜索

搜索全部
  1. SearchResponse<Account> searchResp = esClient.search(s -> s  
  2.         .index(indexName)  
  3.         .query(q -> q.matchAll(m -> m))  // 搜索全部
  4.         , Account.class  
  5. );
  6. HitsMetadata<Account> hits = searchResp.hits();  
  7. long totalValue = hits.total().value(); // 匹配到的数量  
  8. hits.hits().forEach(h -> {  
  9.     Account acc = h.source(); // 这就是得到的实体类  
  10.     acc.setId(h.id());  
  11. });
复制代码
ES API 的对象定义,基本与返回的 json 一一对应的,所以 SearchResponse 就不过多赘述。
搜索 firstname = Amber
  1. SearchResponse<Account> searchResp = esClient.search(s -> s  
  2.         .index(indexName)  
  3.         .query(q -> q  // 查询  
  4.                 .match(t -> t  
  5.                         .field("firstname")  
  6.                         .query("Amber")  
  7.                 )  
  8.         )  
  9.         , Account.class  
  10. );
  11. // 也可以这样写
  12. Query firstNameQuery = MatchQuery.of(m -> m.field("firstname").query("Amber"))._toQuery();
  13. SearchResponse<Account> searchResp = esClient.search(s -> s  
  14.         .index(indexName)  
  15.         .query(firstNameQuery)  
  16.         , Account.class  
  17. );
复制代码
嵌套查询,比如搜索  firstname = Amber AND age = 32
  1. Query firstNameQuery = MatchQuery.of(m -> m.field("firstname").query("Amber"))._toQuery();  
  2. Query ageQuery = MatchQuery.of(m -> m.field("age").query(32))._toQuery();  
  3.   
  4. SearchResponse<Account> searchResp = esClient.search(s -> s  
  5.         .index(indexName)  
  6.         .query(q -> q  
  7.                 .bool(b -> b.must(firstNameQuery, ageQuery))  
  8.         )  
  9.         , Account.class  
  10. );
复制代码
浅分页

from 和 size 参数类似于 mysql 的 limit,详细阐明见 Paginate search results
  1. SearchResponse<Account> searchResp = esClient.search(s -> s  
  2.         .index(indexName)   
  3.         .from(0)  // 分页参数
  4.                 .size(20)  // 分页参数
  5.         , Account.class  
  6. );
复制代码
排序
  1. SearchResponse<Account> searchResp = esClient.search(s -> s  
  2.         .index(indexName)   
  3.         .sort(so -> so  // 排序字段1
  4.                 .field(f -> f  
  5.                         .field("age")  
  6.                         .order(SortOrder.Asc)  
  7.                 )  
  8.         )  
  9.         .sort(so -> so  // 排序字段2
  10.                 .field(f -> f  
  11.                         .field("account_number")  
  12.                         .order(SortOrder.Desc)  
  13.                 )  
  14.         )  
  15.         , Account.class  
  16. );
复制代码
Spring Data Elasticsearch

文档: Spring Data Elasticsearch
添加 jar 和配置

pom.xml添加依赖
  1. <dependency>
  2.   <groupId>org.springframework.boot</groupId>
  3.   <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  4. </dependency>
复制代码
yml 配置
  1. spring:  
  2.   elasticsearch:  
  3.     uris: http://xxx:9200  
  4.     username: xxx  
  5.     password: xxx
  6. logging:  
  7.   level:  
  8.   # 输出es的查询参数(调试用)
  9.     tracer: TRACE  
复制代码
索引操作

实体类
  1. @Data  
  2. @Document(indexName = "account")  
  3. public class Account {  
  4.   
  5.     @Id  
  6.     private String id;  
  7.     // 解决ES中字段与实体类字段不一致的问题  
  8.     @Field(name = "account_number", type = FieldType.Long)  
  9.     private Long accountNumber;  
  10.     @Field(type = FieldType.Text)  
  11.     private String address;  
  12.     @Field(type = FieldType.Integer)  
  13.     private Integer age;  
  14.     @Field(type = FieldType.Long)  
  15.     private Long balance;  
  16.     @Field(type = FieldType.Text)  
  17.     private String city;  
  18.     @Field(type = FieldType.Text)  
  19.     private String email;  
  20.     @Field(type = FieldType.Text)  
  21.     private String employer;  
  22.     @Field(type = FieldType.Text)  
  23.     private String firstname;  
  24.     @Field(type = FieldType.Text)  
  25.     private String lastname;  
  26.     @Field(type = FieldType.Text)  
  27.     private String gender;  
  28.     @Field(type = FieldType.Text)  
  29.     private String state;  
  30.         ... 省略get、set 方法
  31. }
复制代码
  1. IndexOperations idxOpt = template.indexOps(Account.class);  
  2. // 索引是否存在  
  3. boolean idxExist = idxOpt.exists();  
  4.   
  5. // 创建索引  
  6. boolean createSuccess = idxOpt.createWithMapping();  
  7. System.out.println(createSuccess);  
  8.   
  9. // 删除索引  
  10. boolean deleted = idxOpt.delete();
复制代码
文档操作(CRUD)
  1. Account account = new Account();  
  2. account.setId("1");  
  3. account.setLastname("guyu");  
  4.   
  5. // 这是插入或覆盖,如果id存在了就是覆盖  
  6. template.save(account);  
  7.   
  8. // 修改,用的是es的_update  
  9. template.update(account);
  10. // 删除  
  11. template.delete(account)
  12. // 批量新增(用的是es的_bulk)
  13. List<Account> accountList = ...
  14. template.save(accountList);
  15. // 根据id查询
  16. Account account = template.get("1", Account.class);
复制代码
搜索 + 排序 + 分页
  1. // 搜索 firstname = Amber AND age = 32
  2. Criteria criteria = new Criteria();  
  3. criteria.and(new Criteria("firstname").is("Amber"));  
  4. criteria.and(new Criteria("age").is(32));  
  5.   
  6. // 分页  
  7. int pageNum = 1; // 页码  
  8. int pageSize = 20; // 每页数量  
  9. Query query = new CriteriaQueryBuilder(criteria)  
  10.         .withSort(Sort.by(new Order(Sort.Direction.ASC, "age"))) // 排序字段1  
  11.         .withSort(Sort.by(new Order(Sort.Direction.DESC, "balance"))) // 排序字段1  
  12.         .withPageable(PageRequest.of(pageNum - 1, pageSize)) // 浅分页  
  13.         // 不需要查询的字段
  14.         .withSourceFilter(new FetchSourceFilterBuilder().withExcludes("email", "address").build())
  15.         .build();  
  16.   
  17. SearchHits<Account> searchHits = template.search(query, Account.class);  
  18. long totalValue = searchHits.getTotalHits(); // 匹配到的数量  
  19. for (SearchHit<Account> searchHit : searchHits.getSearchHits()) {  
  20.     Account account = searchHit.getContent(); // 这就是得到的实体类  
  21. }
复制代码
总结

本文先容了 SpringBoot 整合 ElasticSearch 的两种方案,但均只是简单提及,更详细的用法需要自行查看官方文档。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

张春

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表