v2.0-beta

大功告成!
This commit is contained in:
xpc1024 2023-01-30 20:20:20 +08:00
parent cc437c19e6
commit 2527559f85
10 changed files with 129 additions and 119 deletions

View File

@ -11,6 +11,10 @@ import lombok.Data;
**/
@Data
public class Param extends Tree {
/**
* 上一节点类型
*/
private EsQueryTypeEnum prevQueryType;
/**
* 节点类型
*/

View File

@ -44,14 +44,14 @@ public abstract class AbstractChainWrapper<T, R, Children extends AbstractChainW
}
@Override
public <V> Children allEq(boolean condition, Map<String, V> params, boolean null2IsNull) {
getWrapper().allEq(condition, params, null2IsNull);
public <V> Children allEq(boolean condition, Map<String, V> params) {
getWrapper().allEq(condition, params);
return typedThis;
}
@Override
public <V> Children allEq(boolean condition, BiPredicate<String, V> filter, Map<String, V> params, boolean null2IsNull) {
getWrapper().allEq(condition, filter, params, null2IsNull);
public <V> Children allEq(boolean condition, BiPredicate<String, V> filter, Map<String, V> params) {
getWrapper().allEq(condition, filter, params);
return typedThis;
}

View File

@ -45,6 +45,10 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
* 全局父节点 每次指向nested条件后
*/
protected String parentId;
/**
* 上一节点类型
*/
protected EsQueryTypeEnum prevQueryType;
/**
* 参数列表
*/
@ -52,7 +56,11 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
/**
* 队列 存放父id
*/
protected LinkedList<String> queue;
protected LinkedList<String> parentIdQueue;
/**
* 队列 存放上一节点类型
*/
protected LinkedList<EsQueryTypeEnum> prevQueryTypeQueue;
/**
* 基础排序参数列表
*/
@ -111,11 +119,13 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
aggregationParamList = new ArrayList<>();
paramList = new ArrayList<>();
level = 0;
queue = new LinkedList<>();
prevQueryType = AND_MUST;
parentIdQueue = new LinkedList<>();
prevQueryTypeQueue = new LinkedList<>();
}
@Override
public <V> Children allEq(boolean condition, Map<String, V> params, boolean null2IsNull) {
public <V> Children allEq(boolean condition, Map<String, V> params) {
if (condition && CollectionUtils.isNotEmpty(params)) {
params.forEach(this::eq);
}
@ -123,7 +133,7 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
}
@Override
public <V> Children allEq(boolean condition, BiPredicate<String, V> filter, Map<String, V> params, boolean null2IsNull) {
public <V> Children allEq(boolean condition, BiPredicate<String, V> filter, Map<String, V> params) {
if (condition && CollectionUtils.isNotEmpty(params)) {
params.forEach((k, v) -> {
if (filter.test(k, v)) {
@ -520,10 +530,20 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
private void addBaseParam(Param param, EsQueryTypeEnum queryTypeEnum, String column, Object val, Float boost) {
param.setId(UUID.randomUUID().toString());
Optional.ofNullable(parentId).ifPresent(param::setParentId);
param.setPrevQueryType(prevQueryType);
param.setQueryTypeEnum(queryTypeEnum);
param.setVal(val);
param.setColumn(column);
param.setBoost(boost);
// 入队之前需要先对MP中的拼接or()特殊处理,嵌套or则不需要处理 之所以在此处处理,是因为可以少遍历一遍树 节省OLog(N) 时间复杂度
if (CollectionUtils.isNotEmpty(paramList)) {
Param prev = paramList.get(paramList.size() - 1);
if (OR.equals(prev.getQueryTypeEnum())) {
// 上一节点是拼接or() 则修改当前节点的prevQueryType为OR_SHOULD 让其走should查询
param.setPrevQueryType(OR_SHOULD);
}
}
paramList.add(param);
}
@ -608,16 +628,21 @@ public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T,
level++;
paramList.add(param);
this.parentId = param.getId();
queue.push(parentId);
parentIdQueue.push(parentId);
prevQueryTypeQueue.push(queryTypeEnum);
consumer.accept(instance());
// 深度优先在consumer条件消费完后会来执行这里 此时parentId需要重置 至于为什么 可断点打在consumer前后观察一波 整个框架最难的地方就在此
level--;
if (!queue.isEmpty()) {
this.parentId = queue.pollLast();
if (!parentIdQueue.isEmpty()) {
this.parentId = parentIdQueue.pollLast();
}
if (!prevQueryTypeQueue.isEmpty()) {
this.prevQueryType = prevQueryTypeQueue.pollLast();
}
if (level == 0) {
// 仙人板板
this.parentId = null;
this.prevQueryType = AND_MUST;
}
}
return typedThis;

View File

@ -1,5 +1,6 @@
package cn.easyes.core.conditions;
import cn.easyes.common.enums.EsQueryTypeEnum;
import cn.easyes.common.params.SFunction;
import cn.easyes.common.utils.ArrayUtils;
import cn.easyes.common.utils.ExceptionUtils;
@ -58,12 +59,15 @@ public class LambdaEsQueryWrapper<T> extends AbstractLambdaQueryWrapper<T, Lambd
exclude = new String[]{};
}
LambdaEsQueryWrapper(T entity, List<Param> paramList, LinkedList<String> queue, Integer level, String parentId, List<BaseSortParam> baseSortParams,
List<AggregationParam> aggregationParamList) {
LambdaEsQueryWrapper(T entity, List<Param> paramList, LinkedList<String> parentIdQueue,
LinkedList<EsQueryTypeEnum> prevQueryTypeQueue, Integer level, String parentId,
EsQueryTypeEnum pervQueryType, List<BaseSortParam> baseSortParams, List<AggregationParam> aggregationParamList) {
super.setEntity(entity);
this.queue = queue;
this.parentIdQueue = parentIdQueue;
this.prevQueryTypeQueue = prevQueryTypeQueue;
this.level = level;
this.parentId = parentId;
this.prevQueryType = pervQueryType;
this.paramList = paramList;
this.baseSortParams = baseSortParams;
this.aggregationParamList = aggregationParamList;
@ -71,7 +75,7 @@ public class LambdaEsQueryWrapper<T> extends AbstractLambdaQueryWrapper<T, Lambd
@Override
protected LambdaEsQueryWrapper<T> instance() {
return new LambdaEsQueryWrapper<>(entity, paramList, queue, level, parentId, baseSortParams, aggregationParamList);
return new LambdaEsQueryWrapper<>(entity, paramList, parentIdQueue, prevQueryTypeQueue, level, parentId, prevQueryType, baseSortParams, aggregationParamList);
}
@Override

View File

@ -41,7 +41,7 @@ import static cn.easyes.common.constants.BaseEsConstants.*;
import static cn.easyes.common.enums.EsQueryTypeEnum.*;
/**
* 核心 wrpeer处理类
* 核心 wrapper处理类
* <p>
* Copyright © 2021 xpc1024 All Rights Reserved
**/
@ -76,9 +76,27 @@ public class WrapperProcessor {
*/
public static BoolQueryBuilder initBoolQueryBuilder(List<Param> paramList, Class<?> entityClass) {
// 数据预处理
List<Param> rootList = new ArrayList<>();
preProcessData(paramList, rootList, entityClass);
// 建立参数森林无根树
TreeBuilder treeBuilder = new TreeBuilder(rootList, paramList);
List<Param> tree = (List<Param>) treeBuilder.build();
BoolQueryBuilder rootBool = QueryBuilders.boolQuery();
// 对森林的每个根节点递归封装 这里看似简单实则很绕很烧脑 整个框架的核心 主要依托树的递归 深度优先遍历 森林
return getBool(tree, rootBool);
}
/***
* 数据预处理 原始数据转换为目标数据
* @param paramList 参数列表
* @param rootList 子树根
* @param entityClass 实体类
*/
private static void preProcessData(List<Param> paramList, List<Param> rootList, Class<?> entityClass) {
EntityInfo entityInfo = EntityInfoHelper.getEntityInfo(entityClass);
GlobalConfig.DbConfig dbConfig = GlobalConfigCache.getGlobalConfig().getDbConfig();
List<Param> rootList = new ArrayList<>();
paramList.forEach(param -> {
// 驼峰及自定义字段转换
String realField = FieldUtils.getRealField(param.getColumn(), entityInfo.getMappingColumnMap(), dbConfig);
@ -98,30 +116,6 @@ public class WrapperProcessor {
rootList.add(param);
}
});
// 建树
TreeBuilder treeBuilder = new TreeBuilder(rootList, paramList);
List<Param> tree = (List<Param>) treeBuilder.build();
BoolQueryBuilder rootBool = QueryBuilders.boolQuery();
// 遍历,对森林的每个根节点递归封装
EsQueryTypeEnum parentQueryType = getParentQueryType(dbConfig.isEnableMust2Filter());
tree.forEach(root -> setBool(rootBool, root, parentQueryType));
return rootBool;
}
/**
* 根据配置获取根节点查询类型
*
* @param enableMust2Filter 是否开启must转filter配置 true开启 false
* @return 根节点查询类型
*/
private static EsQueryTypeEnum getParentQueryType(boolean enableMust2Filter) {
if (enableMust2Filter) {
return EsQueryTypeEnum.FILTER;
} else {
return EsQueryTypeEnum.AND_MUST;
}
}
/**
@ -129,10 +123,9 @@ public class WrapperProcessor {
*
* @param bool BoolQueryBuilder
* @param param 查询参数
* @param parentType 父查询类型
*/
@SneakyThrows
private static void setBool(BoolQueryBuilder bool, Param param, EsQueryTypeEnum parentType) {
private static void initBool(BoolQueryBuilder bool, Param param) {
List<Param> children = (List<Param>) param.getChildren();
QueryBuilder queryBuilder;
switch (param.getQueryTypeEnum()) {
@ -141,121 +134,122 @@ public class WrapperProcessor {
break;
case TERM:
queryBuilder = QueryBuilders.termQuery(param.getColumn(), param.getVal()).boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case MATCH:
queryBuilder = QueryBuilders.matchQuery(param.getColumn(), param.getVal()).boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case MATCH_PHRASE:
queryBuilder = QueryBuilders.matchPhraseQuery(param.getColumn(), param.getVal()).boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case MATCH_PHRASE_PREFIX:
queryBuilder = QueryBuilders.matchPhrasePrefixQuery(param.getColumn(), param.getVal()).boost(param.getBoost()).maxExpansions((int) param.getExt1());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case MULTI_MATCH:
queryBuilder = QueryBuilders.multiMatchQuery(param.getVal(), param.getColumns()).operator((Operator) param.getExt1()).minimumShouldMatch(String.valueOf(param.getExt2()));
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case MATCH_ALL:
queryBuilder = QueryBuilders.matchAllQuery().boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case QUERY_STRING:
queryBuilder = QueryBuilders.queryStringQuery(param.getColumn()).boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case PREFIX:
queryBuilder = QueryBuilders.prefixQuery(param.getColumn(), (String) param.getVal()).boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case GT:
queryBuilder = QueryBuilders.rangeQuery(param.getColumn()).gt(param.getVal()).boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case GE:
queryBuilder = QueryBuilders.rangeQuery(param.getColumn()).gte(param.getVal()).boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case LT:
queryBuilder = QueryBuilders.rangeQuery(param.getColumn()).lt(param.getVal()).boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case LE:
queryBuilder = QueryBuilders.rangeQuery(param.getColumn()).lte(param.getVal()).boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case BETWEEN:
queryBuilder = QueryBuilders.rangeQuery(param.getColumn()).gte(param.getExt1()).lte(param.getExt2()).boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case WILDCARD:
queryBuilder = QueryBuilders.wildcardQuery(param.getColumn(), param.getVal().toString());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case TERMS:
queryBuilder = QueryBuilders.termsQuery(param.getColumn(), (Collection<?>) param.getVal());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case EXISTS:
queryBuilder = QueryBuilders.existsQuery(param.getColumn()).boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case GEO_BOUNDING_BOX:
queryBuilder = QueryBuilders.geoBoundingBoxQuery(param.getColumn()).setCorners((GeoPoint) param.getExt1(), (GeoPoint) param.getExt2()).boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case GEO_DISTANCE:
GeoDistanceQueryBuilder geoDistanceBuilder = QueryBuilders.geoDistanceQuery(param.getColumn()).point((GeoPoint) param.getExt2()).boost(param.getBoost());
MyOptional.ofNullable(param.getExt1()).ifPresent(ext1 -> geoDistanceBuilder.distance((double) param.getVal(), (DistanceUnit) ext1), () -> geoDistanceBuilder.distance((String) param.getVal()));
queryBuilder = geoDistanceBuilder;
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case GEO_POLYGON:
queryBuilder = QueryBuilders.geoPolygonQuery(param.getColumn(), (List<GeoPoint>) param.getVal());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case GEO_SHAPE_ID:
queryBuilder = QueryBuilders.geoShapeQuery(param.getColumn(), param.getVal().toString()).boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case GEO_SHAPE:
queryBuilder = QueryBuilders.geoShapeQuery(param.getColumn(), (Geometry) param.getVal()).relation((ShapeRelation) param.getExt1()).boost(param.getBoost());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case NESTED_MATCH:
queryBuilder = QueryBuilders.nestedQuery(param.getExt1().toString(), QueryBuilders.matchQuery(param.getExt1().toString() + PATH_FIELD_JOIN + param.getColumn(), param.getVal()).boost(param.getBoost()), (ScoreMode) param.getExt2());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case HAS_CHILD:
queryBuilder = new HasChildQueryBuilder(param.getExt1().toString(), QueryBuilders.matchQuery(param.getExt1().toString() + PATH_FIELD_JOIN + param.getColumn(), param.getVal()).boost(param.getBoost()), (ScoreMode) param.getExt2());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case HAS_PARENT:
queryBuilder = new HasParentQueryBuilder(param.getExt1().toString(), QueryBuilders.matchQuery(param.getExt1().toString() + PATH_FIELD_JOIN + param.getColumn(), param.getVal()).boost(param.getBoost()), (boolean) param.getExt2());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
case PARENT_ID:
queryBuilder = new ParentIdQueryBuilder(param.getColumn(), param.getVal().toString());
setChildrenBool(bool, queryBuilder, parentType);
setBool(bool, queryBuilder, param.getPrevQueryType());
break;
// 下面四种嵌套类型 需要对孩子节点递归处理
case AND_MUST:
queryBuilder = getChildrenBool(children, QueryBuilders.boolQuery(), parentType);
setChildrenBool(bool, queryBuilder, AND_MUST);
queryBuilder = getBool(children, QueryBuilders.boolQuery());
setBool(bool, queryBuilder, AND_MUST);
break;
case FILTER:
queryBuilder = getChildrenBool(children, QueryBuilders.boolQuery(), parentType);
setChildrenBool(bool, queryBuilder, FILTER);
queryBuilder = getBool(children, QueryBuilders.boolQuery());
setBool(bool, queryBuilder, FILTER);
break;
case MUST_NOT:
queryBuilder = getChildrenBool(children, QueryBuilders.boolQuery(), parentType);
setChildrenBool(bool, queryBuilder, MUST_NOT);
queryBuilder = getBool(children, QueryBuilders.boolQuery());
setBool(bool, queryBuilder, MUST_NOT);
break;
case OR_SHOULD:
queryBuilder = getChildrenBool(children, QueryBuilders.boolQuery(), parentType);
setChildrenBool(bool, queryBuilder, OR_SHOULD);
queryBuilder = getBool(children, QueryBuilders.boolQuery());
setBool(bool, queryBuilder, OR_SHOULD);
break;
default:
throw ExceptionUtils.eee("非法参数类型");
@ -263,13 +257,13 @@ public class WrapperProcessor {
}
/**
* 设置孩子节点的bool
* 设置节点的bool
*
* @param bool 节点BoolQueryBuilder
* @param queryBuilder 孩子节点BoolQueryBuilder
* @param bool 节点BoolQueryBuilder
* @param queryBuilder 非根节点BoolQueryBuilder
* @param parentType 查询类型
*/
private static void setChildrenBool(BoolQueryBuilder bool, QueryBuilder queryBuilder, EsQueryTypeEnum parentType) {
private static void setBool(BoolQueryBuilder bool, QueryBuilder queryBuilder, EsQueryTypeEnum parentType) {
if (EsQueryTypeEnum.AND_MUST.equals(parentType)) {
bool.must(queryBuilder);
} else if (EsQueryTypeEnum.OR_SHOULD.equals(parentType)) {
@ -288,11 +282,11 @@ public class WrapperProcessor {
* @param builder 新的根bool
* @return 子节点bool合集, 统一封装至入参builder中
*/
private static BoolQueryBuilder getChildrenBool(List<Param> paramList, BoolQueryBuilder builder, EsQueryTypeEnum parentType) {
private static BoolQueryBuilder getBool(List<Param> paramList, BoolQueryBuilder builder) {
if (CollectionUtils.isEmpty(paramList)) {
return builder;
}
paramList.forEach(param -> setBool(builder, param, parentType));
paramList.forEach(param -> initBool(builder, param));
return builder;
}

View File

@ -19,12 +19,7 @@ import static cn.easyes.common.constants.BaseEsConstants.*;
public interface Compare<Children, R> extends Serializable {
default <V> Children allEq(Map<String, V> params) {
return allEq(params, true);
}
default <V> Children allEq(Map<String, V> params, boolean null2IsNull) {
return allEq(true, params, null2IsNull);
return allEq(true, params);
}
/**
@ -32,18 +27,14 @@ public interface Compare<Children, R> extends Serializable {
*
* @param condition 执行条件
* @param params map 类型的参数, key 是字段名, value 是字段值
* @param null2IsNull 是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段
* @param <V> ignore
* @return children
*/
<V> Children allEq(boolean condition, Map<String, V> params, boolean null2IsNull);
<V> Children allEq(boolean condition, Map<String, V> params);
default <V> Children allEq(BiPredicate<String, V> filter, Map<String, V> params) {
return allEq(filter, params, true);
}
default <V> Children allEq(BiPredicate<String, V> filter, Map<String, V> params, boolean null2IsNull) {
return allEq(true, filter, params, null2IsNull);
return allEq(true, filter, params);
}
/**
@ -52,11 +43,10 @@ public interface Compare<Children, R> extends Serializable {
* @param condition 执行条件
* @param filter 返回 true 来允许字段传入比对条件中
* @param params map 类型的参数, key 是字段名, value 是字段值
* @param null2IsNull 是否参数为 null 自动执行 isNull 方法, false 则忽略这个字段
* @param <V> ignore
* @return 泛型
*/
<V> Children allEq(boolean condition, BiPredicate<String, V> filter, Map<String, V> params, boolean null2IsNull);
<V> Children allEq(boolean condition, BiPredicate<String, V> filter, Map<String, V> params);
default Children eq(R column, Object val) {
return eq(true, column, val);

View File

@ -79,10 +79,6 @@ public class GlobalConfig {
* data refresh policy 数据刷新策略,默认为NONE
*/
private RefreshPolicy refreshPolicy = RefreshPolicy.NONE;
/**
* must convert to filter must by default, must 条件转filter 默认不转换
*/
private boolean enableMust2Filter = false;
/**
* Batch update threshold 10000 by default 批量更新阈值 默认值为1万
*/

View File

@ -16,4 +16,3 @@ easy-es:
field-strategy: not_empty
refresh-policy: immediate
enable-track-total-hits: true
enable-must2-filter: false

View File

@ -800,24 +800,23 @@ public class AllTest {
@Test
@Order(21)
public void testDSL(){
// 设置检索条件
// SQL写法
// where business_type = 1 and (state = 9 or (state = 8 and bidding_sign = 1)) or business_type = 2 and state in (2,3)
// RestHighLevelClient写法
List<Integer> values = Arrays.asList(2,3);
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.termQuery("business_type",1));
boolQueryBuilder.must(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("state",9))
.should(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("state",8)).must(QueryBuilders.termQuery("bidding_sign",1))));
boolQueryBuilder.should(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("business_type",2)).must(QueryBuilders.termsQuery("state",values)));
System.out.println(boolQueryBuilder);
System.out.println();
// MP及EE写法
LambdaEsQueryWrapper<Document> wrapper = new LambdaEsQueryWrapper<>();
wrapper.eq("business_type",1)
.and(a->a.eq("state",9).or(b->b.eq("state",8).eq("bidding_sign",1))
)
.or(i->i.eq("business_type",2).in("state",2,3));
documentMapper.selectList(wrapper);
}

View File

@ -16,7 +16,6 @@ easy-es:
field-strategy: not_empty
refresh-policy: immediate
enable-track-total-hits: true
enable-must2-filter: false
#logging:
# level:
# tracer: trace