mirror of
https://github.com/RemainderTime/spring-boot-base-demo.git
synced 2026-02-06 15:10:56 +08:00
es工具类常用操作方法实现和封装
This commit is contained in:
@@ -7,10 +7,17 @@ import lombok.Data;
|
||||
* @author remaindertime
|
||||
* @className EsModel
|
||||
* @date 2024/12/10
|
||||
* @description
|
||||
* @description es基础模型
|
||||
*/
|
||||
@Data
|
||||
public class EsModel<T> {
|
||||
public class EsBaseModel<T> {
|
||||
|
||||
public EsBaseModel(String indexName, String documentId, T documentModel, Class<T> clazz) {
|
||||
this.indexName = indexName;
|
||||
this.documentId = documentId;
|
||||
this.documentModel = documentModel;
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* 索引名称
|
||||
@@ -31,4 +38,5 @@ public class EsModel<T> {
|
||||
* 映射对象类对象
|
||||
*/
|
||||
private Class<T> clazz;
|
||||
|
||||
}
|
||||
81
src/main/java/cn/xf/basedemo/common/model/EsSearchModel.java
Normal file
81
src/main/java/cn/xf/basedemo/common/model/EsSearchModel.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package cn.xf.basedemo.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* packageName cn.xf.basedemo.common.model
|
||||
* @author remaindertime
|
||||
* @className EsSearchModel
|
||||
* @date 2024/12/11
|
||||
* @description es 搜索模型
|
||||
*/
|
||||
@Data
|
||||
public class EsSearchModel<T> {
|
||||
|
||||
public EsSearchModel() {
|
||||
// 使用 LinkedHashMap 保持插入顺序
|
||||
this.sort = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 索引名称
|
||||
*/
|
||||
private String indexName;
|
||||
|
||||
/**
|
||||
* 文档类型
|
||||
*/
|
||||
private Class<T> clazz;
|
||||
|
||||
/**
|
||||
* 页数
|
||||
*/
|
||||
private Integer pageNum;
|
||||
|
||||
/**
|
||||
* 每页数量
|
||||
*/
|
||||
private Integer pageSize;
|
||||
/**
|
||||
* 精准查询字段
|
||||
*/
|
||||
private Map<String, Object> termQuery;
|
||||
|
||||
/**
|
||||
* 模糊查询字段(一般是text类型)
|
||||
*/
|
||||
private Map<String, Object> matchQuery;
|
||||
|
||||
/**
|
||||
* 排序字段规则 ({"age":"desc"})
|
||||
*/
|
||||
private Map<String, String> sort;
|
||||
|
||||
/**
|
||||
* 分组去重字段(支持的字段类型:keyword、numeric、date 和 boolean )
|
||||
*/
|
||||
private String repeatField;;
|
||||
|
||||
/**
|
||||
* 分组嵌套查询别名
|
||||
*/
|
||||
private String innerAlias;
|
||||
|
||||
/**
|
||||
* 分组嵌套查询数量
|
||||
*/
|
||||
private Integer innerSize;
|
||||
|
||||
/**
|
||||
* 指定需要返回的字段
|
||||
*/
|
||||
private List<String> includes;
|
||||
/**
|
||||
* 指定需要排除的字段
|
||||
*/
|
||||
private List<String> excludes;
|
||||
|
||||
}
|
||||
@@ -1,24 +1,22 @@
|
||||
package cn.xf.basedemo.common.utils;
|
||||
|
||||
import cn.xf.basedemo.common.model.EsModel;
|
||||
import cn.xf.basedemo.common.model.EsBaseModel;
|
||||
import cn.xf.basedemo.common.model.EsSearchModel;
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.elasticsearch._types.FieldValue;
|
||||
import co.elastic.clients.elasticsearch._types.OpType;
|
||||
import co.elastic.clients.elasticsearch._types.*;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.*;
|
||||
import co.elastic.clients.elasticsearch.core.IndexRequest;
|
||||
import co.elastic.clients.elasticsearch.core.IndexResponse;
|
||||
import co.elastic.clients.elasticsearch.core.UpdateRequest;
|
||||
import co.elastic.clients.elasticsearch.core.UpdateResponse;
|
||||
import co.elastic.clients.elasticsearch.core.*;
|
||||
import co.elastic.clients.elasticsearch.core.search.*;
|
||||
import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
|
||||
import co.elastic.clients.elasticsearch.indices.ExistsRequest;
|
||||
import co.elastic.clients.json.JsonData;
|
||||
import co.elastic.clients.transport.endpoints.BooleanResponse;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -29,19 +27,22 @@ import java.util.stream.Collectors;
|
||||
* @date 2024/12/10
|
||||
* @description elasticsearch工具类
|
||||
*/
|
||||
//@Component
|
||||
@Slf4j
|
||||
@Component
|
||||
public class EsUtil {
|
||||
|
||||
// @Autowired
|
||||
private static ElasticsearchClient esClient;
|
||||
public static ElasticsearchClient esClient;
|
||||
|
||||
{
|
||||
esClient = (ElasticsearchClient) ApplicationContextUtils.getBean("elasticsearchClient");
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断索引是否存在
|
||||
* @param indexName
|
||||
* @return
|
||||
*/
|
||||
private static boolean existIndex(String indexName) {
|
||||
public static boolean existIndex(String indexName) {
|
||||
try {
|
||||
// 创建 ExistsRequest 请求
|
||||
ExistsRequest request = new ExistsRequest.Builder()
|
||||
@@ -98,17 +99,22 @@ public class EsUtil {
|
||||
|
||||
/**
|
||||
* 新增文档
|
||||
* @param esModel
|
||||
* @param esBaseModel
|
||||
* @return
|
||||
*/
|
||||
public static boolean addDocument(EsModel esModel) {
|
||||
public static boolean addDocument(EsBaseModel esBaseModel) {
|
||||
try {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
String jsonString = objectMapper.writeValueAsString(esBaseModel.getDocumentModel());
|
||||
log.info("es新增文档,文档内容:{}", jsonString);
|
||||
// 创建 IndexRequest 实例
|
||||
IndexRequest request = new IndexRequest.Builder()
|
||||
.index(esModel.getIndexName())
|
||||
.id(esModel.getDocumentId()) //指定文档id,不指定会自动生成
|
||||
.document(esModel.getDocumentModel())
|
||||
.opType(OpType.Create).build(); // 只会在文档 ID 不存在时创建文档
|
||||
.index(esBaseModel.getIndexName())
|
||||
.id(esBaseModel.getDocumentId()) //指定文档id,不指定会自动生成
|
||||
.document(esBaseModel.getDocumentModel())
|
||||
.opType(OpType.Create) // 只会在文档 ID 不存在时创建文档
|
||||
.build();
|
||||
|
||||
IndexResponse response = esClient.index(request);
|
||||
if ("created".equals(response.result())) {
|
||||
log.info("Document created: " + response.id());
|
||||
@@ -118,22 +124,24 @@ public class EsUtil {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
log.error("es新增文档失败", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新文档
|
||||
* @param esModel
|
||||
* @param esBaseModel
|
||||
* @return
|
||||
*/
|
||||
public boolean updateDocument(EsModel esModel) {
|
||||
public boolean updateDocument(EsBaseModel esBaseModel) {
|
||||
try {
|
||||
UpdateRequest updateRequest = new UpdateRequest.Builder<>()
|
||||
.index(esModel.getIndexName())
|
||||
.id(esModel.getDocumentId())
|
||||
.doc(esModel.getDocumentModel()).build();
|
||||
UpdateResponse updateResponse = esClient.update(updateRequest, esModel.getClazz());
|
||||
.index(esBaseModel.getIndexName())
|
||||
.id(esBaseModel.getDocumentId())
|
||||
.doc(esBaseModel.getDocumentModel()).build();
|
||||
UpdateResponse updateResponse = esClient.update(updateRequest, esBaseModel.getClazz());
|
||||
log.info("Document updated: " + updateResponse.id());
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
@@ -144,20 +152,20 @@ public class EsUtil {
|
||||
|
||||
/**
|
||||
* 更新文档指定字段(script 脚本)
|
||||
* @param esModel
|
||||
* @param esBaseModel
|
||||
* @param script 脚本内容
|
||||
* @param params 传递参数内容
|
||||
*/
|
||||
public void updateDocumentWithScript(EsModel esModel, String script, Map<String, JsonData> params) {
|
||||
public void updateDocumentWithScript(EsBaseModel esBaseModel, String script, Map<String, JsonData> params) {
|
||||
try {
|
||||
UpdateRequest updateRequest = new UpdateRequest.Builder<>()
|
||||
.index(esModel.getIndexName())
|
||||
.id(esModel.getDocumentId())
|
||||
.index(esBaseModel.getIndexName())
|
||||
.id(esBaseModel.getDocumentId())
|
||||
.script(s ->
|
||||
s.source(script)// 脚本内容:.source("ctx._source.age += params.increment")
|
||||
.params(params)) // 传递参数内容:.params("increment",sonData.of(5))
|
||||
.build();
|
||||
UpdateResponse updateResponse = esClient.update(updateRequest, esModel.getClazz());
|
||||
UpdateResponse updateResponse = esClient.update(updateRequest, esBaseModel.getClazz());
|
||||
log.info("Document updated: " + updateResponse.id());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -165,50 +173,218 @@ public class EsUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建查询条件
|
||||
* @param queryMap
|
||||
* 根据id查询文档
|
||||
* @param esBaseModel
|
||||
* @return
|
||||
*/
|
||||
public Query createBoolQuery(Map<String, Object> queryMap) {
|
||||
BoolQuery.Builder cQuery = new BoolQuery.Builder();
|
||||
for (Map.Entry<String, Object> entry : queryMap.entrySet()) {
|
||||
if (Objects.isNull(entry.getValue())) {
|
||||
continue;
|
||||
public static <T> T getDocumentById(EsBaseModel esBaseModel) {
|
||||
try {
|
||||
GetRequest getRequest = new GetRequest.Builder()
|
||||
.index(esBaseModel.getIndexName())
|
||||
.id(esBaseModel.getDocumentId())
|
||||
.build();
|
||||
GetResponse<T> getResponse = esClient.get(getRequest, esBaseModel.getClazz());
|
||||
if (getResponse.found()) {
|
||||
return getResponse.source();
|
||||
}
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value.getClass().isArray()) { //数组查询,使用 TermsQuery
|
||||
Object[] values = (Object[]) entry.getValue();
|
||||
List<FieldValue> objs = Arrays.stream(values)
|
||||
.map(v -> FieldValue.of(v)) // 将每个对象转换为 FieldValue
|
||||
.collect(Collectors.toList());
|
||||
cQuery.must(new TermsQuery.Builder()
|
||||
.field(key)
|
||||
.terms(t -> t.value(objs))
|
||||
} catch (Exception e) {
|
||||
log.error("es列表查询失败", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询文档列表
|
||||
* @param searchModel
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> getDocumentList(EsSearchModel searchModel) {
|
||||
List<T> eslist = new ArrayList<>();
|
||||
try {
|
||||
SearchResponse<T> search = esClient.search(buildSearchRequest(searchModel), searchModel.getClazz());
|
||||
if (Objects.isNull(search)) {
|
||||
return eslist;
|
||||
}
|
||||
HitsMetadata<T> hits = search.hits();
|
||||
if (Objects.isNull(hits)) {
|
||||
return eslist;
|
||||
}
|
||||
List<Hit<T>> sourceHitList = hits.hits();
|
||||
if (CollectionUtils.isEmpty(sourceHitList)) {
|
||||
return eslist;
|
||||
}
|
||||
sourceHitList.forEach(item -> {
|
||||
// 处理每个命中
|
||||
eslist.add(item.source());
|
||||
});
|
||||
return eslist;
|
||||
} catch (Exception e) {
|
||||
log.error("es列表查询失败", e);
|
||||
}
|
||||
return eslist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询文档数量
|
||||
* @param searchModel
|
||||
* @return
|
||||
*/
|
||||
public static long getDocumentCount(EsSearchModel searchModel) {
|
||||
try {
|
||||
CountRequest.Builder countRequest = new CountRequest.Builder();
|
||||
countRequest.index(searchModel.getIndexName());
|
||||
countRequest.query(createBoolQuery(searchModel.getTermQuery(), searchModel.getMatchQuery()));
|
||||
CountResponse count = esClient.count(countRequest.build());
|
||||
if (Objects.isNull(count)) {
|
||||
log.info("es列表数量查询异常{}", searchModel);
|
||||
return 0;
|
||||
}
|
||||
return count.count();
|
||||
} catch (Exception e) {
|
||||
log.error("es列表数量查询失败", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id删除文档
|
||||
* @param esBaseModel
|
||||
* @return
|
||||
*/
|
||||
public static Boolean deleteDocumentById(EsBaseModel esBaseModel) {
|
||||
try {
|
||||
DeleteRequest deleteRequest = new DeleteRequest.Builder()
|
||||
.index(esBaseModel.getDocumentId())
|
||||
.id(esBaseModel.getDocumentId())
|
||||
.build();
|
||||
DeleteResponse deleteResponse = esClient.delete(deleteRequest);
|
||||
if ("deleted".equals(deleteResponse.result())) {
|
||||
log.info("Document deleted: " + deleteResponse.id());
|
||||
return true;
|
||||
} else {
|
||||
log.info("Document delete failed: " + deleteResponse.id());
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("es列表删除失败", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据条件删除文档
|
||||
* @param searchModel
|
||||
* @return 删除数量
|
||||
*/
|
||||
public static long deleteDocumentByQuery(EsSearchModel searchModel) {
|
||||
try {
|
||||
DeleteByQueryRequest.Builder deleteRequest = new DeleteByQueryRequest.Builder();
|
||||
deleteRequest.index(searchModel.getIndexName());
|
||||
deleteRequest.query(createBoolQuery(searchModel.getTermQuery(), searchModel.getMatchQuery()));
|
||||
deleteRequest.refresh(true); //设置删除操作后是否立即刷新索引,使删除结果立即可见
|
||||
deleteRequest.timeout(new Time.Builder().time("2s").build()); //设置删除操作的超时时间
|
||||
deleteRequest.conflicts(Conflicts.Proceed); //Conflicts.Proceed:在版本冲突时继续删除操作;Conflicts.Abort:在版本冲突时中止删除操作
|
||||
DeleteByQueryResponse dResponse = esClient.deleteByQuery(deleteRequest.build());
|
||||
if (Objects.nonNull(dResponse)) {
|
||||
log.info("es条件删除成功,删除数量:{}", dResponse.deleted());
|
||||
return dResponse.deleted();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("es条件删除数据失败", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建搜索请求对象
|
||||
* @param searchModel
|
||||
* @return
|
||||
*/
|
||||
private static SearchRequest buildSearchRequest(EsSearchModel searchModel) {
|
||||
//定义查询对象
|
||||
SearchRequest.Builder searchRequest = new SearchRequest.Builder();
|
||||
//设置索引名称
|
||||
searchRequest.index(searchModel.getIndexName());
|
||||
//分组去重
|
||||
if (StringUtils.isNotBlank(searchModel.getRepeatField())) {
|
||||
searchRequest.collapse(buildCollapse(searchModel));
|
||||
}
|
||||
//设置查询条件
|
||||
searchRequest.query(createBoolQuery(searchModel.getTermQuery(), searchModel.getMatchQuery()));
|
||||
//设置排序规则
|
||||
if (searchModel.getSort() != null) {
|
||||
searchRequest.sort(buildSort(searchModel.getSort()));
|
||||
}
|
||||
//设置分页参数
|
||||
if (searchModel.getPageSize() != null && searchModel.getPageSize() != null) {
|
||||
searchRequest.from(searchModel.getPageSize() * (searchModel.getPageNum() - 1));
|
||||
searchRequest.size(searchModel.getPageSize());
|
||||
}
|
||||
//设置查询字段/排查字段
|
||||
SourceConfig sourceConfig = buildSourceConfig(searchModel.getIncludes(), searchModel.getExcludes());
|
||||
if (Objects.nonNull(sourceConfig)) {
|
||||
searchRequest.source(sourceConfig);
|
||||
}
|
||||
return searchRequest.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建查询条件
|
||||
* @param termQuery
|
||||
* @param matchQuery
|
||||
* @return
|
||||
*/
|
||||
private static Query createBoolQuery(Map<String, Object> termQuery, Map<String, Object> matchQuery) {
|
||||
BoolQuery.Builder cQuery = new BoolQuery.Builder();
|
||||
// TermQuery 精准匹配
|
||||
if (termQuery != null) {
|
||||
for (Map.Entry<String, Object> entry : termQuery.entrySet()) {
|
||||
if (Objects.isNull(entry.getValue())) {
|
||||
continue;
|
||||
}
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value.getClass().isArray()) { //数组查询,使用 TermsQuery
|
||||
Object[] values = (Object[]) entry.getValue();
|
||||
List<FieldValue> objs = Arrays.stream(values)
|
||||
.map(v -> FieldValue.of(v)) // 将每个对象转换为 FieldValue
|
||||
.collect(Collectors.toList());
|
||||
cQuery.must(new TermsQuery.Builder()
|
||||
.field(key)
|
||||
.terms(t -> t.value(objs))
|
||||
.build()
|
||||
._toQuery());
|
||||
} else if (value.toString().contains(" ")) { // 短语查询,使用 MatchPhraseQuery (要严格按照单词顺序字符串中有空格,短信需匹配)
|
||||
cQuery.must(new MatchPhraseQuery.Builder()
|
||||
.field(key)
|
||||
.query(value.toString())
|
||||
.build()
|
||||
._toQuery());
|
||||
} else { // 其他情况,使用 TermQuery 精准匹配
|
||||
cQuery.must(new TermQuery.Builder()
|
||||
.field(key)
|
||||
.value(value.toString())
|
||||
.build()
|
||||
._toQuery());
|
||||
}
|
||||
}
|
||||
}
|
||||
// MatchQuery 模糊匹配全文检索分词查询
|
||||
if (matchQuery != null) {
|
||||
for (Map.Entry<String, Object> entry : matchQuery.entrySet()) {
|
||||
if (Objects.isNull(entry.getValue())) {
|
||||
continue;
|
||||
}
|
||||
cQuery.must(new MatchQuery.Builder()
|
||||
.field(entry.getKey())
|
||||
.query(entry.getValue().toString())
|
||||
.build()
|
||||
._toQuery());
|
||||
} else if (value.toString().contains(" ")) { // 短语查询,使用 MatchPhraseQuery (要严格按照单词顺序字符串中有空格,短信需匹配)
|
||||
cQuery.must(new MatchPhraseQuery.Builder()
|
||||
.field(key)
|
||||
.query(value.toString())
|
||||
.build()
|
||||
._toQuery());
|
||||
} else { // 其他情况,使用 TermQuery 精准匹配
|
||||
cQuery.must(new TermQuery.Builder()
|
||||
.field(key)
|
||||
.value(value.toString())
|
||||
.build()
|
||||
._toQuery());
|
||||
// 使用 MatchQuery 模糊匹配全文检索分词查询
|
||||
// cQuery.must(new MatchQuery.Builder()
|
||||
// .field(entry.getKey())
|
||||
// .query(value.toString())
|
||||
// .build()
|
||||
// ._toQuery());
|
||||
}
|
||||
}
|
||||
return cQuery.build()._toQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建时间区间查询
|
||||
* @param startTime 开始时间
|
||||
@@ -216,7 +392,7 @@ public class EsUtil {
|
||||
* @param fieldName 时间字段
|
||||
* @return
|
||||
*/
|
||||
public Query createTimeQuery(String startTime, String endTime, String fieldName) {
|
||||
public static Query createTimeQuery(String startTime, String endTime, String fieldName) {
|
||||
DateRangeQuery dataQuery = new DateRangeQuery.Builder()
|
||||
.field(fieldName)
|
||||
.build();
|
||||
@@ -227,6 +403,67 @@ public class EsUtil {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置查询字段/排查字段
|
||||
* @param includes 需要字段
|
||||
* @param excludes 排除字段
|
||||
* @return
|
||||
*/
|
||||
private static SourceConfig buildSourceConfig(List<String> includes, List<String> excludes) {
|
||||
boolean isIncludes = CollectionUtils.isEmpty(includes);
|
||||
boolean isExcludes = CollectionUtils.isEmpty(excludes);
|
||||
//设置查询字段/排查字段
|
||||
if (isIncludes || isExcludes) {
|
||||
SourceFilter.Builder sourceFilter = new SourceFilter.Builder();
|
||||
if (isIncludes)
|
||||
sourceFilter.includes(includes);
|
||||
if (isExcludes)
|
||||
sourceFilter.excludes(excludes);
|
||||
return new SourceConfig.Builder().filter(sourceFilter.build()).build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建分组去重
|
||||
* @param searchModel
|
||||
* @return
|
||||
*/
|
||||
private static FieldCollapse buildCollapse(EsSearchModel searchModel) {
|
||||
FieldCollapse.Builder fieldCollapse = new FieldCollapse.Builder();
|
||||
//设置分组字段
|
||||
fieldCollapse.field(searchModel.getRepeatField());
|
||||
//设置嵌套配置
|
||||
if (StringUtils.isNotBlank(searchModel.getInnerAlias())) {
|
||||
InnerHits.Builder innerHits = new InnerHits.Builder();
|
||||
//设置别名
|
||||
innerHits.name(searchModel.getInnerAlias());
|
||||
//设置查询数量
|
||||
if (searchModel.getInnerSize() != null) {
|
||||
innerHits.size(searchModel.getInnerSize());
|
||||
}
|
||||
fieldCollapse.innerHits(InnerHits.of(i -> i.name(searchModel.getInnerAlias()).size(10)));
|
||||
}
|
||||
return fieldCollapse.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建排序规则
|
||||
* @param sortMap
|
||||
* @return
|
||||
*/
|
||||
private static List<SortOptions> buildSort(Map<String, String> sortMap) {
|
||||
if (sortMap == null) {
|
||||
return null;
|
||||
}
|
||||
List<SortOptions> sortList = new ArrayList<>();
|
||||
for (Map.Entry<String, String> sort : sortMap.entrySet()) {
|
||||
sortList.add(new SortOptions.Builder().field(f -> f.field(sort.getKey()).order(SortOrder.valueOf(sort.getValue()))).build());
|
||||
}
|
||||
return sortList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 案例:组合多条件查询(关于 must、mustNot、should 条件的使用)
|
||||
*/
|
||||
|
||||
29
src/main/java/cn/xf/basedemo/common/utils/StringUtil.java
Normal file
29
src/main/java/cn/xf/basedemo/common/utils/StringUtil.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package cn.xf.basedemo.common.utils;
|
||||
|
||||
/**
|
||||
* packageName cn.xf.basedemo.common.utils
|
||||
* @author remaindertime
|
||||
* @className StringUtil
|
||||
* @date 2024/12/11
|
||||
* @description 字符串工具类
|
||||
*/
|
||||
public class StringUtil {
|
||||
|
||||
/**
|
||||
* 驼峰命名法转下划线命名法
|
||||
*
|
||||
* @param camelCase 驼峰命名法字符串
|
||||
* @return 下划线命名法字符串
|
||||
*/
|
||||
public static String camelToKebabCase(String camelCase) {
|
||||
if (camelCase == null || camelCase.isEmpty()) {
|
||||
return camelCase;
|
||||
}
|
||||
|
||||
// 使用正则表达式将大写字母前插入一个"-"
|
||||
String result = camelCase.replaceAll("([a-z])([A-Z])", "$1-$2");
|
||||
|
||||
// 转换为小写
|
||||
return result.toLowerCase();
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,6 @@ 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.apache.http.impl.nio.reactor.IOReactorConfig;
|
||||
import org.elasticsearch.client.Node;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.elasticsearch.client.RestClientBuilder;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -25,7 +23,7 @@ import org.springframework.stereotype.Component;
|
||||
* @description es工具类
|
||||
*/
|
||||
@Component
|
||||
public class ElasticsearchConfig {
|
||||
public class EsConfig {
|
||||
|
||||
@Value("${elasticsearch.host}")
|
||||
private String elasticsearchHost;
|
||||
@@ -52,43 +50,19 @@ public class ElasticsearchConfig {
|
||||
AuthScope.ANY,
|
||||
new UsernamePasswordCredentials(username, password)
|
||||
);
|
||||
|
||||
// 自定义 RestClientBuilder 配置
|
||||
RestClientBuilder restClientBuilder = RestClient.builder(
|
||||
new HttpHost(elasticsearchHost, elasticsearchPort, "http") // Elasticsearch 节点地址
|
||||
new HttpHost(elasticsearchHost, elasticsearchPort, "http")
|
||||
).setHttpClientConfigCallback(httpClientBuilder ->
|
||||
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider) // 配置认证信息
|
||||
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider) // 配置认证信息
|
||||
);
|
||||
|
||||
// 配置异步连接数和线程数
|
||||
restClientBuilder.setHttpClientConfigCallback(httpAsyncClientBuilder ->
|
||||
httpAsyncClientBuilder
|
||||
// 设置最大连接数和每个节点的最大连接数
|
||||
.setMaxConnTotal(1200) // 总连接数:最多可以与所有节点同时保持 1200 个连接
|
||||
.setMaxConnPerRoute(100) // 每个节点最大连接数:对于每个节点允许最多 100 个并发连接
|
||||
// 设置 IO 线程数
|
||||
.setDefaultIOReactorConfig(IOReactorConfig.custom()
|
||||
.setIoThreadCount(16) // IO 线程数设置为 16,通常为 CPU 核心数的 2-4 倍
|
||||
.build())
|
||||
);
|
||||
|
||||
// 配置连接超时、套接字超时、获取连接超时
|
||||
restClientBuilder.setRequestConfigCallback(builder ->
|
||||
builder
|
||||
.setConnectTimeout(20000) // 连接超时:连接建立超时时间为 20 秒
|
||||
.setSocketTimeout(20000) // 套接字超时:请求超时,通常是 20 秒
|
||||
.setConnectionRequestTimeout(20000) // 获取连接超时:20 秒
|
||||
builder.setConnectTimeout(20000)
|
||||
.setSocketTimeout(20000)
|
||||
.setConnectionRequestTimeout(20000)
|
||||
);
|
||||
|
||||
// 配置失败监听器,自定义重试逻辑
|
||||
restClientBuilder.setFailureListener(new RestClient.FailureListener() {
|
||||
@Override
|
||||
public void onFailure(Node node) {
|
||||
// 可自定义错误处理逻辑,如重试或记录错误
|
||||
System.out.println("Elasticsearch node failure detected: " + node);
|
||||
super.onFailure(node); // 默认的失败处理
|
||||
}
|
||||
});
|
||||
|
||||
// 创建 RestClientTransport 和 ElasticsearchClient
|
||||
RestClient restClient = restClientBuilder.build();
|
||||
ElasticsearchTransport transport = new RestClientTransport(
|
||||
@@ -96,7 +70,6 @@ public class ElasticsearchConfig {
|
||||
new JacksonJsonpMapper() // 使用 Jackson 进行 JSON 处理
|
||||
);
|
||||
|
||||
// 返回 ElasticsearchClient 实例
|
||||
return new ElasticsearchClient(transport);
|
||||
}
|
||||
|
||||
@@ -6,14 +6,9 @@ import cn.xf.basedemo.interceptor.SessionContext;
|
||||
import cn.xf.basedemo.model.res.LoginInfoRes;
|
||||
import cn.xf.basedemo.service.UserService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springdoc.core.annotations.RouterOperations;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/**
|
||||
* @program: xf-boot-base
|
||||
@@ -43,4 +38,17 @@ public class UserController {
|
||||
LoginUser loginUser = SessionContext.getInstance().get();
|
||||
return RetObj.success(loginUser);
|
||||
}
|
||||
|
||||
@Operation(summary = "es同步用户信息", description = "用户信息")
|
||||
@GetMapping("/syncEs")
|
||||
public RetObj syncEs(Long userId){
|
||||
return userService.syncEs(userId);
|
||||
}
|
||||
|
||||
@Operation(summary = "es查询用户信息", description = "用户信息")
|
||||
@GetMapping("/getEsId")
|
||||
public RetObj getEsId(Long userId){
|
||||
return userService.getEsId(userId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,4 +13,8 @@ import cn.xf.basedemo.model.res.LoginInfoRes;
|
||||
public interface UserService {
|
||||
|
||||
RetObj login(LoginInfoRes res);
|
||||
|
||||
RetObj syncEs(Long userId);
|
||||
|
||||
RetObj getEsId(Long userId);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package cn.xf.basedemo.service.impl;
|
||||
|
||||
import cn.xf.basedemo.common.model.EsBaseModel;
|
||||
import cn.xf.basedemo.common.model.LoginInfo;
|
||||
import cn.xf.basedemo.common.model.LoginUser;
|
||||
import cn.xf.basedemo.common.model.RetObj;
|
||||
import cn.xf.basedemo.common.utils.EsUtil;
|
||||
import cn.xf.basedemo.common.utils.JwtTokenUtils;
|
||||
import cn.xf.basedemo.common.utils.RSAUtils;
|
||||
import cn.xf.basedemo.common.utils.StringUtil;
|
||||
import cn.xf.basedemo.config.GlobalConfig;
|
||||
import cn.xf.basedemo.mappers.UserMapper;
|
||||
import cn.xf.basedemo.model.domain.User;
|
||||
@@ -90,4 +93,27 @@ public class UserServiceImpl implements UserService {
|
||||
|
||||
return RetObj.success(loginUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RetObj syncEs(Long userId) {
|
||||
User user = userMapper.selectById(userId);
|
||||
if (Objects.isNull(user)) {
|
||||
return RetObj.error("用户不存在");
|
||||
}
|
||||
String index = StringUtil.camelToKebabCase(user.getClass().getSimpleName());
|
||||
if (!EsUtil.existIndex(index)) {
|
||||
EsUtil.createIndex(index);
|
||||
}
|
||||
EsUtil.addDocument(new EsBaseModel(index, String.valueOf(user.getId()), user, user.getClass()));
|
||||
return RetObj.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RetObj getEsId(Long userId) {
|
||||
Object user = EsUtil.getDocumentById(new EsBaseModel("user", String.valueOf(userId), null, User.class));
|
||||
if(Objects.nonNull(user)){
|
||||
return RetObj.success(user);
|
||||
}
|
||||
return RetObj.error("es中不存在该用户");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,9 @@ mybatis-plus:
|
||||
|
||||
# 参考文章 https://zhuanlan.zhihu.com/p/145359625
|
||||
management:
|
||||
health:
|
||||
elasticsearch: #禁用健康检查
|
||||
enabled: false
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
var json = JSON.stringify(data);
|
||||
var cipher = this.encryptByPublicKey(json);
|
||||
console.log("密文 :" + cipher);
|
||||
var url = "http://117.72.35.70:8089/user/login";
|
||||
var url = "http://localhost:8089/user/login";
|
||||
axios.post(url, {
|
||||
encryptedData: cipher,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user