【Elasticsearch 中心件】Elasticsearch 客户端使用案例

瑞星  金牌会员 | 2024-12-12 08:58:51 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 852|帖子 852|积分 2556

完备案例代码:https://github.com/idealzouhu/java-demos/tree/main/middleware-demos/spring-boot-elasticsearch/elasticsearch-client
  一、安装 Elasticsearch

1.1 启动 Elasticsearch

运行以下命令启动 Elasticsearch :
  1. $ docker network create elastic
  2. $ docker pull docker.elastic.co/elasticsearch/elasticsearch:7.17.26
  3. $ docker run --name es01-test ^
  4.   --net elastic ^
  5.   -p 127.0.0.1:9200:9200 ^
  6.   -p 127.0.0.1:9300:9300 ^
  7.   -e "discovery.type=single-node" ^
  8.   -e "xpack.security.enabled=true" ^
  9.   -e "ELASTIC_PASSWORD=Elastic@123456" ^
  10.   -e "KIBANA_PASSWORD=Kibana@123456" ^
  11.   docker.elastic.co/elasticsearch/elasticsearch:7.17.26
复制代码
其中,具体参数的寄义如下:


  • -e "xpack.security.enabled=true":启用 Elasticsearch 的安全功能。具体细节检察 Set up minimal security for Elasticsearch
  • -e "ELASTIC_PASSWORD=Elastic@123456":为 elastic 用户设置暗码。 elastic 用户拥有超等权限。
  • -e "KIBANA_PASSWORD=Kibana@123456":为 kibana_system 用户设置暗码。kibana_system 用户是一个为 Kibana 服务账户 创建的专用用户。它有足够的权限来与 Elasticsearch 通信,但它并没有 elastic 用户的超高权限。
1.2 启动 Kibana

Kibana 重要用来可视化和管理Elasticsearch数据。
运行以下命令启动 Kibana
  1. $ docker pull docker.elastic.co/kibana/kibana:7.17.26
  2. $ docker run --name kib01-test ^
  3.   --net elastic ^
  4.   -p 127.0.0.1:5601:5601 ^
  5.   -e "ELASTICSEARCH_HOSTS=http://es01-test:9200" ^
  6.   -e "ELASTICSEARCH_USERNAME=elastic" ^
  7.   -e "ELASTICSEARCH_PASSWORD=Elastic@123456" ^
  8.   docker.elastic.co/kibana/kibana:7.17.26
复制代码
其中,具体参数的寄义如下:


  • ELASTICSEARCH_HOSTS=http://es01-test:9200: 指定了 Kibana 应该毗连到哪个 Elasticsearch 节点。
  • ELASTICSEARCH_USERNAME=elastic: 指定 Kibana 使用的用户名是 elastic。
  • ELASTICSEARCH_PASSWORD=Elastic@123456: elastic 用户的暗码。
   留意,我们也可以直接在 HTTP 请求里面设置用户名和参数,比方 curl -u elastic:your_elastic_password http://localhost:9200
  在启动 Kibana 后,访问 Kibana 可视化界面 http://localhost:5601。
二、客户端代码

2.1 导入依赖

创建 Spring Boot 3.0.6 项目,导入相关依赖:
  1. <dependency>
  2.     <groupId>co.elastic.clients</groupId>
  3.     <artifactId>elasticsearch-java</artifactId>
  4.     <version>7.17.26</version>
  5.     <exclusions>
  6.         <exclusion>
  7.             <groupId>org.elasticsearch.client</groupId>
  8.             <artifactId>elasticsearch-rest-client</artifactId>
  9.         </exclusion>
  10.     </exclusions>
  11. </dependency>
  12. <dependency>
  13.     <groupId>org.elasticsearch.client</groupId>
  14.     <artifactId>elasticsearch-rest-client</artifactId>
  15.     <version>7.17.26</version>
  16. </dependency>
  17. <dependency>
  18.     <groupId>com.fasterxml.jackson.core</groupId>
  19.     <artifactId>jackson-databind</artifactId>
  20.     <version>2.17.0</version>
  21. </dependency>
复制代码
如果遇到相关依赖辩说,可以检察 Installation | Elasticsearch Java API Client 7.17
2.2 设置 application.yaml

  1. spring:
  2.   application:
  3.     name: elasticsearch-client
  4. # 打印 es 的 http 请求,适合在开发调试中使用,正式环境请关闭
  5. logging:
  6.   level:
  7.     tracer: TRACE
复制代码
2.3 界说实体类

  1. @Data
  2. @AllArgsConstructor
  3. @RequiredArgsConstructor
  4. public class Account {
  5.     // ES中 ducument 的 _id
  6.     private String id;
  7.     // 解决ES中字段与实体类字段不一致的问题
  8.     @JsonProperty("account_number")
  9.     private Long accountNumber;
  10.     private String address;
  11.     private Integer age;
  12.     private Long balance;
  13.     private String city;
  14.     private String email;
  15.     private String employer;
  16.     private String firstname;
  17.     private String lastname;
  18.     private String gender;
  19.     private String state;
  20. }
复制代码
2.4 毗连 Elasticserach

  1. @Configuration
  2. public class ElasticsearchConfig {
  3.     /**
  4.      * 创建并返回一个 Elasticsearch 客户端。
  5.      *
  6.      * <p>
  7.      *     使用 RestClientBuilder 构建 RestClient,并使用 BasicCredentialsProvider 设置用户名和密码。
  8.      *     <a href="https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/7.17/connecting.html">官方客户端连接教程</a>
  9.      *     <a href="https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/7.17/_basic_authentication.html">官方基本认证教程</a>
  10.      * </p>
  11.      *
  12.      * @return 初始化好的 Elasticsearch 客户端
  13.      */
  14.     @Bean
  15.     public ElasticsearchClient esClient() {
  16.         // 1. 配置认证
  17.         String userName = "elastic";
  18.         String password = "Elastic@123456";
  19.         final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
  20.         credentialsProvider.setCredentials(
  21.                 AuthScope.ANY, new UsernamePasswordCredentials(userName, password)
  22.         );
  23.         // 2. 创建 low-level 客户端(使用身份验证)
  24.         RestClient restClient = RestClient.builder(
  25.                         new HttpHost("localhost", 9200))    // 指定 Elasticsearch 服务器的主机名和端口号
  26.                 .setHttpClientConfigCallback(httpClientBuilder ->
  27.                         httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
  28.                 )
  29.                 .build();
  30.         // 3. 创建传输对象,使用 Jackson 映射器将 Java 对象转换为 JSON 格式
  31.         ElasticsearchTransport transport = new RestClientTransport(
  32.                 restClient, new JacksonJsonpMapper());
  33.         // 4. 创建 API 客户端
  34.         return new ElasticsearchClient(transport);
  35.     }
  36. }
复制代码
2.5 界说 Service 层接口

  1. public interface AccountElasticsearchService {
  2.     /**
  3.      * 将 Account 对象索引到 Elasticsearch 中。
  4.      *
  5.      * @param account 需要索引的账户对象
  6.      * @throws IOException 当 Elasticsearch 请求失败时抛出
  7.      */
  8.     void indexAccount(Account account) throws IOException;
  9.     /**
  10.      * 批量将多个 Account 对象索引到 Elasticsearch 中。
  11.      *
  12.      * @param accounts 需要索引的账户对象列表
  13.      * @throws IOException 当 Elasticsearch 请求失败时抛出
  14.      */
  15.     public void indexMultipleAccounts(List<Account> accounts) throws IOException;
  16.     /**
  17.      * 根据账户编号搜索账户信息。
  18.      *
  19.      * @param accountNumber 账户编号
  20.      * @return 匹配的账户信息,以字符串形式返回
  21.      * @throws IOException 当 Elasticsearch 请求失败时抛出
  22.      */
  23.     String searchAccountByAccountNumber(Long accountNumber) throws IOException;
  24.     /**
  25.      * 根据账户 ID 从 Elasticsearch 获取账户信息。
  26.      *
  27.      * @param id 账户 ID
  28.      * @return 对应账户的详细信息
  29.      * @throws IOException 当 Elasticsearch 请求失败时抛出
  30.      */
  31.     String  searchAccountById(String id) throws IOException;
  32.     /**
  33.      * 根据 firstname 和 age 查询账户。
  34.      *
  35.      * @param firstName 姓名
  36.      * @param age 年龄
  37.      * @throws IOException 当 Elasticsearch 请求失败时抛出
  38.      */
  39.     public void searchByNameAndAge(String firstName, int age) throws IOException;
  40.     /**
  41.      * 更新账户信息。
  42.      *
  43.      * @param account 修改后的账户对象
  44.      * @return 返回更新结果信息
  45.      * @throws IOException 当 Elasticsearch 请求失败时抛出
  46.      */
  47.     String updateAccountById(Account account) throws IOException;
  48.     /**
  49.      * 根据账户 ID 从 Elasticsearch 中删除账户信息。
  50.      *
  51.      * @param accountId 要删除的账户 ID
  52.      * @return 返回删除结果信息
  53.      * @throws IOException 当 Elasticsearch 请求失败时抛出
  54.      */
  55.      String deleteAccountById(String accountId) throws IOException;
  56. }
复制代码
2.6 实现 Service 层功能

  1. @Slf4j
  2. @Service
  3. @RequiredArgsConstructor
  4. public class AccountElasticsearchServiceImpl implements AccountElasticsearchService {
  5.     private final ElasticsearchClient esClient;
  6.     /**
  7.      * 将 Account 对象索引到 Elasticsearch 中。
  8.      *
  9.      * @param account 需要索引的账户对象
  10.      * @throws IOException 当 Elasticsearch 请求失败时抛出
  11.      */
  12.     @Override
  13.     public void indexAccount(Account account) throws IOException {
  14.         // 创建索引请求并执行
  15.         IndexResponse createResponse = esClient.index(i -> i
  16.                 .index("accounts")  // 指定索引名称
  17.                 .id(account.getId())      // 文档 ID, 使用 account.getId() 获取文档的唯一标识
  18.                 .document(account)        // 文档内容
  19.         );
  20.         log.info("Document indexed with ID:{}", createResponse.id());
  21.     }
  22.     /**
  23.      * 批量将多个 Account 对象索引到 Elasticsearch 中。
  24.      *
  25.      * @param accounts 需要索引的账户对象列表
  26.      * @throws IOException 当 Elasticsearch 请求失败时抛出
  27.      */
  28.     @Override
  29.     public void indexMultipleAccounts(List<Account> accounts) throws IOException {
  30.         // 创建批量请求构建器
  31.         BulkRequest.Builder bulkRequest = new BulkRequest.Builder();
  32.         // 循环添加每个账户对象的索引请求
  33.         for (Account account : accounts) {
  34.             bulkRequest.operations(op -> op
  35.                     .index(idx -> idx
  36.                             .index("accounts")
  37.                             .id(account.getId())
  38.                             .document(account)
  39.                     )
  40.             );
  41.         }
  42.         // 执行批量请求
  43.         BulkResponse bulkResponse = esClient.bulk(bulkRequest.build());
  44.         // 打印出成功的文档 ID
  45.         if (bulkResponse.errors()) {
  46.             log.error("Bulk had errors");
  47.             for (BulkResponseItem  item : bulkResponse.items()) {
  48.                 if (item.error() != null) {
  49.                     log.error(item.error().reason());
  50.                 }
  51.             }
  52.         } else {
  53.             log.info("Bulk indexing completed successfully");
  54.         }
  55.     }
  56.    
  57.     /**
  58.      * 根据账户编号搜索账户信息。
  59.      *
  60.      * @param accountNumber 账户编号
  61.      * @return 匹配的账户信息,以字符串形式返回
  62.      * @throws IOException 当 Elasticsearch 请求失败时抛出
  63.      */
  64.     @Override
  65.     public String searchAccountByAccountNumber(Long accountNumber) throws IOException {
  66.         // 使用 Elasticsearch 客户端进行搜索
  67.         SearchResponse<Account> searchResponse = esClient.search(s -> s
  68.                         .index("accounts")  // 指定索引名称
  69.                         .query(q -> q
  70.                                 .term(t -> t
  71.                                         .field("account_number")  // 搜索字段
  72.                                         .value(v -> v.longValue(accountNumber))  // 动态传入搜索值
  73.                                 )),
  74.                 Account.class);
  75.         // 获取总命中数
  76.         TotalHits totalHits = searchResponse.hits().total();
  77.         boolean isExactResult = totalHits.relation() == TotalHitsRelation.Eq;
  78.         if (isExactResult) {
  79.             log.info("Found exactly " + totalHits.value() + " matching account(s).");
  80.         } else {
  81.             log.info("Found more than " + totalHits.value() + " matching account(s).");
  82.         }
  83.         // 构建返回结果
  84.         StringBuilder resultBuilder = new StringBuilder("Search results:\n");
  85.         for (Hit<Account> hit : searchResponse.hits().hits()) {
  86.             Account foundAccount = hit.source();  // 获取匹配到的账户信息
  87.             if (foundAccount != null) {
  88.                 resultBuilder.append(String.format(
  89.                         "Account: %s, Name: %s %s\n",
  90.                         foundAccount.getAccountNumber(),
  91.                         foundAccount.getFirstname(),
  92.                         foundAccount.getLastname()));
  93.             }
  94.         }
  95.         // 返回结果
  96.         if (resultBuilder.length() == 0) {
  97.             return "No matching accounts found for account number: " + accountNumber;
  98.         }
  99.         return resultBuilder.toString();
  100.     }
  101.     /**
  102.      * 根据账户 ID 从 Elasticsearch 获取账户信息。
  103.      *
  104.      * @param id 账户 ID, 也是文档的唯一标识
  105.      * @return 对应账户的详细信息
  106.      * @throws IOException 当 Elasticsearch 请求失败时抛出
  107.      */
  108.     @Override
  109.     public String searchAccountById(String id) throws IOException {
  110.         // 创建查询请求
  111.         GetResponse<Account> response = esClient.get(g -> g
  112.                         .index("accounts")
  113.                         .id(id),
  114.                 Account.class
  115.         );
  116.         // 判断是否找到对应的账户
  117.         if (response.found()) {
  118.             // 获取账户信息
  119.             Account account = response.source();
  120.             // 将账户信息转换为 JSON 字符串并返回
  121.             ObjectMapper mapper = new ObjectMapper();
  122.             return mapper.writeValueAsString(account);
  123.         }else {
  124.             return "Account not found";
  125.         }
  126.     }
  127.     /**
  128.      * 根据 firstname 和 age 查询账户。
  129.      *
  130.      * @param firstName 姓名
  131.      * @param age       年龄
  132.      * @throws IOException 当 Elasticsearch 请求失败时抛出
  133.      */
  134.     @Override
  135.     public void searchByNameAndAge(String firstName, int age) throws IOException {
  136.         // 创建查询条件
  137.         Query firstNameQuery = MatchQuery.of(m -> m.field("firstname").query(firstName))._toQuery();
  138.         Query  ageQuery = MatchQuery.of(m -> m.field("age").query(age))._toQuery();
  139.         // 使用 bool 查询将多个条件组合起来
  140.         BoolQuery boolQuery = BoolQuery.of(
  141.                 b -> b.must(firstNameQuery).must(ageQuery)
  142.         );
  143.         // 创建搜索请求
  144.         SearchRequest searchRequest = SearchRequest.of(builder -> builder
  145.                 .index("accounts")
  146.                 .query(q -> q.bool(boolQuery))
  147.         );
  148.         // 执行查询
  149.         SearchResponse<Account> searchResponse = esClient.search(searchRequest, Account.class);
  150.         // 打印结果
  151.         System.out.println("Search results:");
  152.         for (Hit<Account> hit : searchResponse.hits().hits()) {
  153.             System.out.println("Found account: " + hit.source());
  154.         }
  155.     }
  156.     /**
  157.      * 根据账户 ID 更新账户信息。
  158.      *
  159.      * @param account 要更新的账户信息
  160.      * @return 返回更新结果信息
  161.      * @throws IOException 当 Elasticsearch 请求失败时抛出
  162.      */
  163.     @Override
  164.     public String updateAccountById(Account account) throws IOException {
  165.         // 创建更新请求
  166.         UpdateRequest<Account, Account> updateRequest = UpdateRequest.of(builder -> builder
  167.                 .index("accounts")
  168.                 .id(account.getId())
  169.                 .doc(account)
  170.         );
  171.         // 执行更新操作
  172.         UpdateResponse<Account> updateResponse = esClient.update(updateRequest, Account.class);
  173.         // 根据更新结果构建响应信息
  174.         return "Document updated with result: " + updateResponse.result();
  175.     }
  176.     /**
  177.      * 根据账户 ID 从 Elasticsearch 中删除账户信息。
  178.      *
  179.      * @param accountId 要删除的账户 ID
  180.      * @return 返回删除结果信息
  181.      * @throws IOException 当 Elasticsearch 请求失败时抛出
  182.      */
  183.     @Override
  184.     public String deleteAccountById(String accountId) throws IOException {
  185.         // 创建删除请求
  186.         DeleteRequest deleteRequest =  DeleteRequest.of(builder -> builder
  187.                 .index("accounts")  // 指定索引
  188.                 .id(accountId)            // 指定文档 ID
  189.         );
  190.         // 执行删除操作
  191.         DeleteResponse deleteResponse = esClient.delete(deleteRequest);
  192.         // 根据删除结果构建响应信息
  193.         return "Document deleted with result: " + deleteResponse.result();
  194.     }
  195. }
复制代码
三、测试项目

3.1 添加数据

具体测试代码为:
  1. @SpringBootTest
  2. class AccountElasticsearchServiceImplTest {
  3.     @Autowired
  4.     private AccountElasticsearchService accountElasticsearchService;
  5.     @Test
  6.     void indexAccount() throws IOException {
  7.         // 创建测试数据
  8.         Account account = new Account();
  9.         account.setId("12345");
  10.         account.setAccountNumber(12345L);
  11.         account.setEmail("john.doe@example.com");
  12.         // 调用 indexAccount 方法
  13.         accountElasticsearchService.indexAccount(account);
  14.     }
  15. }
复制代码
运行效果为:
  1. 2024-12-07T13:10:42.684+08:00 TRACE 3592 --- [elasticsearch-client] [           main] tracer                                   : curl -iX PUT 'http://localhost:9200/accounts/_doc/12345' -d '{"id":"12345","email":"john.doe@example.com","account_number":12345}'
  2. # HTTP/1.1 201 Created
  3. # Location: /accounts/_doc/12345
  4. # X-elastic-product: Elasticsearch
  5. # content-type: application/json; charset=UTF-8
  6. # content-length: 160
  7. #
  8. # {"_index":"accounts","_type":"_doc","_id":"12345","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":4,"_primary_term":1}
  9. 2024-12-07T13:10:42.714+08:00  INFO 3592 --- [elasticsearch-client] [           main] .e.c.s.i.AccountElasticsearchServiceImpl : Document indexed with ID:12345
复制代码
3.2 搜索数据

具体测试代码为:
  1. @SpringBootTest
  2. class AccountElasticsearchServiceImplTest {
  3.     @Autowired
  4.     private AccountElasticsearchService accountElasticsearchService;
  5.   @Test
  6.     void searchAccountByAccountNumber() throws IOException {
  7.         String result = accountElasticsearchService.searchAccountByAccountNumber(12345L);
  8.         System.out.println(result);
  9.     }
  10. }
复制代码
运行效果为:
  1. 2024-12-07T13:11:14.508+08:00 TRACE 34672 --- [elasticsearch-client] [           main] tracer                                   : curl -iX POST 'http://localhost:9200/accounts/_search?typed_keys=true' -d '{"query":{"term":{"account_number":{"value":12345}}}}'
  2. # HTTP/1.1 200 OK
  3. # X-elastic-product: Elasticsearch
  4. # content-type: application/json; charset=UTF-8
  5. # content-length: 303
  6. #
  7. # {"took":711,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":1,"relation":"eq"},"max_score":1.0,"hits":[{"_index":"accounts","_type":"_doc","_id":"12345","_score":1.0,"_source":{"id":"12345","email":"john.doe@example.com","account_number":12345}}]}}
  8. 2024-12-07T13:11:14.718+08:00  INFO 34672 --- [elasticsearch-client] [           main] .e.c.s.i.AccountElasticsearchServiceImpl : Found exactly 1 matching account(s).
  9. Search results:
  10. Account: 12345, Name: null null
复制代码
3.3 更新数据

具体测试代码为:
  1. @SpringBootTest
  2. class AccountElasticsearchServiceImplTest {
  3.     @Autowired
  4.     private AccountElasticsearchService accountElasticsearchService;
  5.   @Test
  6.     void searchAccountByAccountNumber() throws IOException {
  7.         String result = accountElasticsearchService.searchAccountByAccountNumber(12345L);
  8.         System.out.println(result);
  9.     }
  10. }
复制代码
运行效果为:
  1. 2024-12-07T13:12:56.867+08:00 TRACE 11832 --- [elasticsearch-client] [           main] tracer                                   : curl -iX POST 'http://localhost:9200/accounts/_update/12345' -d '{"doc":{"id":"12345","email":"john.doe@example.com","firstname":"John","lastname":"Doe","account_number":12345}}'
  2. # HTTP/1.1 200 OK
  3. # X-elastic-product: Elasticsearch
  4. # content-type: application/json; charset=UTF-8
  5. # content-length: 160
  6. #
  7. # {"_index":"accounts","_type":"_doc","_id":"12345","_version":2,"result":"updated","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":5,"_primary_term":1}
  8. Document updated with result: Updated
复制代码
3.4 删除数据

具体测试代码为:
  1. @SpringBootTest
  2. class AccountElasticsearchServiceImplTest {
  3.     @Autowired
  4.     private AccountElasticsearchService accountElasticsearchService;
  5.     @Test
  6.     void deleteAccountById() {
  7.         try {
  8.             String result = accountElasticsearchService.deleteAccountById("12345L");
  9.             System.out.println(result);
  10.         } catch (IOException e) {
  11.             e.printStackTrace();
  12.         }
  13.     }
  14. }
复制代码
运行效果为:
  1. 2024-12-07T13:15:10.064+08:00 TRACE 13804 --- [elasticsearch-client] [           main] tracer                                   : curl -iX DELETE 'http://localhost:9200/accounts/_doc/12345'
  2. # HTTP/1.1 200 OK
  3. # X-elastic-product: Elasticsearch
  4. # content-type: application/json; charset=UTF-8
  5. # content-length: 160
  6. #
  7. # {"_index":"accounts","_type":"_doc","_id":"12345","_version":3,"result":"deleted","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":6,"_primary_term":1}
  8. Document deleted with result: Deleted
复制代码
参考资料

Connecting | Elasticsearch Java API Client 7.17
ElasticSearch8 - SpringBoot整合ElasticSearch - 王谷雨 - 博客园

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

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

瑞星

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

标签云

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