SpringBoot集成elasticsearch
1. 引入jar包
//elastic search
compile('org.elasticsearch:elasticsearch:7.10.1')
compile('org.elasticsearch.client:elasticsearch-rest-client:7.10.1')
compile('org.elasticsearch.client:elasticsearch-rest-high-level-client:7.10.1')
2. 配置项
elasticsearch.schema=http
elasticsearch.address= 127.0.0.1:9200
elasticsearch.userName= ""
elasticsearch.password= ""
elasticsearch.connectTimeout= 10000
elasticsearch.socketTimeout= 10000
elasticsearch.connectionRequestTimeout= 10000
elasticsearch.maxConnectNum= 100
elasticsearch.maxConnectPerRoute= 100
management.health.elasticsearch.enabled=false
3. esConfig
@Configuration
public class EsConfig {
/**
* 协议
*/
@Value("${elasticsearch.schema:http}")
private String schema;
/**
* 集群地址,如果有多个用“,”隔开
*/
@Value("${elasticsearch.address}")
private String address;
@Value("${elasticsearch.userName}")
private String userName;
@Value("${elasticsearch.password}")
private String password;
/**
* 连接超时时间
*/
@Value("${elasticsearch.connectTimeout:5000}")
private int connectTimeout;
/**
* Socket 连接超时时间
*/
@Value("${elasticsearch.socketTimeout:10000}")
private int socketTimeout;
/**
* 获取连接的超时时间
*/
@Value("${elasticsearch.connectionRequestTimeout:5000}")
private int connectionRequestTimeout;
/**
* 最大连接数
*/
@Value("${elasticsearch.maxConnectNum:100}")
private int maxConnectNum;
/**
* 最大路由连接数
*/
@Value("${elasticsearch.maxConnectPerRoute:100}")
private int maxConnectPerRoute;
@Bean
public RestHighLevelClient restHighLevelClient() {
// 拆分地址
List<HttpHost> hostLists = new ArrayList<>();
String[] hostList = address.split(",");
for (String addr : hostList) {
String host = addr.split(":")[0];
String port = addr.split(":")[1];
hostLists.add(new HttpHost(host, Integer.parseInt(port), schema));
}
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
// 转换成 HttpHost 数组
HttpHost[] httpHost = hostLists.toArray(new HttpHost[]{});
// 构建连接对象
RestClientBuilder builder = RestClient.builder(httpHost);
// 异步连接延时配置
builder.setRequestConfigCallback(requestConfigBuilder -> {
requestConfigBuilder.setConnectTimeout(connectTimeout);
requestConfigBuilder.setSocketTimeout(socketTimeout);
requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeout);
return requestConfigBuilder;
});
// 异步连接数配置
builder.setHttpClientConfigCallback(httpClientBuilder -> {
httpClientBuilder.setMaxConnTotal(maxConnectNum);
httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
return httpClientBuilder;
});
return new RestHighLevelClient(builder);
}
}
4. elasticTools
//elasticsearchEntity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ElasticSearchEntity {
/**
* 主键标识,用户ES持久化
*/
private String id;
/**
* JSON对象,实际存储数据
*/
private Map data;
}
@Slf4j
@Component
public class ElasticSearch {
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* @param idxName 索引名称
* @param idxSQL 索引描述
* @return void
* @throws
* @See
* @date 2019/10/17 17:30
* @since
*/
public void createIndex(String idxName, String idxSQL) {
try {
if (!this.indexExist(idxName)) {
//log.error(" idxName={} 已经存在,idxSql={}", idxName, idxSQL);
return;
}
CreateIndexRequest request = new CreateIndexRequest(idxName);
buildSetting(request);
request.mapping(idxSQL, XContentType.JSON);
// request.settings() 手工指定Setting
CreateIndexResponse res = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
if (!res.isAcknowledged()) {
throw new RuntimeException("初始化失败");
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param idxName 索引名称
* @param builder 索引描述
* @return void
* @throws
* @See
* @date 2019/10/17 17:30
* @since
*/
public void createIndex2(String idxName, XContentBuilder builder) {
try {
if (!this.indexExist(idxName)) {
//log.error(" idxName={} 已经存在,idxSql={}", idxName, builder);
return;
}
CreateIndexRequest request = new CreateIndexRequest(idxName);
GetIndexRequest getIndexRequest = new GetIndexRequest(idxName);
buildSetting(request);
request.mapping(builder);
// request.settings() 手工指定Setting
boolean exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
if (!exists) {
CreateIndexResponse res = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
if (!res.isAcknowledged()) {
log.info("初始化失败");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 断某个index是否存在
*
* @param idxName index名
* @return boolean
* @throws
* @See
* @date 2019/10/17 17:27
* @since
*/
public boolean indexExist(String idxName) throws Exception {
GetIndexRequest request = new GetIndexRequest(idxName);
request.local(false);
request.humanReadable(true);
request.includeDefaults(false);
request.indicesOptions(IndicesOptions.lenientExpandOpen());
return restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
}
/**
* 断某个index是否存在
*
* @param idxName index名
* @return boolean
* @throws
* @See
* @date 2019/10/17 17:27
* @since
*/
public boolean isExistsIndex(String idxName) throws Exception {
return restHighLevelClient.indices().exists(new GetIndexRequest(idxName), RequestOptions.DEFAULT);
}
/**
* 设置分片
*
* @param request
* @return void
* @throws
* @See
* @date 2019/10/17 19:27
* @since
*/
public void buildSetting(CreateIndexRequest request) {
request.settings(Settings.builder().put("index.number_of_shards", 3)
.put("index.number_of_replicas", 2));
}
/**
* @param idxName index
* @param entity 对象
* @return void
* @throws
* @See
* @date 2019/10/17 17:27
* @since
*/
public void insertOrUpdateOne(String idxName, ElasticSearchEntity entity) {
IndexRequest request = new IndexRequest(idxName, "_doc");
log.info("Data : id={},entity={}", entity.getId(), JSON.toJSONString(entity.getData()));
request.id(entity.getId());
request.source(entity.getData(), XContentType.JSON);
// request.source(JSON.toJSONString(entity.getData()), XContentType.JSON);
try {
restHighLevelClient.index(request, RequestOptions.DEFAULT);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 批量插入数据
*
* @param idxName index
* @param list 带插入列表
* @return void
* @throws
* @See
* @date 2019/10/17 17:26
* @since
*/
public void insertBatch(String idxName, List<ElasticSearchEntity> list) {
BulkRequest request = new BulkRequest();
list.forEach(item -> request.add(new IndexRequest(idxName, "_doc").id(item.getId())
.source(item.getData(), XContentType.JSON)));
try {
if (!CollectionUtils.isEmpty(request.requests())) {
restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 异步批量插入,并执行回调方法
*
* @param idxName
* @param list
* @param consumer
*/
public void insertBatchAsync(String idxName, List<ElasticSearchEntity> list, BiConsumer consumer, Object param) {
BulkRequest request = new BulkRequest();
list.parallelStream().forEach(item -> request.add(new IndexRequest(idxName, "_doc").id(item.getId())
.source(item.getData(), XContentType.JSON)));
try {
if (!CollectionUtils.isEmpty(request.requests())) {
restHighLevelClient.bulkAsync(request, RequestOptions.DEFAULT, getActionListener(consumer, list, param));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void insertBatchAsync(String idxName, List<JSONObject> list, Consumer<List<JSONObject>> consumer) {
BulkRequest request = new BulkRequest();
list.forEach(item -> request.add(new IndexRequest(idxName, "_doc").id(item.getString("msgid"))
.source(item, XContentType.JSON)));
try {
if (!CollectionUtils.isEmpty(request.requests())) {
restHighLevelClient.bulkAsync(request, RequestOptions.DEFAULT, getActionListener(consumer, list));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void updateBatch(String idxName, List<ElasticSearchEntity> list) {
BulkRequest request = new BulkRequest();
list.forEach(item -> request.add(new UpdateRequest(idxName, item.getId()).upsert(item.getData(), XContentType.JSON)));
try {
if (!CollectionUtils.isEmpty(request.requests())) {
restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 批量删除
*
* @param idxName index
* @param idList 待删除列表
* @return void
* @throws
* @See
* @date 2019/10/17 17:14
* @since
*/
public <T> void deleteBatch(String idxName, Collection<T> idList) {
BulkRequest request = new BulkRequest();
idList.forEach(item -> request.add(new DeleteRequest(idxName, "_doc", item.toString())));
try {
if (!CollectionUtils.isEmpty(request.requests())) {
restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @param idxName index
* @param builder 查询参数
* @param c 结果类对象
* @return java.util.List<T>
* @throws
* @See
* @date 2019/10/17 17:14
* @since
*/
public <T> List<T> search(String idxName, SearchSourceBuilder builder, Class<T> c) {
SearchRequest request = new SearchRequest(idxName);
request.source(builder);
try {
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
long totalHits = response.getHits().getTotalHits().value;
SearchHit[] hits = response.getHits().getHits();
List<T> res = new ArrayList<>(hits.length);
for (SearchHit hit : hits) {
res.add(JSON.parseObject(hit.getSourceAsString(), c));
}
return res;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public <T> PageInfo<T> searchPage(String idxName, SearchSourceBuilder builder, int pageNum, int pageSize, Class<T> c) {
SearchRequest request = new SearchRequest(idxName);
request.source(builder);
try {
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
int totalHits = (int) response.getHits().getTotalHits().value;
SearchHit[] hits = response.getHits().getHits();
List<T> res = new ArrayList<>(hits.length);
for (SearchHit hit : hits) {
//解析高亮字段
//获取当前命中的对象的高亮的字段
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
HighlightField hghlightContent = highlightFields.get("text.content");
String newName = "";
if (hghlightContent != null) {
//获取该高亮字段的高亮信息
Text[] fragments = hghlightContent.getFragments();
//将前缀、关键词、后缀进行拼接
for (Text fragment : fragments) {
newName += fragment;
}
}
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
sourceAsMap.put("content", newName);
res.add(JSON.parseObject(JSONObject.toJSONString(sourceAsMap), c));
}
// 封装分页
PageInfo<T> page = new PageInfo<>();
page.setList(res);
page.setPageNum(pageNum);
page.setPageSize(pageSize);
page.setTotal(totalHits);
page.setPages(totalHits == 0 ? 0 : (totalHits % pageNum == 0 ? totalHits / pageNum : (totalHits / pageNum) + 1));
page.setHasNextPage(page.getPageNum() < page.getPages());
return page;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 删除index
*
* @param idxName
* @return void
* @throws
* @See
* @date 2019/10/17 17:13
* @since
*/
public void deleteIndex(String idxName) {
try {
if (!this.indexExist(idxName)) {
//log.error(" idxName={} 已经存在", idxName);
return;
}
restHighLevelClient.indices().delete(new DeleteIndexRequest(idxName), RequestOptions.DEFAULT);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @param idxName
* @param builder
* @return void
* @throws
* @See
* @date 2019/10/17 17:13
* @since
*/
public void deleteByQuery(String idxName, QueryBuilder builder) {
DeleteByQueryRequest request = new DeleteByQueryRequest(idxName);
request.setQuery(builder);
//设置批量操作数量,最大为10000
request.setBatchSize(10000);
request.setConflicts("proceed");
try {
restHighLevelClient.deleteByQuery(request, RequestOptions.DEFAULT);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public ActionListener getActionListener(Consumer consumer, List<JSONObject> list) {
return new ActionListener() {
@Override
public void onResponse(Object o) {
consumer.accept(list);
}
@Override
public void onFailure(Exception e) {
log.warn("work with es failed, exception={}", ExceptionUtils.getStackTrace(e));
}
};
}
public ActionListener getActionListener(BiConsumer consumer, List<ElasticSearchEntity> list, Object param) {
return new ActionListener() {
@Override
public void onResponse(Object o) {
consumer.accept(list, param);
}
@Override
public void onFailure(Exception e) {
log.warn("work with es failed, exception={}", ExceptionUtils.getStackTrace(e));
}
};
}
public XContentBuilder getFinanceMapping() throws IOException {
// 创建 会话文本Mapping
XContentBuilder xContentBuilder = XContentFactory.jsonBuilder()
.startObject()
.startObject("properties")
.startObject("msgid")
.field("type", "keyword")
.endObject()
.startObject("seq")
.field("type", "long")
.endObject()
.startObject("action")
.field("type", "keyword")
.endObject()
.startObject("from")
.field("type", "keyword")
.endObject()
.startObject("roomid")
.field("type", "keyword")
.endObject()
.startObject("msgtime")
.field("type", "long")
.endObject()
.startObject("msgtype")
.field("type", "keyword")
.endObject()
.endObject()
.endObject();
return xContentBuilder;
}
}
5. 注意点
elasticsearchentity中的map,可采用fastjson进行转换JSON.parseObject(JSON.toJSONString(xxx,Map.class));
参考:
https://gitee.com/LinkWeChat/link-wechat