|
@@ -1,8 +1,11 @@
|
|
|
package com.ruoyi.framework.config;
|
|
|
|
|
|
+import cn.hutool.core.util.ObjectUtil;
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
+import com.ruoyi.common.constant.DefineConstant;
|
|
|
import com.ruoyi.framework.config.properties.ElasticSearchConfig;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
import org.apache.http.HttpHost;
|
|
|
import org.elasticsearch.action.ActionListener;
|
|
|
import org.elasticsearch.action.DocWriteRequest;
|
|
@@ -34,24 +37,25 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
|
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
|
|
import org.elasticsearch.common.xcontent.XContentType;
|
|
|
import org.elasticsearch.core.TimeValue;
|
|
|
-import org.elasticsearch.index.query.BoolQueryBuilder;
|
|
|
-import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
|
|
|
-import org.elasticsearch.index.query.MultiMatchQueryBuilder;
|
|
|
-import org.elasticsearch.index.query.QueryBuilders;
|
|
|
+import org.elasticsearch.index.query.*;
|
|
|
import org.elasticsearch.search.SearchHit;
|
|
|
import org.elasticsearch.search.SearchHits;
|
|
|
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
|
|
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
|
|
|
import org.elasticsearch.search.sort.FieldSortBuilder;
|
|
|
+import org.elasticsearch.search.sort.SortBuilder;
|
|
|
+import org.elasticsearch.search.sort.SortBuilders;
|
|
|
import org.elasticsearch.search.sort.SortOrder;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.context.annotation.Bean;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
import org.springframework.util.CollectionUtils;
|
|
|
+import org.springframework.util.MultiValueMap;
|
|
|
|
|
|
import javax.annotation.PostConstruct;
|
|
|
import java.io.IOException;
|
|
|
import java.util.ArrayList;
|
|
|
+import java.util.Collection;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
@@ -342,111 +346,234 @@ public class ElasticSearchClient {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 检索、分页
|
|
|
- *
|
|
|
- * @param indexName 索引名称
|
|
|
- * @param mpParams 查询参数
|
|
|
- * @param from 起始页
|
|
|
- * @param size 每页数量
|
|
|
- * @param fields 返回列
|
|
|
- * @param preciseQuery 1:精确查询 2:模糊查询
|
|
|
+ * @param indexName (索引名)也可以类似的说成表名
|
|
|
+ * @param equalsCondition 关键字等值条件
|
|
|
+ * 若是一个字符串以%结尾,则匹配以去掉%的字符串开头的记录
|
|
|
+ * 若是一个字符串以*开头或结尾,则模糊匹配去掉*的记录 类似于sql中的like '%str%'
|
|
|
+ * 若传入的是一个普通的字符串,则等值查询
|
|
|
+ * 若传入的是一个集合,则使用的是in条件查询
|
|
|
+ * @param rangeCondition 条件范围查询
|
|
|
+ * 字段,字段对应值的区间,区间格式[,]/(,)/[,)/(,],逗号的左右可以没值
|
|
|
+ * @param orderBy 排序字段
|
|
|
+ * 若是字段以中划线-开头,则使用降序排序,类似于sql中的desc
|
|
|
+ * 若正常字段排序,则使用增序排序,类似于sql中的asc
|
|
|
+ * @param pageNum 页数
|
|
|
+ * @param pageSize 每页大小
|
|
|
* @return
|
|
|
+ * @Description ElasticSearch条件查询
|
|
|
*/
|
|
|
- public List<Map<String, Object>> searchDocument(String indexName, Map<String, Object> mpParams,
|
|
|
- int from, int size, String fields, String sorts, Integer preciseQuery) {
|
|
|
- SearchRequest searchRequest = new SearchRequest(indexName);
|
|
|
- // 大多数搜索参数添加到searchSourceBuilder
|
|
|
- SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
|
|
|
- List<Map<String, Object>> mapList = new ArrayList<>();
|
|
|
- try {
|
|
|
- // 组合字段查询
|
|
|
- BoolQueryBuilder boolQueryBuilder = this.getBoolQueryBuilder(mpParams, preciseQuery);
|
|
|
- searchSourceBuilder.query(boolQueryBuilder);
|
|
|
- // 自定义返回列
|
|
|
- if (StrUtil.isNotBlank(fields)) {
|
|
|
- searchSourceBuilder.fetchSource(new FetchSourceContext(true, fields.split(","), Strings.EMPTY_ARRAY));
|
|
|
+ public Map<String, Object> searchDocument(String indexName,
|
|
|
+ Map<String, Object> equalsCondition,
|
|
|
+ Map<String, Object> rangeCondition,
|
|
|
+ List<String> orderBy,
|
|
|
+ Integer pageNum,
|
|
|
+ Integer pageSize) {
|
|
|
+ Map<String, Object> resultMap = new HashMap<>(8);
|
|
|
+ List<Map<String, Object>> queryResult = new ArrayList<>();
|
|
|
+ long totalNum = 0;
|
|
|
+ SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
|
|
|
+ BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
|
|
|
+ // and 等值查询
|
|
|
+ // 某一field=具体的值; 也可以某一field 的值 in 具体定义集合里的值
|
|
|
+ if (null != equalsCondition && !equalsCondition.isEmpty()) {
|
|
|
+ for (Map.Entry<String, Object> entry : equalsCondition.entrySet()) {
|
|
|
+ String key = entry.getKey();
|
|
|
+ //由于我创建索引的时候使用字符串不分词使用的.keyword类型
|
|
|
+ if (key.endsWith("_s")) {
|
|
|
+ queryValueBuild(boolQueryBuilder, key + ".keyword", entry.getValue());
|
|
|
+ } else {
|
|
|
+ queryValueBuild(boolQueryBuilder, key, entry.getValue());
|
|
|
+ }
|
|
|
}
|
|
|
- // 排序
|
|
|
- if (StrUtil.isNotBlank(sorts)) {
|
|
|
- searchSourceBuilder.sort(sorts + ".keyword", SortOrder.DESC);
|
|
|
+ }
|
|
|
+ //范围查询
|
|
|
+ if (null != rangeCondition && !rangeCondition.isEmpty()) {
|
|
|
+ rangeValueBuild(boolQueryBuilder, rangeCondition);
|
|
|
+ }
|
|
|
+ sourceBuilder.query(boolQueryBuilder);
|
|
|
+ //排序
|
|
|
+ if (null != orderBy && !orderBy.isEmpty()) {
|
|
|
+ buildSort(sourceBuilder, orderBy);
|
|
|
+ }
|
|
|
+ //分页(es分页查询默认是查询返回10条记录,而深度分页,默认是10000条数据,也就是一次性最多返回10000条,设置size就可以实现,但是如果实际数据量特别大,可以使用scroll游标查询,此处主要常规分页查询)
|
|
|
+ if (ObjectUtil.isNotEmpty(pageNum)) {
|
|
|
+ pageSize = pageSize == null ? 10000 : pageSize;
|
|
|
+ if (pageNum > 0) {
|
|
|
+ sourceBuilder.from(pageSize * (pageNum - 1));
|
|
|
+ } else {
|
|
|
+ sourceBuilder.from(0);
|
|
|
}
|
|
|
- // 分页
|
|
|
- searchSourceBuilder.from(from);
|
|
|
- searchSourceBuilder.size(size);
|
|
|
- // 允许搜索的超时时长,10s
|
|
|
- searchSourceBuilder.timeout(new TimeValue(elasticSearchConfig.getSearchTimeOut(), TimeUnit.SECONDS));
|
|
|
- searchRequest.source(searchSourceBuilder);
|
|
|
- SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
|
|
|
- // 返回结果
|
|
|
- SearchHits searchHitArray = searchResponse.getHits();
|
|
|
- for (SearchHit searchHit : searchHitArray.getHits()) {
|
|
|
- Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
|
|
|
- mapList.add(sourceAsMap);
|
|
|
+ sourceBuilder.size(pageSize);
|
|
|
+ }
|
|
|
+
|
|
|
+ //执行查询
|
|
|
+ SearchResponse response = executeSearch(indexName, sourceBuilder);
|
|
|
+ SearchHits searchHits = response.getHits();
|
|
|
+ SearchHit[] hits = searchHits.getHits();
|
|
|
+ totalNum = searchHits.getTotalHits().value;
|
|
|
+ for (SearchHit hit : hits) {
|
|
|
+ Map<String, Object> sourceMap = hit.getSourceAsMap();
|
|
|
+ sourceMap.put("id_s", hit.getId());
|
|
|
+ queryResult.add(sourceMap);
|
|
|
+ }
|
|
|
+ resultMap.put("pageList", queryResult);
|
|
|
+ resultMap.put("totalNum", totalNum);
|
|
|
+ resultMap.put("pageNum", pageNum);
|
|
|
+ resultMap.put("pageSize", pageSize);
|
|
|
+ return resultMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param boolQueryBuilder
|
|
|
+ * @param key
|
|
|
+ * @param value
|
|
|
+ * @Description 查询条件组装
|
|
|
+ */
|
|
|
+ private static void queryValueBuild(BoolQueryBuilder boolQueryBuilder, String key, Object value) {
|
|
|
+ TermQueryBuilder termQueryBuilder;
|
|
|
+ if (null != value && !"".equals(value)) {
|
|
|
+ if (value instanceof String) {
|
|
|
+ String strValue = (String) value;
|
|
|
+ if (strValue.endsWith("%")) {
|
|
|
+ PrefixQueryBuilder prefixQueryBuilder = QueryBuilders.prefixQuery(key, strValue.replace("%", ""));
|
|
|
+ boolQueryBuilder.must(prefixQueryBuilder);
|
|
|
+ } else if (strValue.startsWith("*") && strValue.endsWith("*")) {
|
|
|
+ WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery(key, strValue);
|
|
|
+ boolQueryBuilder.must(wildcardQueryBuilder);
|
|
|
+ } else if (strValue.startsWith("*") || strValue.endsWith("*")) {
|
|
|
+ MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(key, strValue.replace("*", ""));
|
|
|
+ boolQueryBuilder.must(matchQueryBuilder);
|
|
|
+ } else {
|
|
|
+ termQueryBuilder = QueryBuilders.termQuery(key, strValue);
|
|
|
+ boolQueryBuilder.must(termQueryBuilder);
|
|
|
+ }
|
|
|
+ } else if (value instanceof Collection) {
|
|
|
+ Collection<? extends Object> collectionValue = (Collection<? extends Object>) value;
|
|
|
+ //此处使用了多值条件
|
|
|
+ boolQueryBuilder.must(QueryBuilders.termsQuery(key, collectionValue));
|
|
|
+ } else {
|
|
|
+ termQueryBuilder = QueryBuilders.termQuery(key, value);
|
|
|
+ boolQueryBuilder.must(termQueryBuilder);
|
|
|
}
|
|
|
- } catch (Exception e) {
|
|
|
- e.printStackTrace();
|
|
|
}
|
|
|
- return mapList;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * @param boolQueryBuilder
|
|
|
+ * @param rangeCondition
|
|
|
+ * @Description 范围条件查询组装
|
|
|
+ */
|
|
|
+ private static void rangeValueBuild(BoolQueryBuilder boolQueryBuilder, Map<String, Object> rangeCondition) {
|
|
|
+ for (Map.Entry<String, Object> entry : rangeCondition.entrySet()) {
|
|
|
+ Map<String, Object> range = intervalParse((String) entry.getValue());
|
|
|
+ String key = entry.getKey();
|
|
|
+ RangeQueryBuilder rangeQueryBuilder;
|
|
|
+ if (key.endsWith("_s")) {
|
|
|
+ rangeQueryBuilder = QueryBuilders.rangeQuery(key + ".keyword");
|
|
|
+ } else {
|
|
|
+ rangeQueryBuilder = QueryBuilders.rangeQuery(key);
|
|
|
+ }
|
|
|
+ if (!ObjectUtil.isEmpty(range.get("leftValue"))) {
|
|
|
+ if (DefineConstant.INTERVAL_OPEN_VALUE.equals(range.get("leftType"))) {
|
|
|
+ rangeQueryBuilder.from(range.get("leftValue"), false);
|
|
|
+ } else if (DefineConstant.INTERVAL_CLOSE_VALUE.equals(range.get("leftType"))) {
|
|
|
+ rangeQueryBuilder.from(range.get("leftValue"), true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!ObjectUtil.isEmpty(range.get("rightValue"))) {
|
|
|
+ if (DefineConstant.INTERVAL_OPEN_VALUE.equals(range.get("rightType"))) {
|
|
|
+ rangeQueryBuilder.to(range.get("rightValue"), false);
|
|
|
+ } else if (DefineConstant.INTERVAL_CLOSE_VALUE.equals(range.get("rightType"))) {
|
|
|
+ rangeQueryBuilder.to(range.get("rightValue"), true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ boolQueryBuilder.must(rangeQueryBuilder);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
- * 组合字段查询条件
|
|
|
- *
|
|
|
- * @param mpParams
|
|
|
- * @param preciseQuery 1:精确查询 2:模糊查询 3.匹配多个字段的模糊查询(使用IK中文分词器)
|
|
|
+ * @param interval
|
|
|
* @return
|
|
|
+ * @Description 区间解析:[,]/(,)/[,)/(,]
|
|
|
*/
|
|
|
- public BoolQueryBuilder getBoolQueryBuilder(Map<String, Object> mpParams, Integer preciseQuery) {
|
|
|
- BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
|
|
|
- try {
|
|
|
- if (null != mpParams.get("search") && null != mpParams.get("fields")) {
|
|
|
- if (mpParams.get("fields") instanceof List) {
|
|
|
- String[] fields = (String[]) ((List) mpParams.get("fields")).
|
|
|
- toArray(new String[((List) mpParams.get("fields")).size()]);
|
|
|
- MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(mpParams.get("search"), fields);
|
|
|
- boolQueryBuilder = boolQueryBuilder.must(multiMatchQueryBuilder);
|
|
|
+ private static Map<String, Object> intervalParse(String interval) {
|
|
|
+ Map<String, Object> range = new HashMap<>();
|
|
|
+ if (interval.startsWith(DefineConstant.INTERVAL_CLOSE_LEFT)) {
|
|
|
+ range.put("leftType", DefineConstant.INTERVAL_CLOSE_VALUE);
|
|
|
+ } else if (interval.startsWith(DefineConstant.INTERVAL_OPEN_LEFT)) {
|
|
|
+ range.put("leftType", DefineConstant.INTERVAL_OPEN_VALUE);
|
|
|
+ } else {
|
|
|
+ log.error("区间参数格式错误:{}", interval);
|
|
|
+ //若实际业务相关需要,抛出异常处理throw new Exception();
|
|
|
+ }
|
|
|
+ if (interval.endsWith(DefineConstant.INTERVAL_CLOSE_RIGHT)) {
|
|
|
+ range.put("rightType", DefineConstant.INTERVAL_CLOSE_VALUE);
|
|
|
+ } else if (interval.startsWith(DefineConstant.INTERVAL_OPEN_RIGHT)) {
|
|
|
+ range.put("rightType", DefineConstant.INTERVAL_OPEN_VALUE);
|
|
|
+ } else {
|
|
|
+ log.error("区间参数格式错误:{}", interval);
|
|
|
+ //若实际业务相关需要,抛出异常处理throw new Exception();
|
|
|
+ }
|
|
|
+ int strLen = interval.length();
|
|
|
+ String[] lr = interval.substring(1, strLen - 1).split(DefineConstant.COMMAN_SIGN, 2);
|
|
|
+ if (lr.length > 0) {
|
|
|
+ range.put("leftValue", lr[0]);
|
|
|
+ }
|
|
|
+ if (lr.length > 1) {
|
|
|
+ range.put("rightValue", lr[1]);
|
|
|
+ }
|
|
|
+ return range;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param sourceBuilder
|
|
|
+ * @param orderBy
|
|
|
+ * @Description 查询排序
|
|
|
+ */
|
|
|
+ private static void buildSort(SearchSourceBuilder sourceBuilder, List<String> orderBy) {
|
|
|
+ SortBuilder<FieldSortBuilder> sortBuilder;
|
|
|
+ for (String sortField : orderBy) {
|
|
|
+ if (sortField.startsWith("-")) {
|
|
|
+ //降序排序
|
|
|
+ if (sortField.endsWith("_s")) {
|
|
|
+ sortBuilder = SortBuilders.fieldSort(sortField.replace("-", "") + ".keyword").order(SortOrder.DESC);
|
|
|
+ } else {
|
|
|
+ sortBuilder = SortBuilders.fieldSort(sortField.replace("-", "")).order(SortOrder.DESC);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ //升序排序
|
|
|
+ if (sortField.endsWith("_s")) {
|
|
|
+ sortBuilder = SortBuilders.fieldSort(sortField.replace("-", "") + ".keyword").order(SortOrder.ASC);
|
|
|
+ } else {
|
|
|
+ sortBuilder = SortBuilders.fieldSort(sortField.replace("-", "")).order(SortOrder.ASC);
|
|
|
}
|
|
|
}
|
|
|
-// if (mpParams != null) {
|
|
|
-//
|
|
|
-// if (preciseQuery != null && preciseQuery == 1) {
|
|
|
-// for (Map.Entry<String, Object> entry : mpParams.entrySet()) {
|
|
|
-// // 精确匹配
|
|
|
-// TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery(entry.getKey() + ".keyword", entry.getValue());
|
|
|
-// boolQueryBuilder = boolQueryBuilder.must(termQueryBuilder);
|
|
|
-// }
|
|
|
-//
|
|
|
-// }
|
|
|
-// } else if (preciseQuery != null && preciseQuery == 2) {
|
|
|
-// for (Map.Entry<String, Object> entry : mpParams.entrySet()) {
|
|
|
-// // 模糊匹配
|
|
|
-// MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(entry.getKey(), entry.getValue());
|
|
|
-// boolQueryBuilder = boolQueryBuilder.must(matchQueryBuilder);
|
|
|
-// }
|
|
|
-//
|
|
|
-// } else if (preciseQuery != null && preciseQuery == 3) {
|
|
|
-// //多词模糊匹配
|
|
|
-//// List<Object> values = ((MultiValueMap) mpParams).get("fields");
|
|
|
-//
|
|
|
-// /* for (String value : values) {
|
|
|
-//
|
|
|
-// log.info(key + ": " + value);
|
|
|
-//
|
|
|
-// }*/
|
|
|
-// ((MultiValueMap) mpParams).entrySet().forEach(entry -> {
|
|
|
-//
|
|
|
-// MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("新品", entry.).minimumShouldMatch("50%");
|
|
|
-// boolQueryBuilder = boolQueryBuilder.must(multiMatchQueryBuilder);
|
|
|
-// });
|
|
|
-//
|
|
|
-// }
|
|
|
- } catch (Exception e) {
|
|
|
- e.printStackTrace();
|
|
|
+ sourceBuilder.sort(sortBuilder);
|
|
|
}
|
|
|
- return boolQueryBuilder;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * @param indexName 对应的es的index名
|
|
|
+ * @param sourceBuilder
|
|
|
+ * @return
|
|
|
+ * @Description 执行查询
|
|
|
+ */
|
|
|
+ private static SearchResponse executeSearch(String indexName, SearchSourceBuilder sourceBuilder) {
|
|
|
+ // 获取不同系统的换行符
|
|
|
+ String lineSeparator = System.lineSeparator();
|
|
|
+ log.info(lineSeparator + "ES查询:index:" + indexName + lineSeparator + "search:" + sourceBuilder.toString() + lineSeparator);
|
|
|
+ SearchRequest searchRequest = new SearchRequest(indexName);
|
|
|
+ SearchResponse response = null;
|
|
|
+ searchRequest.source(sourceBuilder);
|
|
|
+ try {
|
|
|
+ response = client.search(searchRequest, RequestOptions.DEFAULT);
|
|
|
+ log.info("search status:{}, totalNum:{}", response.status(), response.getHits().getTotalHits());
|
|
|
+ } catch (IOException e) {
|
|
|
+ //异常处理,实际业务中是需要根据需求具体处理,自定义异常捕获
|
|
|
+ log.error(e.getMessage());
|
|
|
+ }
|
|
|
+ return response;
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
* 索引创建前,判断索引是否存在
|