v2.0.0-beta5

1.嵌套类型支持高亮注解
2.增加小黑子趣味debug模式
3.修复已知缺陷
4.持续更新中...
This commit is contained in:
邢鹏成 2023-10-30 22:20:47 +08:00
parent db8c94495e
commit 095a7bb2b2
24 changed files with 407 additions and 94 deletions

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.dromara.easy-es</groupId>
<artifactId>easy-es-parent</artifactId>
<version>2.0.0-beta4</version>
<version>2.0.0-beta5</version>
<relativePath>../easy-es-parent</relativePath>
</parent>

View File

@ -9,7 +9,7 @@ import java.util.Arrays;
**/
public enum FieldType {
/**
* none Required inside the framework, do not use 框架内部需要,切勿使用,若不慎使用则会被当做keyword&text类型
* none Required inside the framework, do not use 框架内部需要,切勿使用,若不慎使用则会被当做keyword_text类型
*/
NONE("none"),
/**

View File

@ -5,7 +5,7 @@
<parent>
<groupId>org.dromara.easy-es</groupId>
<artifactId>easy-es-parent</artifactId>
<version>2.0.0-beta4</version>
<version>2.0.0-beta5</version>
<relativePath>../easy-es-parent</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -18,8 +18,7 @@ import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.dromara.easyes.common.constants.BaseEsConstants.ENABLE_BANNER;
import static org.dromara.easyes.common.constants.BaseEsConstants.ENABLE_PREFIX;
import static org.dromara.easyes.common.constants.BaseEsConstants.*;
/**
* 注册bean
@ -46,19 +45,50 @@ public class MapperScannerRegister implements ImportBeanDefinitionRegistrar, Res
//打印banner @author dazer007
boolean banner = Optional.ofNullable(environment.getProperty(ENABLE_BANNER)).map(Boolean::parseBoolean).orElse(Boolean.TRUE);
if (banner) {
boolean iKunMode = Optional.ofNullable(environment.getProperty(ENABLE_I_KUN_MODE)).map(Boolean::parseBoolean).orElse(Boolean.FALSE);
String versionStr = EEVersionUtils.getJarVersion(this.getClass());
System.out.println("\n" +
"___ _ _ ___\n" +
" | __| __ _ ___ | || | ___ | __| ___\n" +
" | _| / _` | (_-< \\_, | |___| | _| (_-<\n" +
" |___| \\__,_| /__/_ _|__/ _____ |___| /__/_\n" +
"_|\"\"\"\"\"|_|\"\"\"\"\"|_|\"\"\"\"\"|_| \"\"\"\"|_| |_|\"\"\"\"\"|_|\"\"\"\"\"|\n" +
"\"`-0-0-'\"`-0-0-'\"`-0-0-'\"`-0-0-'\"`-0-0-'\"`-0-0-'\"`-0-0-'\n" +
"------------------------------------------------------>"
);
String wechatStr = ":: wechat :: 252645816, add and become muscle man! >";
if (iKunMode) {
System.out.println(" 鸡你太美\n" +
" 鸡你实在太美\n" +
" 鸡你是太美\n" +
" 鸡你太美\n" +
" 实在是太美鸡你\n" +
" 鸡你 实在是太美鸡你 美\n" +
" 鸡你 实在是太美鸡美 太美\n" +
" 鸡你 实在是太美鸡美 太美\n" +
" 鸡你 实在是太美鸡美 太美\n" +
" 鸡你 鸡你实在是美太美 美蓝球球球\n" +
"鸡 鸡 鸡你实在是太美 篮球篮球球球球\n" +
" 鸡 鸡你太美裆鸡太啊 球球蓝篮球球\n" +
" 鸡你太美裆裆鸡美 球球球\n" +
" 鸡你裆小 j 鸡太美\n" +
" 鸡太美 鸡太美\n" +
" 鸡美 鸡美\n" +
" 鸡美 鸡美\n" +
" 鸡美 鸡美\n" +
" 鸡太 鸡太\n" +
" 鸡 脚 鸡脚\n" +
" 皮 鞋 皮鞋金猴\n" +
" 金光 金光 大道\n" +
" 大道\n" +
" 鸡神保佑 永不宕机 永无BUG");
wechatStr = ":: wechat :: 252645816, add and join ikun(小黑子) group! >";
} else {
System.out.println("\n" +
"___ _ _ ___\n" +
" | __| __ _ ___ | || | ___ | __| ___\n" +
" | _| / _` | (_-< \\_, | |___| | _| (_-<\n" +
" |___| \\__,_| /__/_ _|__/ _____ |___| /__/_\n" +
"_|\"\"\"\"\"|_|\"\"\"\"\"|_|\"\"\"\"\"|_| \"\"\"\"|_| |_|\"\"\"\"\"|_|\"\"\"\"\"|\n" +
"\"`-0-0-'\"`-0-0-'\"`-0-0-'\"`-0-0-'\"`-0-0-'\"`-0-0-'\"`-0-0-'\n" +
"----------------------------------------------------------->"
);
}
// 版本长度并不固定,比如beta版,所以需要特殊处理
int width = 38;
int width = 43;
int blank = width - versionStr.length();
StringBuilder sb = new StringBuilder();
sb.append(":: version :: ")
@ -67,11 +97,15 @@ public class MapperScannerRegister implements ImportBeanDefinitionRegistrar, Res
sb.append(" ");
}
sb.append(">");
if (iKunMode) {
System.out.println("----------------------------------------------------------->");
}
System.out.println(":: project :: Easy-Es >");
System.out.println(sb);
System.out.println(":: home :: https://easy-es.cn/ >");
System.out.println(":: community :: https://dromara.org/ >");
System.out.println(":: wechat :: 252645816, add and become muscle man! >");
System.out.println("------------------------------------------------------>");
System.out.println(":: home :: https://easy-es.cn/ >");
System.out.println(":: community :: https://dromara.org/ >");
System.out.println(wechatStr);
System.out.println("----------------------------------------------------------->");
}
AnnotationAttributes mapperScanAttrs = AnnotationAttributes

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.dromara.easy-es</groupId>
<artifactId>easy-es-parent</artifactId>
<version>2.0.0-beta4</version>
<version>2.0.0-beta5</version>
<relativePath>../easy-es-parent</relativePath>
</parent>

View File

@ -14,6 +14,10 @@ public interface BaseEsConstants {
* 是否打印本框架Banner
*/
String ENABLE_BANNER = "easy-es.banner";
/**
* 是否开启iKun模式
*/
String ENABLE_I_KUN_MODE = "easy-es.global-config.i-kun-mode";
/**
* 默认主键名称
*/
@ -139,6 +143,10 @@ public interface BaseEsConstants {
* DSL语句前缀
*/
String DSL_PREFIX = "===> Execute By Easy-Es: ";
/**
* DSL语句
*/
String I_KUN_PREFIX = "===> 鸡你太美提醒您, 以下内容由Easy-Es执行:";
/**
* count DSL语句前缀
*/
@ -249,4 +257,12 @@ public interface BaseEsConstants {
* 缩放因子索引字段名称
*/
String SCALING_FACTOR_FIELD = "scaling_factor";
/**
* path分隔符
*/
String SIGN = "\\.";
/**
* path分隔符 不转义
*/
String STR_SIGN = ".";
}

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.dromara.easy-es</groupId>
<artifactId>easy-es-parent</artifactId>
<version>2.0.0-beta4</version>
<version>2.0.0-beta5</version>
<relativePath>../easy-es-parent</relativePath>
</parent>

View File

@ -1,14 +1,14 @@
package org.dromara.easyes.core.biz;
import org.dromara.easyes.annotation.rely.IdType;
import org.dromara.easyes.annotation.rely.JoinField;
import org.dromara.easyes.annotation.rely.RefreshPolicy;
import org.dromara.easyes.common.constants.BaseEsConstants;
import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.parser.deserializer.ExtraProcessor;
import com.alibaba.fastjson.serializer.SerializeFilter;
import lombok.Data;
import lombok.experimental.Accessors;
import org.dromara.easyes.annotation.rely.IdType;
import org.dromara.easyes.annotation.rely.JoinField;
import org.dromara.easyes.annotation.rely.RefreshPolicy;
import org.dromara.easyes.common.constants.BaseEsConstants;
import java.lang.reflect.Field;
import java.util.*;
@ -124,7 +124,11 @@ public class EntityInfo {
/**
* 当前主类的高亮字段列表
*/
private List<HighLightParam> highLightParams = new ArrayList<>();
private List<HighLightParam> highlightParams = new ArrayList<>();
/**
* 嵌套类-高亮字段列表
*/
private Map<Class<?>, List<HighLightParam>> nestedHighLightParamsMap = new HashMap<>();
/**
* fastjson 字段命名策略
*/
@ -137,6 +141,10 @@ public class EntityInfo {
* 实体字段->高亮返回结果 键值对
*/
private final Map<String, String> highlightFieldMap = new HashMap<>();
/**
* 嵌套类 实体字段->高亮返回结果字段
*/
private final Map<Class<?>, Map<String, String>> nestedHighlightFieldMap = new HashMap<>();
/**
* 实体字段名->es字段类型
*/
@ -182,6 +190,7 @@ public class EntityInfo {
* 数据刷新策略
*/
private RefreshPolicy refreshPolicy;
/**
* 获取需要进行查询的字段列表
*

View File

@ -21,6 +21,10 @@ public class GlobalConfig {
* whether to print dsl log 是否打印执行的dsl语句
*/
private boolean printDsl = true;
/**
* for fun, whether to print love chinese "kung fu" mode 是否开启爱坤(小黑子)模式 开启后日志将进入疯狂状态, 后期也会在此特殊模式下提供更多趣味化及傻瓜功能 让编码不仅简单,还有趣!
*/
private boolean iKunMode;
/**
* process index mode Manual by default 索引处理模式 默认开启手动模式
*/

View File

@ -18,6 +18,7 @@ import org.dromara.easyes.common.utils.*;
import org.dromara.easyes.core.biz.*;
import org.dromara.easyes.core.cache.BaseCache;
import org.dromara.easyes.core.cache.GlobalConfigCache;
import org.dromara.easyes.core.config.GlobalConfig;
import org.dromara.easyes.core.toolkit.EntityInfoHelper;
import org.dromara.easyes.core.toolkit.FieldUtils;
import org.dromara.easyes.core.toolkit.IndexUtils;
@ -1053,8 +1054,8 @@ public class BaseEsMapperImpl<T> implements BaseEsMapper<T> {
// 解析json
T entity = JSON.parseObject(searchHit.getSourceAsString(), entityClass, entityInfo.getExtraProcessor());
// 高亮字段处理
if (CollectionUtils.isNotEmpty(entityInfo.getHighLightParams())) {
// 主类中高亮字段处理
if (CollectionUtils.isNotEmpty(entityInfo.getHighlightParams())) {
Map<String, String> highlightFieldMap = getHighlightFieldMap();
Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
highlightFields.forEach((key, value) -> {
@ -1063,6 +1064,9 @@ public class BaseEsMapperImpl<T> implements BaseEsMapper<T> {
});
}
// 嵌套类中的高亮处理
setInnerHighlight(searchHit, entity, entityInfo.getNestedHighlightFieldMap());
// 得分字段处理
setScore(entity, searchHit.getScore(), entityInfo);
@ -1078,6 +1082,97 @@ public class BaseEsMapperImpl<T> implements BaseEsMapper<T> {
return entity;
}
/**
* 设置嵌套类型中的高亮
*
* @param searchHit 查询结果
* @param root 主实体对象
* @param nestedHighlightFieldMap 字段缓存
*/
private void setInnerHighlight(SearchHit searchHit, T root, Map<Class<?>, Map<String, String>> nestedHighlightFieldMap) {
// 遍历innerHits 批量设置
if (CollectionUtils.isEmpty(searchHit.getInnerHits())) {
return;
}
searchHit.getInnerHits()
.forEach((k, v) -> {
SearchHit[] hits = v.getHits();
Arrays.stream(hits).forEach(hit -> {
SearchHit.NestedIdentity nestedIdentity = hit.getNestedIdentity();
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (CollectionUtils.isNotEmpty(highlightFields) && nestedIdentity != null) {
highlightFields.forEach((k1, v1) -> {
String highLightContent = Arrays.stream(v1.getFragments()).map(Text::string).collect(Collectors.joining());
String[] split = k1.split(SIGN);
String highLightField = split[split.length - 1];
processInnerHighlight(nestedIdentity.getField().string(), root, nestedIdentity,
highLightField, highLightContent, nestedHighlightFieldMap);
});
}
});
});
}
/**
* 递归处理嵌套类中的高亮
*
* @param path 嵌套类路径
* @param root
* @param nestedIdentity 嵌套路径
* @param highlightField 高亮字段
* @param highlightContent 高亮内容
* @param nestedHighlightFieldMap 字段缓存
*/
private void processInnerHighlight(String path, Object root, SearchHit.NestedIdentity nestedIdentity, String highlightField,
String highlightContent, Map<Class<?>, Map<String, String>> nestedHighlightFieldMap) {
// 反射, 获取嵌套对象
Method method = BaseCache.getterMethod(root.getClass(), nestedIdentity.getField().string());
Object invoke = null;
try {
invoke = method.invoke(root);
} catch (Throwable e) {
e.printStackTrace();
LogUtils.error("processInnerHighlight invoke error, class:%s,methodName:%s",
root.getClass().getSimpleName(), nestedIdentity.getField().string());
}
// 嵌套对象为容器的情况
if (invoke instanceof Collection) {
Collection<?> coll = (Collection<?>) invoke;
Iterator<?> iterator = coll.iterator();
int i = 0;
while (iterator.hasNext()) {
Object next = iterator.next();
// 不在nestedIdentity中的项无需处理
if (i == nestedIdentity.getOffset()) {
if (path.equals(nestedIdentity.getField().string())) {
final SearchHit.NestedIdentity child = nestedIdentity.getChild();
if (child != null) {
// 递归 对子项执行相同操作
processInnerHighlight(child.getField().string(), next, child, highlightField, highlightContent, nestedHighlightFieldMap);
} else {
// 已找到需要被高亮的叶子节点
String realHighlightField = Optional.ofNullable(nestedHighlightFieldMap.get(next.getClass()))
.map(highlightFieldMap -> highlightFieldMap.get(highlightField)).orElse(highlightField);
setHighlightValue(next, realHighlightField, highlightContent);
}
}
}
i++;
}
} else {
// 不太可能发生, 因为非容器的嵌套类型无意义,可以直接大宽表,但考虑到健壮性,仍针对个别傻狍子用户兼容处理
Object finalInvoke = invoke;
Optional.ofNullable(finalInvoke).ifPresent(i -> {
String realHighlightField = Optional.ofNullable(nestedHighlightFieldMap.get(finalInvoke.getClass()))
.map(highlightFieldMap -> highlightFieldMap.get(highlightField)).orElse(highlightField);
setHighlightValue(i, realHighlightField, highlightContent);
});
}
}
/**
* 设置距离
*
@ -1395,9 +1490,9 @@ public class BaseEsMapperImpl<T> implements BaseEsMapper<T> {
* @param highlightField 高亮返回字段
* @param value 高亮结果值
*/
private void setHighlightValue(T entity, String highlightField, String value) {
private void setHighlightValue(Object entity, String highlightField, String value) {
try {
Method invokeMethod = BaseCache.setterMethod(entityClass, highlightField);
Method invokeMethod = BaseCache.setterMethod(entity.getClass(), highlightField);
invokeMethod.invoke(entity, value);
} catch (Throwable e) {
LogUtils.formatError("setHighlightValue error,entity:%s,highlightField:%s,value:%s,e:%s",
@ -1450,12 +1545,18 @@ public class BaseEsMapperImpl<T> implements BaseEsMapper<T> {
* @param countRequest 统计数量查询参数
*/
private void printCountDSL(CountRequest countRequest) {
if (GlobalConfigCache.getGlobalConfig().isPrintDsl() && Objects.nonNull(countRequest)) {
Optional.ofNullable(countRequest.query())
.ifPresent(source -> LogUtils.info(BaseEsConstants.COUNT_DSL_PREFIX
+ "\nindex-name: " + org.springframework.util.StringUtils.arrayToCommaDelimitedString(countRequest.indices())
+ "\nDSL" + source));
}
// GlobalConfig globalConfig = GlobalConfigCache.getGlobalConfig();
// if (.isPrintDsl() && Objects.nonNull(countRequest)){
// Optional.ofNullable(countRequest.query())
// .ifPresent(source -> {
// String prefix = globalConfig.isIKunMode() ? I_KUN_PREFIX : DSL_PREFIX;
// LogUtils.info(BaseEsConstants.COUNT_DSL_PREFIX
// + "\nindex-name: " + org.springframework.util.StringUtils.arrayToCommaDelimitedString(countRequest.indices())
// + "\nDSL" + source)
// });
// }
Optional.ofNullable(countRequest.query())
.ifPresent(i -> doPrint(i.toString(), countRequest.indices()));
}
/**
@ -1464,11 +1565,15 @@ public class BaseEsMapperImpl<T> implements BaseEsMapper<T> {
* @param searchRequest es查询请求参数
*/
private void printDSL(SearchRequest searchRequest) {
if (GlobalConfigCache.getGlobalConfig().isPrintDsl() && Objects.nonNull(searchRequest)) {
Optional.ofNullable(searchRequest.source())
.ifPresent(source -> LogUtils.info(BaseEsConstants.DSL_PREFIX
+ "\nindex-name: " + org.springframework.util.StringUtils.arrayToCommaDelimitedString(searchRequest.indices())
+ "\nDSL" + source));
Optional.ofNullable(searchRequest.source())
.ifPresent(i -> doPrint(i.toString(), searchRequest.indices()));
}
private void doPrint(String source, String[] indices) {
GlobalConfig globalConfig = GlobalConfigCache.getGlobalConfig();
if (globalConfig.isPrintDsl()) {
String prefix = globalConfig.isIKunMode() ? I_KUN_PREFIX : DSL_PREFIX;
LogUtils.info(prefix + "\nindex-name: " + String.join(",", indices) + "\nDSL" + source);
}
}
@ -1497,7 +1602,7 @@ public class BaseEsMapperImpl<T> implements BaseEsMapper<T> {
private String getRefreshPolicy() {
// 防止傻狍子用户在全局中把刷新策略修改为GLOBAL
final RefreshPolicy refreshPolicy = EntityInfoHelper.getEntityInfo(entityClass).getRefreshPolicy();
return refreshPolicy.equals(RefreshPolicy.GLOBAL) ? RefreshPolicy.NONE.getValue() : refreshPolicy.getValue();
return RefreshPolicy.GLOBAL.equals(refreshPolicy) ? RefreshPolicy.NONE.getValue() : refreshPolicy.getValue();
}
/**

View File

@ -246,10 +246,14 @@ public class WrapperProcessor {
break;
case NESTED:
realField = getRealField(param.getColumn(), mappingColumnMap);
String[] split = param.getColumn().split("\\.");
queryBuilder = getBool(children, QueryBuilders.boolQuery(), entityInfo, split[split.length - 1]);
queryBuilder = QueryBuilders.nestedQuery(realField, queryBuilder, (ScoreMode) param.getVal());
setBool(bool, queryBuilder, param.getPrevQueryType());
String[] split = param.getColumn().split(SIGN);
String path = split[split.length - 1];
queryBuilder = getBool(children, QueryBuilders.boolQuery(), entityInfo, path);
NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery(realField, queryBuilder, (ScoreMode) param.getVal());
// 设置嵌套类型高亮查询参数
setNestedHighlight(path, param.getColumn(), nestedQueryBuilder, entityInfo);
// 设置bool查询参数
setBool(bool, nestedQueryBuilder, param.getPrevQueryType());
break;
default:
// just ignore,almost never happen
@ -257,6 +261,27 @@ public class WrapperProcessor {
}
}
/**
* 设置嵌套类型高亮查询参数
*
* @param path 嵌套path
* @param column 字段
* @param nestedQueryBuilder 嵌套查询条件构造器
* @param entityInfo 实体信息缓存
*/
private static void setNestedHighlight(String path, String column, NestedQueryBuilder nestedQueryBuilder, EntityInfo entityInfo) {
// 嵌套类型的高亮查询语句构造
Class<?> pathClass = entityInfo.getPathClassMap().get(path);
Optional.ofNullable(pathClass)
.flatMap(i -> Optional.ofNullable(entityInfo.getNestedHighLightParamsMap().get(i)))
.ifPresent(i -> {
// 嵌套类型高亮字段名需要完整的path 例如users.faqs 所以此处用param.column而非path
HighlightBuilder highlightBuilder = initHighlightBuilder(i, column);
Optional.ofNullable(highlightBuilder)
.ifPresent(p -> nestedQueryBuilder.innerHit(new InnerHitBuilder().setHighlightBuilder(p)));
});
}
/**
* 设置节点的bool
*
@ -282,8 +307,10 @@ public class WrapperProcessor {
/**
* 递归获取子节点的bool
*
* @param paramList 子节点参数列表
* @param builder 新的根bool
* @param paramList 子节点参数列表
* @param builder 新的根bool
* @param entityInfo 实体信息缓存
* @param path 路径
* @return 子节点bool合集, 统一封装至入参builder中
*/
private static BoolQueryBuilder getBool(List<Param> paramList, BoolQueryBuilder builder, EntityInfo entityInfo, String path) {
@ -326,7 +353,7 @@ public class WrapperProcessor {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 设置高亮
setHighLight(entityInfo.getHighLightParams(), searchSourceBuilder);
setHighLight(entityInfo.getHighlightParams(), searchSourceBuilder);
// 设置用户指定的各种排序规则
setSort(wrapper, mappingColumnMap, searchSourceBuilder);
@ -404,12 +431,23 @@ public class WrapperProcessor {
return;
}
// 初始化高亮参数
HighlightBuilder highlightBuilder = initHighlightBuilder(highLightParams, null);
Optional.ofNullable(highlightBuilder).ifPresent(searchSourceBuilder::highlighter);
}
private static HighlightBuilder initHighlightBuilder(List<HighLightParam> highLightParams, String path) {
if (CollectionUtils.isEmpty(highLightParams)) {
return null;
}
// 封装高亮参数
HighlightBuilder highlightBuilder = new HighlightBuilder();
highLightParams.forEach(highLightParam -> {
if (StringUtils.isNotBlank(highLightParam.getHighLightField())) {
//field
HighlightBuilder.Field field = new HighlightBuilder.Field(highLightParam.getHighLightField());
// 嵌套类型 须追加其完整path前缀
String highlightField = Optional.ofNullable(path).map(i -> i + STR_SIGN + highLightParam.getHighLightField())
.orElse(highLightParam.getHighLightField());
HighlightBuilder.Field field = new HighlightBuilder.Field(highlightField);
field.highlighterType(highLightParam.getHighLightType().getValue());
highlightBuilder.field(field);
@ -419,7 +457,7 @@ public class WrapperProcessor {
Optional.ofNullable(highLightParam.getNumberOfFragments()).ifPresent(highlightBuilder::numOfFragments);
}
});
searchSourceBuilder.highlighter(highlightBuilder);
return highlightBuilder;
}

View File

@ -257,7 +257,7 @@ public class EntityInfoHelper {
// 初始化封装HighLight注解信息
if (field.isAnnotationPresent(HighLight.class)) {
initHighLightAnnotation(dbConfig, entityInfo, field);
initHighLightAnnotation(dbConfig, entityInfo, field, entityInfo.getMappingColumnMap(), null);
// 此处无需返回true阻断流程,可防止用户未添加IndexField时,框架索引跳过读取此字段的信息
}
@ -373,38 +373,71 @@ public class EntityInfoHelper {
/**
* HighLight注解初始化
*
* @param dbConfig 索引配置
* @param entityInfo 实体信息
* @param field 字段
* @param dbConfig 索引配置
* @param entityInfo 实体信息
* @param field 字段
* @param mappingColumnMap 实体字段与es字段映射关系
* @param nestedClass 嵌套类
*/
private static void initHighLightAnnotation(GlobalConfig.DbConfig dbConfig, EntityInfo entityInfo, Field field) {
private static void initHighLightAnnotation(GlobalConfig.DbConfig dbConfig, EntityInfo entityInfo, Field field,
Map<String, String> mappingColumnMap, Class<?> nestedClass) {
HighLight highLight = field.getAnnotation(HighLight.class);
String mappingField = highLight.mappingField();
if (StringUtils.isNotBlank(mappingField)) {
entityInfo.getNotSerializeField().add(mappingField);
} else {
// 置入字段json序列化忽略缓存
boolean skip = false;
if (StringUtils.isBlank(mappingField)) {
// 如果用户未指定高亮映射字段,则高亮映射字段用当前字段
mappingField = field.getName();
// 当使用当前字段作为高亮字段时,当前字段参与索引创建
skip = true;
}
if (!skip) {
// 添加无需序列化字段至缓存
if (nestedClass == null) {
entityInfo.getNotSerializeField().add(mappingField);
} else {
// 嵌套类型
Set<String> nestedNotSerializeFieldSet = Optional.ofNullable(entityInfo.getNestedNotSerializeField().get(nestedClass))
.orElse(new HashSet<>());
nestedNotSerializeFieldSet.add(mappingField);
entityInfo.getNestedNotSerializeField().put(nestedClass, nestedNotSerializeFieldSet);
}
}
String customField = entityInfo.getMappingColumnMap().get(field.getName());
// 置入高亮字段与实体类中字段名对应关系缓存
String customField = mappingColumnMap.get(field.getName());
String realHighLightField = Objects.isNull(customField) ? field.getName() : customField;
if (dbConfig.isMapUnderscoreToCamelCase()) {
realHighLightField = StringUtils.camelToUnderline(realHighLightField);
}
entityInfo.getHighlightFieldMap().putIfAbsent(realHighLightField, mappingField);
if (nestedClass == null) {
entityInfo.getHighlightFieldMap().putIfAbsent(realHighLightField, mappingField);
} else {
Map<String, String> nestedHighlightFieldMap = Optional.ofNullable(entityInfo.getNestedHighlightFieldMap().get(nestedClass))
.orElse(new HashMap<>());
nestedHighlightFieldMap.putIfAbsent(realHighLightField, mappingField);
entityInfo.getNestedHighlightFieldMap().put(nestedClass, nestedHighlightFieldMap);
}
// 封装高亮参数
HighLightParam highLightParam = new HighLightParam();
highLightParam.setFragmentSize(highLight.fragmentSize())
// 置入高亮查询参数缓存
HighLightParam highlightParam = new HighLightParam();
highlightParam.setFragmentSize(highLight.fragmentSize())
.setPreTag(highLight.preTag())
.setPostTag(highLight.postTag())
.setHighLightField(realHighLightField)
.setHighLightType(highLight.highLightType());
if (MINUS_ONE != highLight.numberOfFragments() && highLight.numberOfFragments() > ZERO) {
highLightParam.setNumberOfFragments(highLight.numberOfFragments());
highlightParam.setNumberOfFragments(highLight.numberOfFragments());
}
if (nestedClass == null) {
entityInfo.getHighlightParams().add(highlightParam);
} else {
List<HighLightParam> nestedHighlightParams = Optional.ofNullable(entityInfo.getNestedHighLightParamsMap().get(nestedClass))
.orElse(new ArrayList<>());
nestedHighlightParams.add(highlightParam);
entityInfo.getNestedHighLightParamsMap().put(nestedClass, nestedHighlightParams);
}
entityInfo.getHighLightParams().add(highLightParam);
}
/**
@ -490,6 +523,10 @@ public class EntityInfoHelper {
mappingColumnMap.putIfAbsent(field.getName(), mappingColumn);
fieldTypeMap.putIfAbsent(field.getName(), fieldType.getType());
// 初始化封装嵌套类中的HighLight注解信息
if (field.isAnnotationPresent(HighLight.class)) {
initHighLightAnnotation(dbConfig, entityInfo, field, mappingColumnMap, nestedClass);
}
});
entityInfo.getNestedNotSerializeField().putIfAbsent(nestedClass, notSerializedFields);
entityInfo.getNestedClassColumnMappingMap().putIfAbsent(nestedClass, columnMappingMap);

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.dromara.easy-es</groupId>
<artifactId>easy-es-parent</artifactId>
<version>2.0.0-beta4</version>
<version>2.0.0-beta5</version>
<relativePath>../easy-es-parent</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<groupId>org.dromara.easy-es</groupId>
<artifactId>easy-es-parent</artifactId>
<version>2.0.0-beta4</version>
<version>2.0.0-beta5</version>
<name>easy-es-parent</name>
<description>easy use for elastic search</description>

View File

@ -9,7 +9,7 @@
<parent>
<groupId>org.dromara.easy-es</groupId>
<artifactId>easy-es</artifactId>
<version>2.0.0-beta4</version>
<version>2.0.0-beta5</version>
</parent>
<properties>

View File

@ -1,5 +1,7 @@
package org.dromara.easyes.sample.entity;
import lombok.Data;
import lombok.experimental.Accessors;
import org.dromara.easyes.annotation.HighLight;
import org.dromara.easyes.annotation.IndexField;
import org.dromara.easyes.annotation.IndexId;
@ -8,8 +10,6 @@ import org.dromara.easyes.annotation.rely.Analyzer;
import org.dromara.easyes.annotation.rely.FieldStrategy;
import org.dromara.easyes.annotation.rely.FieldType;
import org.dromara.easyes.annotation.rely.IdType;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* es 数据模型
@ -18,7 +18,7 @@ import lombok.experimental.Accessors;
**/
@Data
@Accessors(chain = true)
@IndexName(value = "easyes_document", shardsNum = 3, replicasNum = 2, keepGlobalPrefix = true,maxResultWindow = 100)
@IndexName(value = "easyes_document", shardsNum = 3, replicasNum = 2, keepGlobalPrefix = true, maxResultWindow = 100)
public class Document {
/**
* es中的唯一id,如果你想自定义es中的id为你提供的id,比如MySQL中的id,请将注解中的type指定为customize或直接在全局配置文件中指定,如此id便支持任意数据类型)
@ -63,7 +63,7 @@ public class Document {
/**
* 自定义字段名称
*/
@IndexField(value = "wu-la", fieldType = FieldType.TEXT, analyzer = Analyzer.IK_SMART, searchAnalyzer = Analyzer.IK_SMART,fieldData = true)
@IndexField(value = "wu-la", fieldType = FieldType.TEXT, analyzer = Analyzer.IK_SMART, searchAnalyzer = Analyzer.IK_SMART, fieldData = true)
private String customField;
/**

View File

@ -6,12 +6,13 @@ easy-es:
password: WG7WVmuNMtM4GwNYkyWH
keep-alive-millis: 18000
global-config:
i-kun-mode: true
process-index-mode: smoothly
async-process-index-blocking: true
print-dsl: false
db-config:
map-underscore-to-camel-case: true
table-prefix: dev_
index-prefix: dev_
id-type: customize
field-strategy: not_empty
refresh-policy: immediate

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.dromara.easy-es</groupId>
<artifactId>easy-es</artifactId>
<version>2.0.0-beta4</version>
<version>2.0.0-beta5</version>
</parent>
<artifactId>easy-es-test</artifactId>

View File

@ -4,10 +4,11 @@ package org.dromara.easyes.test.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dromara.easyes.annotation.HighLight;
import org.dromara.easyes.annotation.IndexField;
/**
* 文件描述
* 问答
*
* @ProductName: Hundsun HEP
* @ProjectName: easy-es
@ -26,7 +27,14 @@ import org.dromara.easyes.annotation.IndexField;
@NoArgsConstructor
@AllArgsConstructor
public class Faq {
/**
* 问题 高亮内容直接覆盖在原字段值上进行返回,故不需要指定高亮注解中的mappingField
*/
@HighLight
private String faqName;
@IndexField(value = "answer")
/**
* 答案
*/
private String faqAnswer;
}

View File

@ -1,9 +1,9 @@
package org.dromara.easyes.test.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dromara.easyes.annotation.HighLight;
import org.dromara.easyes.annotation.IndexField;
import org.dromara.easyes.annotation.rely.Analyzer;
import org.dromara.easyes.annotation.rely.FieldType;
@ -17,13 +17,21 @@ import java.util.Set;
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
/**
* 用户名
*/
@IndexField(value = "user_name", fieldType = FieldType.TEXT, analyzer = Analyzer.IK_SMART)
@HighLight(mappingField = "highlightUsername")
private String username;
/**
* 年龄
*/
@IndexField(fieldType = FieldType.INTEGER)
private Integer age;
/**
* 密码
*/
@IndexField(fieldType = FieldType.KEYWORD)
private String password;
/**
@ -31,4 +39,15 @@ public class User {
*/
@IndexField(fieldType = FieldType.NESTED, nestedClass = Faq.class)
private Set<Faq> faqs;
/**
* 高亮显示的内容
*/
private String highlightUsername;
public User(String username, Integer age, String password, Set<Faq> faqs) {
this.username = username;
this.age = age;
this.password = password;
this.faqs = faqs;
}
}

View File

@ -11,6 +11,7 @@ import org.dromara.easyes.test.mapper.DocumentMapper;
import org.elasticsearch.geometry.Point;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@ -34,6 +35,15 @@ public class NestedTest {
private DocumentMapper documentMapper;
@Test
@Order(0)
public void testCreateIndex() {
// 初始化创建索引,配置开启手动挡后执行
final Boolean success = documentMapper.createIndex();
Assertions.assertTrue(success);
}
@Test
@Order(1)
public void testInsert() {
// 测试插入数据
Document document = new Document();
@ -49,14 +59,14 @@ public class NestedTest {
document.setStarNum(1);
List<User> users = new ArrayList<>();
Set<Faq> faqs = new HashSet<>();
faqs.add(new Faq("问题1", "回答1"));
faqs.add(new Faq("问题2", "回答2"));
faqs.add(new Faq("q1", "回答1"));
faqs.add(new Faq("q2", "回答2"));
Set<Faq> faqs1 = new HashSet<>();
faqs1.add(new Faq("问题3", "回答3"));
faqs1.add(new Faq("问题4", "回答4"));
users.add(new User("用户1", 18, "12345", faqs));
users.add(new User("用户2", 19, "123", faqs1));
faqs1.add(new Faq("q4", "回答3"));
faqs1.add(new Faq("q3", "回答4"));
users.add(new User("u1", 18, "12345", faqs));
users.add(new User("u2", 19, "123", faqs1));
document.setUsers(users);
int successCount = documentMapper.insert(document);
Assertions.assertEquals(successCount, 1);
@ -65,19 +75,20 @@ public class NestedTest {
users.clear();
faqs.clear();
faqs1.clear();
faqs.add(new Faq("question1", "answer1"));
faqs.add(new Faq("question2", "answer2"));
faqs.add(new Faq("q1", "answer1"));
faqs.add(new Faq("q2", "answer2"));
faqs1.add(new Faq("q3", "a3"));
faqs1.add(new Faq("q4", "a4"));
users.add(new User("user1", 8, "12345", faqs));
users.add(new User("u2", 9, "54321", faqs1));
users.add(new User("u3", 8, "12345", faqs));
users.add(new User("u4", 9, "54321", faqs1));
document.setUsers(users);
successCount = documentMapper.insert(document);
Assertions.assertEquals(successCount, 1);
}
@Test
@Order(2)
public void testNestedMatch() {
// 嵌套查询 查询年龄等于18或8且密码等于12345的数据
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
@ -89,10 +100,10 @@ public class NestedTest {
// 嵌套类型中的字段获取可以用FieldUtils.val或直接传入字符串
LambdaEsQueryWrapper<Document> wrapper1 = new LambdaEsQueryWrapper<>();
wrapper1.eq(Document::getTitle,"老汉")
wrapper1.eq(Document::getTitle, "老汉")
.nested("users.faqs", w -> w.eq("users.faqs.answer", "a4")
.match("users.faqs.faq_name", "q4"))
.nested("users", w -> w.between("users.age", 1, 30))
.nested("users", w -> w.match("users.user_name", "u3"))
.match(Document::getCreator, "吃饭");
List<Document> documents1 = documentMapper.selectList(wrapper1);
System.out.println(documents1);
@ -106,7 +117,7 @@ public class NestedTest {
System.out.println(documents2);
LambdaEsQueryWrapper<Document> wrapper3 = new LambdaEsQueryWrapper<>();
wrapper3.nested("users.faqs",w->w.match("users.faqs.faq_name", "q3").or().match("users.faqs.faq_name","q4"));
wrapper3.nested("users.faqs", w -> w.match("users.faqs.faq_name", "q3").or().match("users.faqs.faq_name", "q4"));
List<Document> documents3 = documentMapper.selectList(wrapper3);
System.out.println(documents3);
}

View File

@ -13,6 +13,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.List;
/**
* searchAfter测试
@ -32,9 +33,38 @@ public class SearchAfterTest {
//重现bug需要注释掉searchAfter中对from的校验
// lambdaEsQueryWrapper.from(10);
SAPageInfo<Document> saPageInfo = documentMapper.searchAfterPage(lambdaEsQueryWrapper, null, 10);
//第一页
System.out.println(saPageInfo);
Assertions.assertEquals(10, saPageInfo.getList().size());
}
@Test
public void test1() {
documentMapper.createIndex();
for (int i = 0; i < 30; i++) {
Document document = new Document();
document.setEsId(String.valueOf(i));
document.setTitle("测试标题" + i);
document.setContent("测试内容" + i);
documentMapper.insert(document);
}
}
@Test
public void test2() {
LambdaEsQueryWrapper<Document> wrapper = EsWrappers.lambdaQuery(Document.class);
wrapper.match(Document::getContent, "测试");
wrapper.orderByDesc(Document::getEsId);
// 第一页,可传null,查完把saPageInfo返回给前端
SAPageInfo<Document> saPageInfo = documentMapper.searchAfterPage(wrapper, null, 10);
System.out.println(saPageInfo);
// 第二页,从saPageInfo中把上一次的nextSearchAfter回传给后端
List<Object> nextSearchAfter = saPageInfo.getNextSearchAfter();
SAPageInfo<Document> saPageInfo1 = documentMapper.searchAfterPage(wrapper, nextSearchAfter, 10);
System.out.println(saPageInfo1);
}
}

View File

@ -6,6 +6,7 @@ easy-es:
# password: WG7WVmuNMtM4GwNYkyWH
keep-alive-millis: 18000
global-config:
i-kun-mode: true
process-index-mode: manual
async-process-index-blocking: true
print-dsl: true

View File

@ -6,7 +6,7 @@
<groupId>org.dromara.easy-es</groupId>
<artifactId>easy-es</artifactId>
<version>2.0.0-beta4</version>
<version>2.0.0-beta5</version>
<name>easy-es</name>
<description>easy use for elastic search</description>