目前springboot整合ES主要有四种方法,分别是TransportClient、RestClient、SpringData-Es、Elasticsearch-SQL。其中官方推荐的是RestClient,本文主要也是说明下RestClient方式的集成,该方式配置同时支持ES单机和ES集群。(MybatisPlus仅在下文的分页查询用到了MybatisPlus的包)
一、环境及版本说明
ES运行方式:ES集群启动(三个节点)
ES组件版本:7.4.2
ES中创建索引:test_index ,包含哪些字段参考下文的实体类TestIndex.java
二、搭建过程
1,pom文件中引入依赖,需要注意:引入的ES依赖版本需要和安装的ES组件版本一致,restClient依赖的版本中包含ES,但是不一定和restClient版本一致,所以需要移除restClient依赖中的ES,然后自己引入ES对应版本的依赖
<!-- Java High Level REST Client -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<exclusions>
<exclusion>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</exclusion>
</exclusions>
<version>7.4.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.4.2</version>
</dependency>
2,yaml配置文件中关于ES的配置
# ElasticSearch配置
es:
ip: 192.168.1.1,192.168.1.2,192.168.1.3 #ES为集群时多个IP用英文逗号分隔;若为单机时只填写一个IP即可
port: 9200
protocal: http
username: elastic #若没有设置账号则用双引号代替 ("")
password: elastic1234 #若没有设置密码则用双引号代替 ("")
3,SpringBoot项目中关于ES的配置文件
package com.demo.config;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ESRestClientConfig {
@Value("${es.ip}")
private String esIps;
@Value("${es.port}")
private int esPort;
@Value("${es.protocal}")
private String protocal;
@Value("${es.username}")
private String username;
@Value("${es.password}")
private String password;
@Bean
public RestHighLevelClient restHighLevelClient() {
// 1,若有多个从节点可以持续在内部new多个HttpHost,参数1是IP,参数2是端口,参数3是通信协议
String[] esIpArray = esIps.split(",");
HttpHost[] httpHostList = new HttpHost[esIpArray.length];
for (int i=0;i<esIpArray.length;i++) {
httpHostList[i] = new HttpHost(esIpArray[i],esPort,protocal);
}
RestClientBuilder builder = RestClient.builder(httpHostList);
// 2,若存在账号密码,则初始化账号密码
if(username != null && username != ""){
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(username, password));
builder.setHttpClientConfigCallback((httpClientBuilder) -> {
httpClientBuilder.disableAuthCaching();
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
return httpClientBuilder;
});
}
// 3, 创建客户端
RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);
return restHighLevelClient;
}
}
4,ES索引对应实体类创建
package com.demo.domain;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.Map;
@Data
public class TestIndex implements Serializable {
private static final long serialVersionUID = 3199902908452021663L;
/**
* 测试字段1
*/
private String id;
/**
* 测试字段2
*/
private String endpoint;
/**
* 测试字段3
*/
private String metric;
/**
* 测试字段4
*/
private Date ts;
/**
* 测试字段5
*/
private Integer step;
}
5,ES操作接口及实现
package com.demo.service;
import com.demo.domain.TestIndex ;
import java.io.IOException;
public interface IESOperateService {
/**
* 添加索引内容
* @throws IOException
*/
public void addIndexData(TestIndex testIndex) throws IOException;
/**
* 根据ID删除索引内容
* @throws IOException
*/
public void deleteIndexData(String id) throws IOException;
/**
* 根据文件ID查询文件
* @throws IOException
*/
public TestIndex getIndexDataById(String id) throws IOException;
/**
* 分页查询索引数据
*/
public Page<TestIndex> pageIndexData() throws Exception;
}
package com.demo.service;
import com.demo.domain.TestIndex ;
import com.alibaba.fastjson.JSON;
import org.elasticsearch.action.delete.DeleteRequest;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Service
public class ESOperateServiceImpl implements IESOperateService {
// ES中的索引名称
private static final String INDEX_NAME="test_index";
@Autowired
private RestHighLevelClient restHighLevelClient;
@Override
public void addIndexData(TestIndex testIndex) throws IOException {
// IndexRequest
IndexRequest indexRequest = new IndexRequest(INDEX_NAME);
String source = JSON.toJSONString(testIndex);
indexRequest.id(testIndex.getId()).source(source, XContentType.JSON);
restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
}
@Override
public void deleteIndexData(String id) throws IOException {
// DeleteRequest
DeleteRequest deleteRequest = new DeleteRequest(INDEX_NAME, id);
// 操作ES
restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
}
@Override
public TestIndex getIndexDataById(String id) throws IOException {
// SearchRequest
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices(INDEX_NAME);
// 构建检索条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 根据字段匹配
QueryBuilder queryBuilder = QueryBuilders.matchQuery("id",id);
searchSourceBuilder.query(queryBuilder);
searchRequest.source(searchSourceBuilder);
// 查询ES
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = searchResponse.getHits();
// 遍历封装列表对象
List<TestIndex> testIndexList = new ArrayList<>();
SearchHit[] searchHits = hits.getHits();
for (SearchHit searchHit : searchHits) {
testIndexList.add(JSON.parseObject(searchHit.getSourceAsString(), TestIndex.class));
}
if (!CollectionUtils.isEmpty(testIndexList )) {
return testIndexList .get(0);
} else {
return null;
}
}
@Override
public Page<TestIndex> pageIndexData() throws Exception {
// SearchRequest
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices(INDEX_NAME);
// 设置分页,含尾不含头(例如第一页为0-10,第二页为10-20)
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(0); // 起始位置
searchSourceBuilder.size(10); // 结束位置
// 根据字段匹配
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery("metric", "testMetric"));
// 时间范围匹配,相当于between and ,因ES中日期数据为格林威治时间格式,故需要将日期转为格林威治时间格式
Date currentDate = new Date();
String beginTs = DateUtil.beginOfMonth(currentDate).toString().replace(" ","T");
String endTs = DateUtil.endOfMonth(currentDate).toString().replace(" ","T");
QueryBuilder queryBuilder = QueryBuilders.rangeQuery("ts").from(beginTs).to(endTs);
boolQueryBuilder.must(queryBuilder);
// 模糊匹配
QueryBuilder queryBuilder1 = QueryBuilders.wildcardQuery("endpoint", ("*test*"));
boolQueryBuilder.must(queryBuilder1);
// 放开分页查询最多10000条的限制,ES默认最多能10000条数据,若需要解除限制,下面配置设为true即可
// 需要注意的是:放开ES最大返回值的限制后,相当于深度分页了,对于ES的性能有很大影响,需慎重使用
searchSourceBuilder.trackTotalHits(true);
// 结果排序
searchSourceBuilder.query(boolQueryBuilder).sort("ts", SortOrder.DESC);
searchRequest.source(searchSourceBuilder);
// 执行ES查询动作
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = searchResponse.getHits();
// 遍历封装列表对象
List<TestIndex> testIndexList = new ArrayList<>();
SearchHit[] searchHits = hits.getHits();
for (SearchHit searchHit : searchHits) {
testIndexList.add(JSON.parseObject(searchHit.getSourceAsString(), TestIndex.class));
}
Page<TestIndex> pageDto = new Page();
// 设置当前页码,我这里是写死的分页数据,应用到实际项目还是需要动态传入参数的
pageDto.setCurrent(1).setRecords(testIndexList).
setTotal(hits.getTotalHits().value).setSize(10);
return pageDto;
}
}
6,ES操作controller层实现测试代码
package com.demo.controller;
import com.demo.service.IESOperateService;
import com.demo.domain.TestIndex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
public class ESController
{
@Autowired
private IESOperateService esOperateService;
@GetMapping("/addIndexData")
public void addIndexData() {
TestIndex testIndex= new TestIndex();
testIndex.setId("123456789");
testIndex.setMetric("testMetric");
testIndex.setEndpoint("testEndpoint");
testIndex.setStep(100);
esOperateService.addIndexData(testIndex);
}
// 控制层调用service接口比较简单,就不一一都写出来了,测试话都可以参考上例
}