​ElasticSearch目录​


文章目录

六、SpringBoot整合ES

官方文档: [Maven Repository | Java REST Client ​​7.15] | Elastic​

6.1、创建工程

目录结构

SpringBoot测试ES-api_spring boot

代码:​​wlw/ESDemo (gitee.com)​

6.2、导入依赖

注意依赖版本和安装的版本一致

<properties>
<java.version>1.8</java.version>
<!--自己定义es版本依赖,保证和本地一致-->
<elasticsearch.version>7.6.1</elasticsearch.version>
</properties>

导入elasticsearch

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

提前导入fastjson、lombok

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
<!-- lombok需要安装插件 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

6.3、创建并编写配置类

package com.wlw.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 ElasticSearchConfig {
// 注册 rest高级客户端
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
//如果是集群就构建多个
RestClient.builder(new HttpHost("127.0.0.1",9200,"http"))
);
return client;
}
}

6.4、创建并编写实体类

package com.wlw.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = -3843548915035470817L;
private String name;
private Integer age;
}

6.5、测试

所有测试均在 ​​SpringbootElasticsearchApplicationTests​​中编写

注入 ​​RestHighLevelClient​

@Autowired
public RestHighLevelClient restHighLevelClient;

6.5.1、索引的操作:{索引的创建,获取(判断是否存在),删除}

package com.wlw;

import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;

/**
* es7.6.x 高级客户端api测试
*/
@SpringBootTest
class WlwEsApiApplicationTests {

@Autowired
public RestHighLevelClient restHighLevelClient;

// 测试索引的创建, Request: PUT /wlw_index
@Test
public void testCreateIndex() throws IOException {
//1、创建索引请求 ,索引名为wlw_index
CreateIndexRequest request = new CreateIndexRequest("wlw_index");
//2、客户端执行请求 ,通过IndicesClient创建,请求后获得响应
CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);

System.out.println(response.isAcknowledged());// 查看是否创建成功
System.out.println(response);// 查看返回对象
restHighLevelClient.close();
}

//获取索引,只能判断其是否存在
@Test
public void testGetIndex() throws IOException {
GetIndexRequest request = new GetIndexRequest("wlw_index");
boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}

// 测试索引删除
@Test
public void testDeleteIndex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("wlw_index");
AcknowledgedResponse response = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
System.out.println(response.isAcknowledged());// 是否删除成功
restHighLevelClient.close();
}

}

6.5.2、文档的操作

1、文档的添加
// 测试添加文档(先创建一个User实体类,添加fastjson依赖)
@Test
public void testAddDocument() throws IOException {
//1、创建一个User对象
User wlw = new User("wlw", 18);
//2、创建请求 (要保证es中有这个索引)
IndexRequest request = new IndexRequest("wlw_index");

//3、制定规则 PUT /wlw_index/_doc/1
request.id("1");// 设置文档ID
request.timeout(TimeValue.timeValueMillis(1000));// request.timeout("1s")
// 将我们的数据放入请求中(需要将数据序列化)
request.source(JSON.toJSONString(wlw), XContentType.JSON);

//4、客户端发送请求,获取响应的结果
IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
// 获取建立索引的状态信息 CREATED,对应命令返回的状态
System.out.println(response.status());
System.out.println(response.toString());
//输出结果:
/**
* CREATED
* IndexResponse[index=wlw_index,type=_doc,id=1,version=1,result=created,seqNo=0,primaryTerm=1,shards={"total":2,"successful":1,"failed":0}]
*/
}
2、文档的获取,并判断其是否存在
// 获取文档,判断是否存在 get /wlw_index/_doc/1
@Test
public void testDocumentIsExists() throws IOException {
GetRequest request = new GetRequest("wlw_index", "1");
// 不获取返回的 _source的上下文了
request.fetchSourceContext(new FetchSourceContext(false));
request.storedFields("_none_");

boolean exists = restHighLevelClient.exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
3、文档信息的获取
// 测试获得文档信息
@Test
public void testGetDocument() throws IOException {
GetRequest request = new GetRequest("wlw_index","1");

GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);
System.out.println(response.getSourceAsString());// 打印文档内容
System.out.println(response);// 返回的全部内容和命令是一样的
restHighLevelClient.close();
}
4、文档的更新
// 测试更新文档内容
@Test
public void testUpdateDocument() throws IOException {
UpdateRequest request = new UpdateRequest("wlw_index", "1");
User user = new User("wlw_update",11);
request.doc(JSON.toJSONString(user), XContentType.JSON);

UpdateResponse response = restHighLevelClient.update(request, RequestOptions.DEFAULT);
System.out.println(response.status()); // OK
restHighLevelClient.close();
}
5、文档的删除
// 测试删除文档
@Test
public void testDeleteDocument() throws IOException {
DeleteRequest request = new DeleteRequest("wlw_index", "1");
request.timeout("1s");

DeleteResponse response = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
System.out.println(response.status());// OK
}
6、文档的查询
// 查询
// SearchRequest 搜索请求
// SearchSourceBuilder 条件构造
// HighlightBuilder 高亮
// TermQueryBuilder 精确查询
// MatchAllQueryBuilder 匹配全部查询
// xxxQueryBuilder ... 对应之前的查询命令
@Test
public void testSearch() throws IOException {
// 1.创建查询请求对象
SearchRequest searchRequest = new SearchRequest("wlw_index");
// 2.构建搜索条件
SearchSourceBuilder searchBuilder = new SearchSourceBuilder();

// 2.1、查询条件 使用QueryBuilders工具类创建
// 精确查询 ,QueryBuilders.termQuery,查询name = wlw 的用户
// 匹配查询,QueryBuilders.matchAllQuery,匹配所有
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "wlw_update");
// MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();

//2.2、其他<可有可无>:(可以参考 SearchSourceBuilder 的字段部分)
// 设置高亮
searchBuilder.highlighter(new HighlightBuilder());
// 设置分页
// searchBuilder.from();
// searchBuilder.size();
searchBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
// 2.3、条件投入
searchBuilder.query(termQueryBuilder);

// 3.添加条件到请求
searchRequest.source(searchBuilder);
// 4.客户端执行查询请求
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 5.查看返回结果
SearchHits hits = searchResponse.getHits();
System.out.println(JSON.toJSONString(hits));

System.out.println("===========遍历输出============");
for (SearchHit documentFields : hits.getHits()) {
System.out.println(documentFields.getSourceAsMap());
}
}



在开发中,查询一般不会这样简单,一般都是多个条件拼接在一起的。

所以需要用到 ​​BoolQueryBuilder​​​ 这个类去将各个条件拼接,
用​​​QueryBuilders​​​ 去构建具体的查询条件,
然后把​​​BoolQueryBuilder​​​放到​​SearchSourceBuilder​​​中。这里提一下​​SearchSourceBuilder​​的query()方法:

public SearchSourceBuilder query(QueryBuilder query) {
this.queryBuilder = query;
return this;
}
//此方法需要的是一个QueryBuilder对象,
//查看源码会发现QueryBuilder有很多实现类,
//其中BoolQueryBuilder 和 TermQueryBuilder都是。

看一下具体的使用方式吧:

@Test
public void testSearch() throws IOException {
// 1.创建查询请求对象
SearchRequest searchRequest = new SearchRequest("wlw_index");
// 2.构建搜索条件
SearchSourceBuilder searchBuilder = new SearchSourceBuilder();

// 2.1、查询条件
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
//以下条件类比mysql: age = 12 and (name = 'wlw' or name = 'tom')
boolQueryBuilder.must(QueryBuilders.termQuery("age", 12));
boolQueryBuilder.must(QueryBuilders.boolQuery().
should(QueryBuilders.termQuery("name", "wlw")).
should(QueryBuilders.termQuery("name", "tom")));
//我们在命令中构造的复杂条件都能在QueryBuilders中找到对应的方法,比如还有:
//QueryBuilders.rangeQuery("age").from(10).to(30);
//boolQueryBuilder 除了must 还有filter,should等.....

//2.2、设置高亮
searchBuilder.highlighter(new HighlightBuilder());
// 设置分页
// searchBuilder.from();
// searchBuilder.size();
searchBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

// 2.3、条件投入
searchBuilder.query(boolQueryBuilder);

// 3.添加条件到请求
searchRequest.source(searchBuilder);

// 4.客户端执行查询请求
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

// 5.查看返回结果
SearchHits hits = searchResponse.getHits();
System.out.println(JSON.toJSONString(hits));

System.out.println("===========遍历输出============");
for (SearchHit documentFields : hits.getHits()) {
System.out.println(documentFields.getSourceAsMap());
}
}
7、批量添加数据

前面的操作都无法批量添加数据

// 上面的这些api无法批量增加数据(只会保留最后一个source)
@Test
public void test() throws IOException {
IndexRequest request = new IndexRequest("index_1");// 没有id会自动生成一个随机ID
request.source(JSON.toJSONString(new User("liu",1)),XContentType.JSON);
request.source(JSON.toJSONString(new User("wang",2)),XContentType.JSON);
request.source(JSON.toJSONString(new User("dui",3)),XContentType.JSON);
IndexResponse index = restHighLevelClient.index(request, RequestOptions.DEFAULT);
System.out.println(index.status());// created
}
// 特殊的,真的项目一般会 批量插入数据
@Test
public void testBulkRequest() throws IOException {
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("10s");
ArrayList<User> users = new ArrayList<>();
users.add(new User("wlw-1",1));
users.add(new User("wlw-2",2));
users.add(new User("wlw-3",3));
users.add(new User("wlw-4",4));
users.add(new User("wlw-5",5));
users.add(new User("wlw-6",6));
// 批量处理请求
for (int i = 0; i < users.size(); i++) {
//批量更新或者批量删除,就修改成对应的的请求对象
bulkRequest.add(
// 这里是数据信息
new IndexRequest("wlw_index")
.id(""+(i + 1)) // 没有设置id 会自定生成一个随机id
.source(JSON.toJSONString(users.get(i)),XContentType.JSON)
);
}
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulk.status());// ok
}

6.6 源码分析

package org.springframework.boot.autoconfigure.elasticsearch;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Elasticsearch REST clients.
*
* @author Brian Clozel
* @author Stephane Nicoll
* @since 2.1.0
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestHighLevelClient.class)
@ConditionalOnMissingBean(RestClient.class)
@EnableConfigurationProperties(ElasticsearchRestClientProperties.class)
@Import({ RestClientBuilderConfiguration.class, RestHighLevelClientConfiguration.class,
RestClientSnifferConfiguration.class })
public class ElasticsearchRestClientAutoConfiguration {

}

虽然这里导入3个类,但都是静态内部类,核心类就一个

package org.springframework.boot.autoconfigure.elasticsearch;

/**
* Elasticsearch rest client configurations.
*
* @author Stephane Nicoll
*/
class ElasticsearchRestClientConfigurations {

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(RestClientBuilder.class)
static class RestClientBuilderConfiguration {

@Bean
RestClientBuilderCustomizer defaultRestClientBuilderCustomizer(ElasticsearchRestClientProperties properties) {
return new DefaultRestClientBuilderCustomizer(properties);
}

// RestClientBuilder
@Bean
RestClientBuilder elasticsearchRestClientBuilder(ElasticsearchRestClientProperties properties,
ObjectProvider<RestClientBuilderCustomizer> builderCustomizers) {
HttpHost[] hosts = properties.getUris().stream().map(this::createHttpHost).toArray(HttpHost[]::new);
RestClientBuilder builder = RestClient.builder(hosts);
builder.setHttpClientConfigCallback((httpClientBuilder) -> {
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(httpClientBuilder));
return httpClientBuilder;
});
builder.setRequestConfigCallback((requestConfigBuilder) -> {
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(requestConfigBuilder));
return requestConfigBuilder;
});
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder;
}

private HttpHost createHttpHost(String uri) {
try {
return createHttpHost(URI.create(uri));
}
catch (IllegalArgumentException ex) {
return HttpHost.create(uri);
}
}

private HttpHost createHttpHost(URI uri) {
if (!StringUtils.hasLength(uri.getUserInfo())) {
return HttpHost.create(uri.toString());
}
try {
return HttpHost.create(new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(),
uri.getQuery(), uri.getFragment()).toString());
}
catch (URISyntaxException ex) {
throw new IllegalStateException(ex);
}
}

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(RestHighLevelClient.class)
static class RestHighLevelClientConfiguration {

// RestHighLevelClient 高级客户端,也是我们这里用到的而客户端
@Bean
RestHighLevelClient elasticsearchRestHighLevelClient(RestClientBuilder restClientBuilder) {
return new RestHighLevelClient(restClientBuilder);
}

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Sniffer.class)
@ConditionalOnSingleCandidate(RestHighLevelClient.class)
static class RestClientSnifferConfiguration {

@Bean
@ConditionalOnMissingBean
Sniffer elasticsearchSniffer(RestHighLevelClient client, ElasticsearchRestClientProperties properties) {
SnifferBuilder builder = Sniffer.builder(client.getLowLevelClient());
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(properties.getSniffer().getInterval()).asInt(Duration::toMillis)
.to(builder::setSniffIntervalMillis);
map.from(properties.getSniffer().getDelayAfterFailure()).asInt(Duration::toMillis)
.to(builder::setSniffAfterFailureDelayMillis);
return builder.build();
}

}

static class DefaultRestClientBuilderCustomizer implements RestClientBuilderCustomizer {

private static final PropertyMapper map = PropertyMapper.get();

private final ElasticsearchRestClientProperties properties;

DefaultRestClientBuilderCustomizer(ElasticsearchRestClientProperties properties) {
this.properties = properties;
}

@Override
public void customize(RestClientBuilder builder) {
}

@Override
public void customize(HttpAsyncClientBuilder builder) {
builder.setDefaultCredentialsProvider(new PropertiesCredentialsProvider(this.properties));
}

@Override
public void customize(RequestConfig.Builder builder) {
map.from(this.properties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis)
.to(builder::setConnectTimeout);
map.from(this.properties::getReadTimeout).whenNonNull().asInt(Duration::toMillis)
.to(builder::setSocketTimeout);
}

}

private static class PropertiesCredentialsProvider extends BasicCredentialsProvider {

PropertiesCredentialsProvider(ElasticsearchRestClientProperties properties) {
if (StringUtils.hasText(properties.getUsername())) {
Credentials credentials = new UsernamePasswordCredentials(properties.getUsername(),
properties.getPassword());
setCredentials(AuthScope.ANY, credentials);
}
properties.getUris().stream().map(this::toUri).filter(this::hasUserInfo)
.forEach(this::addUserInfoCredentials);
}

private URI toUri(String uri) {
try {
return URI.create(uri);
}
catch (IllegalArgumentException ex) {
return null;
}
}

private boolean hasUserInfo(URI uri) {
return uri != null && StringUtils.hasLength(uri.getUserInfo());
}

private void addUserInfoCredentials(URI uri) {
AuthScope authScope = new AuthScope(uri.getHost(), uri.getPort());
Credentials credentials = createUserInfoCredentials(uri.getUserInfo());
setCredentials(authScope, credentials);
}

private Credentials createUserInfoCredentials(String userInfo) {
int delimiter = userInfo.indexOf(":");
if (delimiter == -1) {
return new UsernamePasswordCredentials(userInfo, null);
}
String username = userInfo.substring(0, delimiter);
String password = userInfo.substring(delimiter + 1);
return new UsernamePasswordCredentials(username, password);
}
}
}