8. ElasticSearch——整合SpringBoot

本文介绍如何使用Java操作Elasticsearch,包括选择合适的客户端、整合Spring Boot、配置客户端及执行基本的CRUD操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 前言       

1.1 java发送es通常有两种方式

1.1.1 通过9300 TCP端口

要通过9300发送es,就需要与es建立一个长连接,该操作,在有api与之对应:spring-data-elasticsearch:transport-api.jar,但是我们在java中一般不会通过该端口操作,原因如下

1) springboot版本不同,transport-api.jar不同,不能适配es版本

2) 7.X已经不建议使用,8以后就要废弃

3) ES集群节点之间的通信也是使用的9300端口

1.1.2 通过9200 HTTP端口

通过HTTP端口发送请求,方式很多

1) JestClient:非官方,更新慢

2) RestTemplate:模拟HTTP请求,ES很多操作需要自己封装,麻烦

3) HTTPClient:同上

4)Elasticsearch-Rest-Client:官方RestClient,封装了ES操作,API层次分明,上手简单

官网(https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/java-api.html)截图,如下

所以,最终选择 Elasticsearch-Rest-Client(elasticsearch-rest-high-level-client)

查看官网(https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-getting-started-maven.html),可以看到依赖如下

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.10.2</version>
</dependency>

2. 整合SpringBoot

2.1 新建项目

2.2 添加 Java High Level REST Client依赖

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.4.2</version>
</dependency>

如图:

发现ElasticSearch的版本是6.4.3,这是因为boot对elasticsearch进行了管理,如图:

所以,这里我们需要改变版本号

<elasticsearch.version>7.4.2</elasticsearch.version>

再次查看版本依赖,如图;

2.3 配置

2.3.1 注册中心配置

1)配置文件

application.yml

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  application:
    name: gulimall-search

2)开启服务发现与注册

2.3.2 配置es,给容器注册RestHighLevelClient

1)导入依赖(已完成)

2)配置类

查看官网,如图;

所以,配置类如下:

package com.bjc.gulimall.search.config;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GulimallElasticSearchConfig {

    @Bean
    public RestHighLevelClient esRestClient(){
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("192.168.56.10", 9200, "http")));
        return client;
    }
}

2.3.3 测试

package com.bjc.gulimall.search;

import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
class GulimallSearchApplicationTests {

    @Autowired
    private RestHighLevelClient client;

    @Test
    void test01() {
        System.out.println(client);
    }

}

测试结果:

可以打印出对象,说明可以操作client对象了。之后就可以根据官方API来进行操作es了。

3. API的使用

打开Java High Level REST Client的api界面(https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.4/java-rest-high.html),从Getting started 开始,如图:

3.1 请求设置项RequestOptions

RequestOptions用于对所有的请求进行统一的请求头设置。

参看官网,如图:

将该段代码配置到项目中

@Configuration
public class GulimallElasticSearchConfig {

    /* 通用设置项,基于默认规则 */
    public static final RequestOptions COMMON_OPTIONS;
    static {
        RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
        // 注释掉,因为暂时不需要这些配置
        /*builder.addHeader("Authorization", "Bearer " + TOKEN);
        builder.setHttpAsyncResponseConsumerFactory(
                new HttpAsyncResponseConsumerFactory
                        .HeapBufferedResponseConsumerFactory(30 * 1024 * 1024 * 1024));*/
        COMMON_OPTIONS = builder.build();
    }

    @Bean
    public RestHighLevelClient esRestClient(){
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(new HttpHost("192.168.56.10", 9200, "http")));
        return client;
    }
}

3.2 存储Index Api

3.2.1 存储方式

3.2.1.1 通过json字符串存储

操作步骤:

1)创建IndexRequest对象并指定post请求类型

2)设置数据id

3)获取数据的json串

4)发送请求

如图:

3.2.1.2 通过map实现

如图:

3.2.1.3 使用XContentBuilder 构造器

XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
    builder.field("user", "kimchy");
    builder.timeField("postDate", new Date());
    builder.field("message", "trying out Elasticsearch");
}
builder.endObject();
IndexRequest indexRequest = new IndexRequest("posts")
    .id("1").source(builder);  

3.2.1.4 直接利用source的键值对方式

IndexRequest indexRequest = new IndexRequest("posts")
    .id("1")
    .source("user", "kimchy",
        "postDate", new Date(),
        "message", "trying out Elasticsearch"); 

3.2.2 其他设置

1)设置超时时间

request.timeout(TimeValue.timeValueSeconds(1)); 
request.timeout("1s");

2)设置等待刷新策略

request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL); 
request.setRefreshPolicy("wait_for");  

3)设置版本号

request.version(2); 

4)设置管道

request.setPipeline("pipeline"); 

3.2.3 执行Index

3.3.1 同步执行

IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);

3.2.3 异步执行

client.indexAsync(request, RequestOptions.DEFAULT, listener); 

异步执行会有一个监听器,可以理解为js中的回调函数

listener = new ActionListener<IndexResponse>() {
    @Override
    public void onResponse(IndexResponse indexResponse) {
        
    }

    @Override
    public void onFailure(Exception e) {
        
    }
};

3.2.4 保存测试

@RunWith(SpringRunner.class)
@SpringBootTest
class GulimallSearchApplicationTests {

    @Autowired
    private RestHighLevelClient client;

    @Test
    public void indexTest() throws IOException {
        // users 索引名称
        IndexRequest request = new IndexRequest("users");
        request.id("1");    // 设置数据id  如果不指定id值,就默认自增
        // 组装要保存的数据
        Map<String,String> map = new HashMap<>();
        map.put("name","张三");
        map.put("age","18");
        map.put("gender","F");
        map.put("addr","中国");

        // 要保存的内容并指定内容类型
        request.source(JSONObject.toJSONString(map), XContentType.JSON);
        // 使用客户端执行索引保存
        IndexResponse indexResponse = client.index(request, GulimallElasticSearchConfig.COMMON_OPTIONS);

        // 提取响应信息
        System.out.println(indexResponse);
    }

}

打印结果:

在kibana查询,结果如图

3.3 检索数据

3.3.1 简单的GET检索

参看官网APIhttps://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.4/java-rest-high-document-get.html

根据id检索

@Autowired
private RestHighLevelClient client;

@Test
public void getTest() throws IOException {
     // 参数一:索引名
     // 参数二:id
    GetRequest request = new GetRequest("users","1");
    GetResponse getResponse = client.get(request, GulimallElasticSearchConfig.COMMON_OPTIONS);
    System.out.println(getResponse);
}

 

3.4 Search API检索

参看官网:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.4/java-rest-high-search.html 

参看官网实例,可以知道要做检索,需要构建一个检索请求SearchRequest

例如:

@Autowired
private RestHighLevelClient client;

@Test
public void conditionTest() throws IOException {
    // 1. 构建检索请求
    SearchRequest searchRequest = new SearchRequest();
    // 2. 指定检索索引
    searchRequest.indices("bank");

    // 3.1 指定DSL内容
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    // 3.1.1 构建query条件
    searchSourceBuilder.query(QueryBuilders.matchQuery("address","mill"));  // 匹配address包含mill的雇员
    // 3.1.2 构建聚合条件
    // 按照age的值分布聚合,并给该次统计取一个名字aggAge
    TermsAggregationBuilder aggAge = AggregationBuilders.terms("aggAge").field("age").size(10);
    searchSourceBuilder.aggregation(aggAge);
    // 计算平均薪资
    AvgAggregationBuilder banlanceAvg = AggregationBuilders.avg("banlanceAvg").field("balance");
    searchSourceBuilder.aggregation(banlanceAvg);

    // 3. 创建检索条件 用于构建DSL语言
    searchRequest.source(searchSourceBuilder);

    // 4. 执行检索
    SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

    // 5. 分析结果
    // 5.1 获取所有查到的记录
    SearchHits hits = searchResponse.getHits();
    // 5.2 获取所有命中的记录
    SearchHit[] searchHits = hits.getHits();
    for (SearchHit hit : searchHits) {
        System.out.println(hit.getId());
        System.out.println(hit.getSourceAsMap());
    }

    // 5.3 获取分析(聚合)信息
    Aggregations aggregations = searchResponse.getAggregations();
    Terms terms = aggregations.get("aggAge");
    terms.getBuckets().forEach(item -> System.out.println("年龄:"+ item.getKeyAsString() + "  total:" + item.getDocCount()));

    Avg blanceAvg = aggregations.get("banlanceAvg");
    System.out.println("平均薪资:" + blanceAvg.getValue());
}

结果:

### Spring Boot 中使用 Elasticsearch 进行多索引 Join 查询 在处理复杂的数据检索需求时,类似于 SQL 的联表查询,在 Elasticsearch 和 Spring Boot 集成环境中可以通过特定的方式实现。由于 Elasticsearch 并不直接支持传统意义上的 JOIN 操作,因此推荐采用其他策略来达到相似的效果。 #### 使用 Nested 或 Parent-Child 关系模拟 Join 对于结构化良好的文档模型,可以考虑利用 `nested` 类型字段或者 parent-child 映射关系来设计索引模式[^1]。这种方式允许在一个文档内部存储子对象数组(即嵌套),或者是定义两个独立但相互关联的不同类型的文档集合之间的父子级联系。 当涉及到跨不同索引的实体间的关系时,则更倾向于后者——parent-child 结构: ```java // 定义父类映射 (例如作者) @Document(indexName = "author") public class Author { @Id private String id; private String name; // getters and setters... } // 定义子类映射 (例如书籍), 设置其为 child of author @Document(indexName = "book", parent = Author.class) public class Book { @Id private String id; private String title; // other fields... // getters and setters... } ``` #### 利用 Terms Query 执行 Multi-index Search 另一种方法是在应用程序层面处理所谓的“join”,这通常意味着先从一个索引中获取所需 ID 列表,再基于这些 ID 对另一个或多个目标索引发起新的查询请求。此过程可通过组合使用 terms query 来高效完成: ```java SearchRequest searchRequestAuthors = new SearchRequest("authors"); SearchSourceBuilder builderForAuthors = new SearchSourceBuilder(); builderForAuthors.query(QueryBuilders.matchAllQuery()); searchRequestAuthors.source(builderForAuthors); List<String> authorIds = extractAuthorIdsFromResponse(client.search(searchRequestAuthors, RequestOptions.DEFAULT)); if (!authorIds.isEmpty()) { SearchRequest searchBooksByAuthors = new SearchRequest("books"); SearchSourceBuilder bookQueryBuilder = new SearchSourceBuilder(); bookQueryBuilder.query(QueryBuilders.termsQuery("_id", authorIds)); searchBooksByAuthors.source(bookQueryBuilder); SearchResponse booksResponse = client.search(searchBooksByAuthors, RequestOptions.DEFAULT); processBookResults(booksResponse); } ``` 上述代码片段展示了如何首先搜索 authors 索引来获得一系列作者ID,接着把这些ID作为条件去查询 books 索引下的记录[^3]。 #### 考虑使用 Scripted Fields 动态计算连接结果 如果确实需要更加灵活地模仿 SQL-style joins 行为的话,还可以探索 scripted_fields 特性。不过需要注意的是这种方法可能会带来性能上的开销,并不适合大规模生产环境部署。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值