diff --git a/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Join.java b/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Join.java index 32065ea8..12b863a4 100644 --- a/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Join.java +++ b/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Join.java @@ -5,6 +5,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import static org.dromara.easyes.annotation.rely.AnnotationConstants.DEFAULT_JOIN_FIELD_NAME; + /** * 父子类型 *

@@ -13,8 +15,24 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Join { - String joinField() default "joinField"; + /** + * join字段在es中的名字 + * + * @return 索引中的join字段名称 默认为joinField + */ + String joinField() default DEFAULT_JOIN_FIELD_NAME; + + /** + * 根节点别名 不指定则默认使用加了当前注解的根类的名称小写作为根节点别名(推荐) + * + * @return 根节点别名 + */ String rootAlias() default ""; - Child[] children() default {}; + /** + * 非根节点 + * + * @return 非根节点列表 + */ + Node[] nodes() default {}; } diff --git a/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Child.java b/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Node.java similarity index 75% rename from easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Child.java rename to easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Node.java index c5eb2410..8da47c95 100644 --- a/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Child.java +++ b/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Node.java @@ -12,9 +12,9 @@ import java.lang.annotation.Target; **/ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE}) -public @interface Child { +public @interface Node { /** - * 父文档别名 非必填,默认值为当前类名小写 + * 父文档别名 非必填,不指定时默认值为parentClass类名小写(推荐) * * @return 父文档别名 */ @@ -28,9 +28,9 @@ public @interface Child { Class parentClass(); /** - * 子文档别名列表 + * 子文档别名列表,不指定则为子文档类名小写列表(推荐) 若要自定义必须与childClasses数量和顺序一致 * - * @return 子文档别名列表 非必填,默认值为子文档类名小写 + * @return 子文档别名列表 非必填,默认值为子文档类名小写列表 */ String[] childAliases() default {}; diff --git a/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/rely/AnnotationConstants.java b/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/rely/AnnotationConstants.java index 097d6525..91e1d4aa 100644 --- a/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/rely/AnnotationConstants.java +++ b/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/rely/AnnotationConstants.java @@ -34,4 +34,8 @@ public interface AnnotationConstants { * 默认索引别名 */ String DEFAULT_ALIAS = "ee_default_alias"; + /** + * 默认join字段名称 + */ + String DEFAULT_JOIN_FIELD_NAME = "joinField"; } diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/conditions/function/Func.java b/easy-es-core/src/main/java/org/dromara/easyes/core/conditions/function/Func.java index d7932245..6e2475b7 100644 --- a/easy-es-core/src/main/java/org/dromara/easyes/core/conditions/function/Func.java +++ b/easy-es-core/src/main/java/org/dromara/easyes/core/conditions/function/Func.java @@ -6,6 +6,7 @@ import org.elasticsearch.common.geo.GeoDistance; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.sort.SortBuilder; import org.elasticsearch.search.sort.SortOrder; @@ -13,8 +14,8 @@ import org.elasticsearch.search.sort.SortOrder; import java.io.Serializable; import java.util.*; -import static org.dromara.easyes.common.constants.BaseEsConstants.DEFAULT_BOOST; import static java.util.stream.Collectors.toList; +import static org.dromara.easyes.common.constants.BaseEsConstants.DEFAULT_BOOST; /** * 高阶语法相关 @@ -1704,4 +1705,45 @@ public interface Func extends Serializable { * @return wrapper */ Children mix(boolean condition, QueryBuilder queryBuilder); + + + /** + * 聚合桶排序 + * + * @param bucketOrder 排序规则 + * @return wrapper + */ + default Children bucketOrder(BucketOrder bucketOrder) { + return bucketOrder(true, bucketOrder); + } + + /** + * 聚合桶排序 + * + * @param condition 条件 + * @param bucketOrder 桶排序规则 + * @return wrapper + */ + default Children bucketOrder(boolean condition, BucketOrder bucketOrder) { + return bucketOrder(condition, Arrays.asList(bucketOrder)); + } + + /** + * 聚合桶排序 + * + * @param bucketOrders 排序规则列表 + * @return wrapper + */ + default Children bucketOrder(List bucketOrders) { + return bucketOrder(true, bucketOrders); + } + + /** + * 聚合桶排序 + * + * @param condition 条件 + * @param bucketOrders 排序规则列表 + * @return wrapper + */ + Children bucketOrder(boolean condition, List bucketOrders); } diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/conditions/function/Nested.java b/easy-es-core/src/main/java/org/dromara/easyes/core/conditions/function/Nested.java index 94f4aefa..fd0d2527 100644 --- a/easy-es-core/src/main/java/org/dromara/easyes/core/conditions/function/Nested.java +++ b/easy-es-core/src/main/java/org/dromara/easyes/core/conditions/function/Nested.java @@ -214,6 +214,15 @@ public interface Nested extends Serializable { */ Children hasChild(boolean condition, String type, Consumer consumer, ScoreMode scoreMode); + /** + * 父子类型-根据子查父匹配 返回子文档 无需指定父,由框架根据@Join注解自行推断其父 + * + * @param consumer 嵌套条件函数 + * @return wrapper + */ + default Children hasParent(Consumer consumer) { + return hasParent(true, null, consumer); + } /** * 父子类型-根据子查父匹配 返回子文档 diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/core/AbstractChainWrapper.java b/easy-es-core/src/main/java/org/dromara/easyes/core/core/AbstractChainWrapper.java index 3e8592b5..13336063 100644 --- a/easy-es-core/src/main/java/org/dromara/easyes/core/core/AbstractChainWrapper.java +++ b/easy-es-core/src/main/java/org/dromara/easyes/core/core/AbstractChainWrapper.java @@ -13,6 +13,7 @@ import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.geometry.Geometry; import org.elasticsearch.index.query.Operator; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.sort.SortBuilder; import org.elasticsearch.search.sort.SortOrder; @@ -654,6 +655,13 @@ public abstract class AbstractChainWrapper bucketOrders) { + getWrapper().bucketOrder(condition, bucketOrders); + return typedThis; + } + @Override public Children select(String... columns) { getWrapper().select(columns); diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/core/AbstractWrapper.java b/easy-es-core/src/main/java/org/dromara/easyes/core/core/AbstractWrapper.java index 711c3451..2f03f3b7 100644 --- a/easy-es-core/src/main/java/org/dromara/easyes/core/core/AbstractWrapper.java +++ b/easy-es-core/src/main/java/org/dromara/easyes/core/core/AbstractWrapper.java @@ -17,6 +17,7 @@ import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.geometry.Geometry; import org.elasticsearch.index.query.Operator; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.sort.SortBuilder; import org.elasticsearch.search.sort.SortOrder; @@ -567,6 +568,14 @@ public abstract class AbstractWrapper bucketOrders) { + if (condition) { + this.bucketOrders = bucketOrders; + } + return typedThis; + } + @Override public Children select(String... columns) { this.include = columns; diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/core/BaseEsMapper.java b/easy-es-core/src/main/java/org/dromara/easyes/core/core/BaseEsMapper.java index a59954e3..0452e774 100644 --- a/easy-es-core/src/main/java/org/dromara/easyes/core/core/BaseEsMapper.java +++ b/easy-es-core/src/main/java/org/dromara/easyes/core/core/BaseEsMapper.java @@ -224,11 +224,21 @@ public interface BaseEsMapper { /** * 插入一条记录 可指定路由 * - * @param entity 插入的数据对象 * @param routing 路由 + * @param entity 插入的数据对象 * @return 成功条数 */ - Integer insert(T entity, String routing); + Integer insert(String routing, T entity); + + /** + * 父子类型 插入一条记录 可指定路由, 父id + * + * @param routing 路由 + * @param parentId 父id + * @param entity 插入的数据对象 + * @return 成功条数 + */ + Integer insert(String routing, String parentId, T entity); /** * 插入一条记录,可指定多索引插入 @@ -240,7 +250,7 @@ public interface BaseEsMapper { Integer insert(T entity, String... indexNames); /** - * 插入一条记录,可指定路由及多索引插入 + * 插入数据,可指定路由及多索引插入 * * @param routing 路由 * @param entity 插入的数据对象 @@ -249,6 +259,16 @@ public interface BaseEsMapper { */ Integer insert(String routing, T entity, String... indexNames); + /** + * 父子类型 插入数据,可指定路由,父id及多索引插入 + * + * @param routing 路由 + * @param parentId 父id + * @param entity 插入的数据对象 + * @param indexNames 指定插入的索引名数组 + * @return 总成功条数 + */ + Integer insert(String routing, String parentId, T entity, String... indexNames); /** * 批量插入 @@ -261,11 +281,21 @@ public interface BaseEsMapper { /** * 批量插入 可指定路由 * - * @param entityList 插入的数据对象列表 * @param routing 路由 + * @param entityList 插入的数据对象列表 * @return 总成功条数 */ - Integer insertBatch(Collection entityList, String routing); + Integer insertBatch(String routing, Collection entityList); + + /** + * 父子类型 批量插入 可指定路由, 父id + * + * @param routing 路由 + * @param parentId 父id + * @param entityList 插入的数据对象列表 + * @return 总成功条数 + */ + Integer insertBatch(String routing, String parentId, Collection entityList); /** * 批量插入 可指定多索引 @@ -286,6 +316,17 @@ public interface BaseEsMapper { */ Integer insertBatch(String routing, Collection entityList, String... indexNames); + /** + * 父子类型 批量插入 可指定路由,父id及多索引 + * + * @param routing 路由 + * @param parentId 父id + * @param entityList 插入的数据对象列表 + * @param indexNames 指定插入的索引名数组 + * @return 总成功条数 + */ + Integer insertBatch(String routing, String parentId, Collection entityList, String... indexNames); + /** * 根据 ID 删除 * @@ -297,11 +338,11 @@ public interface BaseEsMapper { /** * 根据 ID 删除 可指定路由 * - * @param id 主键 * @param routing 路由 + * @param id 主键 * @return 成功条数 */ - Integer deleteById(Serializable id, String routing); + Integer deleteById(String routing, Serializable id); /** * 根据 ID 删除 可指定多索引 @@ -334,11 +375,11 @@ public interface BaseEsMapper { /** * 删除(根据ID 批量删除)可指定路由 * - * @param idList 主键列表 * @param routing 路由 + * @param idList 主键列表 * @return 总成功条数 */ - Integer deleteBatchIds(Collection idList, String routing); + Integer deleteBatchIds(String routing, Collection idList); /** * 删除(根据ID 批量删除) @@ -378,11 +419,11 @@ public interface BaseEsMapper { /** * 根据 ID 更新 可指定路由 * - * @param entity 更新对象 * @param routing 路由 + * @param entity 更新对象 * @return 总成功条数 */ - Integer updateById(T entity, String routing); + Integer updateById(String routing, T entity); /** * 根据 ID 更新 可指定多索引 @@ -414,11 +455,11 @@ public interface BaseEsMapper { /** * 根据ID 批量更新 可指定路由 * - * @param entityList 更新对象列表 * @param routing 路由 + * @param entityList 更新对象列表 * @return 总成功条数 */ - Integer updateBatchByIds(Collection entityList, String routing); + Integer updateBatchByIds(String routing, Collection entityList); /** * 根据ID 批量更新 可指定多索引 @@ -459,11 +500,11 @@ public interface BaseEsMapper { /** * 根据 ID 查询 可指定路由 * - * @param id 主键 * @param routing 路由 + * @param id 主键 * @return 指定的返回对象 */ - T selectById(Serializable id, String routing); + T selectById(String routing, Serializable id); /** * 根据 ID 查询 可指定多索引 @@ -493,13 +534,13 @@ public interface BaseEsMapper { List selectBatchIds(Collection idList); /** - * 查询(根据ID 批量查询) + * 查询(根据ID 批量查询) 可指定路由 * - * @param idList 主键列表 * @param routing 路由 + * @param idList 主键列表 * @return 指定的返回对象列表 */ - List selectBatchIds(Collection idList, String routing); + List selectBatchIds(String routing, Collection idList); /** * 查询(根据ID 批量查询) 可指定多索引 diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/core/BaseEsMapperImpl.java b/easy-es-core/src/main/java/org/dromara/easyes/core/core/BaseEsMapperImpl.java index 3af44f6b..df80b932 100644 --- a/easy-es-core/src/main/java/org/dromara/easyes/core/core/BaseEsMapperImpl.java +++ b/easy-es-core/src/main/java/org/dromara/easyes/core/core/BaseEsMapperImpl.java @@ -330,48 +330,66 @@ public class BaseEsMapperImpl implements BaseEsMapper { @Override public Integer insert(T entity) { - Assert.notNull(entity, "insert entity must not be null"); - return insert(null, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); + return insert(null, null, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); } @Override - public Integer insert(T entity, String routing) { - Assert.notNull(entity, "insert entity must not be null"); - return insert(routing, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); + public Integer insert(String routing, T entity) { + return insert(routing, null, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); + } + + @Override + public Integer insert(String routing, String parentId, T entity) { + return insert(routing, parentId, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); } @Override public Integer insert(T entity, String... indexNames) { - return insert(null, entity, indexNames); + return insert(null, null, entity, indexNames); } @Override public Integer insert(String routing, T entity, String... indexNames) { + return insert(routing, null, entity, indexNames); + } + + @Override + public Integer insert(String routing, String parentId, T entity, String... indexNames) { Assert.notNull(entity, "insert entity must not be null"); // 执行插入 return Arrays.stream(getIndexNames(indexNames)) - .mapToInt(indexName -> doInsert(entity, routing, indexName)) + .mapToInt(indexName -> doInsert(entity, routing, parentId, indexName)) .sum(); } @Override public Integer insertBatch(Collection entityList) { - return insertBatch(null, entityList, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); + return insertBatch(null, null, entityList, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); } @Override - public Integer insertBatch(Collection entityList, String routing) { + public Integer insertBatch(String routing, Collection entityList) { return insertBatch(routing, entityList, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); } + @Override + public Integer insertBatch(String routing, String parentId, Collection entityList) { + return insertBatch(routing, parentId, entityList, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); + } + @Override public Integer insertBatch(Collection entityList, String... indexNames) { - return insertBatch(null, entityList, indexNames); + return insertBatch(null, null, entityList, indexNames); } @Override public Integer insertBatch(String routing, Collection entityList, String... indexNames) { + return insertBatch(routing, null, entityList, indexNames); + } + + @Override + public Integer insertBatch(String routing, String parentId, Collection entityList, String... indexNames) { // 老汉裤子都脱了 你告诉我没有数据 怎么*入? if (CollectionUtils.isEmpty(entityList)) { return BaseEsConstants.ZERO; @@ -379,7 +397,7 @@ public class BaseEsMapperImpl implements BaseEsMapper { // 在每条指定的索引上批量执行数据插入 return Arrays.stream(getIndexNames(indexNames)) - .mapToInt(indexName -> doInsertBatch(entityList, routing, indexName)) + .mapToInt(indexName -> doInsertBatch(entityList, routing, parentId, indexName)) .sum(); } @@ -389,7 +407,7 @@ public class BaseEsMapperImpl implements BaseEsMapper { } @Override - public Integer deleteById(Serializable id, String routing) { + public Integer deleteById(String routing, Serializable id) { return deleteById(routing, id, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); } @@ -411,7 +429,7 @@ public class BaseEsMapperImpl implements BaseEsMapper { } @Override - public Integer deleteBatchIds(Collection idList, String routing) { + public Integer deleteBatchIds(String routing, Collection idList) { return deleteBatchIds(routing, idList, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); } @@ -457,7 +475,7 @@ public class BaseEsMapperImpl implements BaseEsMapper { } @Override - public Integer updateById(T entity, String routing) { + public Integer updateById(String routing, T entity) { return updateById(routing, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); } @@ -485,7 +503,7 @@ public class BaseEsMapperImpl implements BaseEsMapper { } @Override - public Integer updateBatchByIds(Collection entityList, String routing) { + public Integer updateBatchByIds(String routing, Collection entityList) { return updateBatchByIds(routing, entityList, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); } @@ -524,7 +542,7 @@ public class BaseEsMapperImpl implements BaseEsMapper { } @Override - public T selectById(Serializable id, String routing) { + public T selectById(String routing, Serializable id) { return selectById(routing, id, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); } @@ -553,7 +571,7 @@ public class BaseEsMapperImpl implements BaseEsMapper { } @Override - public List selectBatchIds(Collection idList, String routing) { + public List selectBatchIds(String routing, Collection idList) { return selectBatchIds(routing, idList, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); } @@ -686,12 +704,14 @@ public class BaseEsMapperImpl implements BaseEsMapper { * * @param entity 插入对象 * @param routing 路由 + * @param parentId 父id * @param indexName 索引名 * @return 成功条数 */ - private Integer doInsert(T entity, String routing, String indexName) { + private Integer doInsert(T entity, String routing, String parentId, String indexName) { // 构建请求入参 - IndexRequest indexRequest = buildIndexRequest(entity, routing, indexName); + IndexRequest indexRequest = buildIndexRequest(entity, routing, parentId, indexName); + Optional.ofNullable(routing).ifPresent(indexRequest::routing); indexRequest.setRefreshPolicy(getRefreshPolicy()); @@ -716,16 +736,17 @@ public class BaseEsMapperImpl implements BaseEsMapper { * * @param entityList 数据列表 * @param routing 路由 + * @param parentId 父id * @param indexName 索引名 * @return 总成功条数 */ - private Integer doInsertBatch(Collection entityList, String routing, String indexName) { + private Integer doInsertBatch(Collection entityList, String routing, String parentId, String indexName) { // 构建批量请求参数 BulkRequest bulkRequest = new BulkRequest(); Optional.ofNullable(routing).ifPresent(bulkRequest::routing); bulkRequest.setRefreshPolicy(getRefreshPolicy()); entityList.forEach(entity -> { - IndexRequest indexRequest = buildIndexRequest(entity, routing, indexName); + IndexRequest indexRequest = buildIndexRequest(entity, routing, parentId, indexName); bulkRequest.add(indexRequest); }); @@ -1033,10 +1054,11 @@ public class BaseEsMapperImpl implements BaseEsMapper { * * @param entity 实体 * @param routing 路由 + * @param parentId 父id * @param indexName 索引名 * @return es请求参数 */ - private IndexRequest buildIndexRequest(T entity, String routing, String indexName) { + private IndexRequest buildIndexRequest(T entity, String routing, String parentId, String indexName) { IndexRequest indexRequest = new IndexRequest(); // id预处理,除下述情况,其它情况使用es默认的id @@ -1058,7 +1080,7 @@ public class BaseEsMapperImpl implements BaseEsMapper { joinField.setName(entityInfo.getJoinAlias()); if (entityInfo.isChild()) { // 子类型,需要追加父 - joinField.setParent(routing); + joinField.setParent(parentId); } jsonObject.put(entityInfo.getJoinFieldName(), joinField); jsonData = jsonObject.toJSONString(); diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/core/Wrapper.java b/easy-es-core/src/main/java/org/dromara/easyes/core/core/Wrapper.java index d842d689..8d3a2daf 100644 --- a/easy-es-core/src/main/java/org/dromara/easyes/core/core/Wrapper.java +++ b/easy-es-core/src/main/java/org/dromara/easyes/core/core/Wrapper.java @@ -4,6 +4,7 @@ package org.dromara.easyes.core.core; import lombok.SneakyThrows; import org.dromara.easyes.core.biz.*; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.builder.SearchSourceBuilder; import java.util.LinkedList; @@ -78,6 +79,11 @@ public abstract class Wrapper implements Cloneable { */ protected List aggregationParamList; + /** + * 聚合桶排序规则列表 + */ + List bucketOrders; + /** * 排序参数列表 */ diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/core/WrapperProcessor.java b/easy-es-core/src/main/java/org/dromara/easyes/core/core/WrapperProcessor.java index 968e995e..82816bbe 100644 --- a/easy-es-core/src/main/java/org/dromara/easyes/core/core/WrapperProcessor.java +++ b/easy-es-core/src/main/java/org/dromara/easyes/core/core/WrapperProcessor.java @@ -21,6 +21,7 @@ import org.elasticsearch.join.query.HasParentQueryBuilder; import org.elasticsearch.join.query.ParentIdQueryBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilders; +import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.collapse.CollapseBuilder; @@ -246,7 +247,9 @@ public class WrapperProcessor { setBool(bool, nestedQueryBuilder, param.getPrevQueryType()); break; case HAS_PARENT: - realField = getRealField(param.getColumn(), mappingColumnMap); + // 如果用户没指定type框架可根据entityInfo上下文自行推断出其父type + String column = Optional.ofNullable(param.getColumn()).orElse(entityInfo.getParentJoinAlias()); + realField = getRealField(column, mappingColumnMap); queryBuilder = getBool(children, QueryBuilders.boolQuery(), entityInfo, param.getColumn()); HasParentQueryBuilder hasParentQueryBuilder = new HasParentQueryBuilder(realField, queryBuilder, (boolean) param.getVal()); setBool(bool, hasParentQueryBuilder, param.getPrevQueryType()); @@ -555,7 +558,7 @@ public class WrapperProcessor { AggregationBuilder cursor = null; for (AggregationParam aggParam : aggregationParamList) { String realField = getRealField(aggParam.getField(), mappingColumnMap); - AggregationBuilder builder = getRealAggregationBuilder(aggParam.getAggregationType(), aggParam.getName(), realField); + AggregationBuilder builder = getRealAggregationBuilder(aggParam.getAggregationType(), aggParam.getName(), realField, wrapper.size, wrapper.bucketOrders); if (aggParam.isEnablePipeline()) { // 管道聚合, 构造聚合树 if (root == null) { @@ -588,9 +591,10 @@ public class WrapperProcessor { * @param aggType 聚合类型 * @param name 聚合返回桶的名称 保持原字段名称 * @param realField 原字段名称 + * @param size 聚合桶大小 * @return 聚合建造者 */ - private static AggregationBuilder getRealAggregationBuilder(AggregationTypeEnum aggType, String name, String realField) { + private static AggregationBuilder getRealAggregationBuilder(AggregationTypeEnum aggType, String name, String realField, Integer size, List bucketOrders) { AggregationBuilder aggregationBuilder; // 解决同一个字段聚合多次,如min(starNum), max(starNum) 字段名重复问题 name += aggType.getValue(); @@ -608,7 +612,10 @@ public class WrapperProcessor { aggregationBuilder = AggregationBuilders.sum(name).field(realField); break; case TERMS: - aggregationBuilder = AggregationBuilders.terms(name).field(realField); + TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms(name).field(realField); + Optional.ofNullable(size).ifPresent(termsAggregationBuilder::size); + Optional.ofNullable(bucketOrders).ifPresent(termsAggregationBuilder::order); + aggregationBuilder = termsAggregationBuilder; break; default: throw new UnsupportedOperationException("不支持的聚合类型,参见AggregationTypeEnum"); diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/toolkit/EntityInfoHelper.java b/easy-es-core/src/main/java/org/dromara/easyes/core/toolkit/EntityInfoHelper.java index 6f37779f..dc4dd0bc 100644 --- a/easy-es-core/src/main/java/org/dromara/easyes/core/toolkit/EntityInfoHelper.java +++ b/easy-es-core/src/main/java/org/dromara/easyes/core/toolkit/EntityInfoHelper.java @@ -107,7 +107,7 @@ public class EntityInfoHelper { */ private static void initJoin(Class clazz, GlobalConfig globalConfig, EntityInfo entityInfo) { Join join = clazz.getAnnotation(Join.class); - if (join == null || ArrayUtils.isEmpty(join.children())) { + if (join == null || ArrayUtils.isEmpty(join.nodes())) { return; } boolean camelCase = globalConfig.getDbConfig().isMapUnderscoreToCamelCase(); @@ -119,7 +119,7 @@ public class EntityInfoHelper { entityInfo.setJoinAlias(underlineJoinAlias); Map> relationMap = entityInfo.getRelationMap(); - Arrays.stream(join.children()) + Arrays.stream(join.nodes()) .forEach(child -> { String parentAlias = StringUtils.isBlank(child.parentAlias()) ? child.parentClass().getSimpleName().toLowerCase() : child.parentAlias(); String underlineParentAlias = camelToUnderline(parentAlias, camelCase); diff --git a/easy-es-sample/src/main/java/org/dromara/easyes/sample/entity/Document.java b/easy-es-sample/src/main/java/org/dromara/easyes/sample/entity/Document.java index 2217523f..b6358673 100644 --- a/easy-es-sample/src/main/java/org/dromara/easyes/sample/entity/Document.java +++ b/easy-es-sample/src/main/java/org/dromara/easyes/sample/entity/Document.java @@ -2,10 +2,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; -import org.dromara.easyes.annotation.IndexName; +import org.dromara.easyes.annotation.*; import org.dromara.easyes.annotation.rely.Analyzer; import org.dromara.easyes.annotation.rely.FieldStrategy; import org.dromara.easyes.annotation.rely.FieldType; @@ -18,7 +15,8 @@ import org.dromara.easyes.annotation.rely.IdType; **/ @Data @Accessors(chain = true) -@IndexName(value = "easyes_document", shardsNum = 3, replicasNum = 2, keepGlobalPrefix = true, maxResultWindow = 100) +@Settings(shardsNum = 3, replicasNum = 2, maxResultWindow = 1000) +@IndexName(value = "easyes_document", keepGlobalPrefix = true) public class Document { /** * es中的唯一id,如果你想自定义es中的id为你提供的id,比如MySQL中的id,请将注解中的type指定为customize或直接在全局配置文件中指定,如此id便支持任意数据类型) diff --git a/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Author.java b/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Author.java index cf335dd1..0ccbdf9e 100644 --- a/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Author.java +++ b/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Author.java @@ -3,26 +3,25 @@ package org.dromara.easyes.test.entity; import lombok.Data; import org.dromara.easyes.annotation.IndexField; -import org.dromara.easyes.annotation.IndexName; -import org.dromara.easyes.annotation.rely.Analyzer; +import org.dromara.easyes.annotation.IndexId; import org.dromara.easyes.annotation.rely.FieldType; /** - * es 作者 数据模型 Document的子文档,Document是其父文档 + * 作者 数据模型 Document的子文档,Document是其父文档 *

- * Copyright © 2021 xpc1024 All Rights Reserved + * Copyright © 2024 xpc1024 All Rights Reserved **/ @Data public class Author { /** * 作者id */ - @IndexField(fieldType = FieldType.KEYWORD) + @IndexId private String authorId; /** * 作者姓名 */ - @IndexField(fieldType = FieldType.TEXT, analyzer = Analyzer.IK_SMART, searchAnalyzer = Analyzer.IK_SMART) + @IndexField(fieldType = FieldType.KEYWORD) private String authorName; } diff --git a/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Contact.java b/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Contact.java index b9d5a07f..2de73225 100644 --- a/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Contact.java +++ b/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Contact.java @@ -3,25 +3,25 @@ package org.dromara.easyes.test.entity; import lombok.Data; import org.dromara.easyes.annotation.IndexField; -import org.dromara.easyes.annotation.IndexName; +import org.dromara.easyes.annotation.IndexId; import org.dromara.easyes.annotation.rely.FieldType; /** - * es 联系方式 数据模型 Author的子文档,Author是其父文档 + * 联系方式 数据模型 Author的子文档,Author是其父文档,Document是其爷文档 *

- * Copyright © 2021 xpc1024 All Rights Reserved + * Copyright © 2024 xpc1024 All Rights Reserved **/ @Data public class Contact { /** - * 手机号 + * 联系人id */ - @IndexField(fieldType = FieldType.KEYWORD) - private String phone; + @IndexId + private String contactId; /** - * 邮箱 + * 地址 */ @IndexField(fieldType = FieldType.TEXT) - private String email; + private String address; } diff --git a/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Document.java b/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Document.java index 94e09ebc..46b8eb63 100644 --- a/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Document.java +++ b/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Document.java @@ -11,7 +11,14 @@ import java.math.BigDecimal; import java.util.List; /** - * es 数据模型 + * es 数据模型 其中Join父子类型结构如下所示 + *

+ *         Document
+ *       /          \
+ *    Comment       Author
+ *                      \
+ *                    Contact
+ * 
*

* Copyright © 2021 xpc1024 All Rights Reserved **/ @@ -19,7 +26,7 @@ import java.util.List; @Accessors(chain = true) @Settings(shardsNum = 3, replicasNum = 2, settingsProvider = MySettingsProvider.class) @IndexName(value = "easyes_document", keepGlobalPrefix = true, refreshPolicy = RefreshPolicy.IMMEDIATE) -@Join(children = {@Child(parentClass = Document.class, childClasses = {Author.class, Comment.class}), @Child(parentClass = Author.class, childClasses = Contact.class)}) +@Join(nodes = {@Node(parentClass = Document.class, childClasses = {Author.class, Comment.class}), @Node(parentClass = Author.class, childClasses = Contact.class)}) public class Document { /** * es中的唯一id,字段名随便起,我这里演示用esId,你也可以用id(推荐),bizId等. diff --git a/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/AuthorMapper.java b/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/AuthorMapper.java index 8703b7ab..d9d666dc 100644 --- a/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/AuthorMapper.java +++ b/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/AuthorMapper.java @@ -1,6 +1,7 @@ package org.dromara.easyes.test.mapper; +import org.dromara.easyes.annotation.EsDS; import org.dromara.easyes.core.core.BaseEsMapper; import org.dromara.easyes.test.entity.Author; @@ -9,5 +10,6 @@ import org.dromara.easyes.test.entity.Author; *

* Copyright © 2024 xpc1024 All Rights Reserved **/ +@EsDS("ds1") public interface AuthorMapper extends BaseEsMapper { } diff --git a/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/ContactMapper.java b/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/ContactMapper.java index d8d1221b..1cac7ecc 100644 --- a/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/ContactMapper.java +++ b/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/ContactMapper.java @@ -1,6 +1,7 @@ package org.dromara.easyes.test.mapper; +import org.dromara.easyes.annotation.EsDS; import org.dromara.easyes.core.core.BaseEsMapper; import org.dromara.easyes.test.entity.Contact; @@ -9,5 +10,6 @@ import org.dromara.easyes.test.entity.Contact; *

* Copyright © 2024 xpc1024 All Rights Reserved **/ +@EsDS("ds1") public interface ContactMapper extends BaseEsMapper { } diff --git a/easy-es-test/src/test/java/org/dromara/easyes/test/join/JoinTest.java b/easy-es-test/src/test/java/org/dromara/easyes/test/join/JoinTest.java index 8ec57c4b..3860de8f 100644 --- a/easy-es-test/src/test/java/org/dromara/easyes/test/join/JoinTest.java +++ b/easy-es-test/src/test/java/org/dromara/easyes/test/join/JoinTest.java @@ -6,9 +6,13 @@ import org.dromara.easyes.core.conditions.update.LambdaEsUpdateWrapper; import org.dromara.easyes.core.toolkit.EntityInfoHelper; import org.dromara.easyes.core.toolkit.FieldUtils; import org.dromara.easyes.test.TestEasyEsApplication; +import org.dromara.easyes.test.entity.Author; import org.dromara.easyes.test.entity.Comment; +import org.dromara.easyes.test.entity.Contact; import org.dromara.easyes.test.entity.Document; +import org.dromara.easyes.test.mapper.AuthorMapper; import org.dromara.easyes.test.mapper.CommentMapper; +import org.dromara.easyes.test.mapper.ContactMapper; import org.dromara.easyes.test.mapper.DocumentMapper; import org.junit.jupiter.api.*; import org.springframework.boot.test.context.SpringBootTest; @@ -17,7 +21,16 @@ import javax.annotation.Resource; import java.util.List; /** - * 父子类型测试 + * 父子类型测试 其结构如下所示,Document文档有子文档Author(作者)和Comment(评论),其中Author还有个子文档Contact(联系方式) + * 下述结构可参考加在Document上的自定义注解@Join和@Node来表达 + *

+ *         Document
+ *       /          \
+ *    Comment       Author
+ *                      \
+ *                    Contact
+ * 
+ * *

* Copyright © 2022 xpc1024 All Rights Reserved **/ @@ -29,6 +42,16 @@ public class JoinTest { private DocumentMapper documentMapper; @Resource private CommentMapper commentMapper; + @Resource + private AuthorMapper authorMapper; + @Resource + private ContactMapper contactMapper; + + /** + * 固定路由,确保后续CRUD中所有父子文档均在统一分片上 + */ + private final static String FIXED_ROUTING = "testRouting"; + @Test @Order(0) @@ -41,29 +64,59 @@ public class JoinTest { @Test @Order(1) public void testInsert() throws InterruptedException { - // 测试新增父子文档,此处开启自动挡模式,父子类型索引已被自动处理 // 新新增父文档,然后再插入子文档 String parentId = "doc-1"; - Document document = new Document(); - // 此处id为自定义id,也可去掉注解@IndexId(type = IdType.CUSTOMIZE),让es自行生成id(推荐), insert完成后 es生成的id直接从document中取获取 - document.setEsId(parentId); - document.setTitle("我是父文档的标题"); - document.setContent("我是父文档的内容 father content "); - documentMapper.insert(document); + Document root = new Document(); + root.setEsId(parentId); + root.setTitle("我是父文档的标题"); + root.setContent("father doc"); + documentMapper.insert(FIXED_ROUTING, root); + Thread.sleep(2000); - // 插入子文档 - Comment comment = new Comment(); - comment.setId("comment-1"); - comment.setCommentContent("test1"); - // 这里特别注意,子文档必须指定其路由为父亲的id,否则傻儿子找不到爹别怪我没提醒 (es语法如此,非框架限制) - commentMapper.insert(comment, parentId); + // 插入子文档1 + Comment nodeA1 = new Comment(); + nodeA1.setId("comment-1"); + nodeA1.setCommentContent("test1"); + // 这里特别注意,子文档必须指定其路由和父亲文档相同,否则傻儿子找不到爹别怪我没提醒 (es语法如此,非框架限制) + commentMapper.insert(FIXED_ROUTING, parentId, nodeA1); // 插入子文档2 - Comment comment1 = new Comment(); - comment1.setId("comment-2"); - comment1.setCommentContent("test2"); - commentMapper.insert(comment1, parentId); + Comment nodeA2 = new Comment(); + nodeA2.setId("comment-2"); + nodeA2.setCommentContent("test2"); + commentMapper.insert(FIXED_ROUTING, parentId, nodeA2); + + // 插入子文档3 + Author nodeB1 = new Author(); + nodeB1.setAuthorId("author-1"); + nodeB1.setAuthorName("tom"); + authorMapper.insert(FIXED_ROUTING, parentId, nodeB1); + + // 插入子文档4 + Author nodeB2 = new Author(); + nodeB2.setAuthorId("author-2"); + nodeB2.setAuthorName("cat"); + authorMapper.insert(FIXED_ROUTING, parentId, nodeB2); + Thread.sleep(2000); + + // 插入孙子文档1(把孙子1挂在子文档3上) + Contact child1 = new Contact(); + child1.setContactId("contact-1"); + child1.setAddress("zhejiang province"); + contactMapper.insert(FIXED_ROUTING, nodeB1.getAuthorId(), child1); + + // 插入孙子文档2(把孙子2挂在子文档3上) + Contact child2 = new Contact(); + child2.setContactId("contact-2"); + child2.setAddress("hangzhou city"); + contactMapper.insert(FIXED_ROUTING, nodeB1.getAuthorId(), child2); + + // 插入孙子文档3(把孙子3挂在子文档4上) + Contact child3 = new Contact(); + child3.setContactId("contact-3"); + child3.setAddress("binjiang region"); + contactMapper.insert(FIXED_ROUTING, nodeB2.getAuthorId(), child3); // es写入数据有延迟 适当休眠 保证后续查询结果正确 Thread.sleep(2000); @@ -72,25 +125,49 @@ public class JoinTest { @Test @Order(2) public void testSelect() { - // 温馨提示,下面wrapper中的type实际上就是JoinField字段注解@TableField中指定的parentName和childName,与原生语法是一致的 + // 温馨提示,下面wrapper中的type实际上就是索引JoinField中指定的父子名称,与原生语法是一致的 // case1: hasChild查询,返回的是相关的父文档 所以查询用父文档实体及其mapper LambdaEsQueryWrapper documentWrapper = new LambdaEsQueryWrapper<>(); documentWrapper.hasChild("comment", w -> w.eq(FieldUtils.val(Comment::getCommentContent), "test1")); List documents = documentMapper.selectList(documentWrapper); System.out.println(documents); + LambdaEsQueryWrapper authorWrapper = new LambdaEsQueryWrapper<>(); + authorWrapper.hasChild("contact", w -> w.match(FieldUtils.val(Contact::getAddress), "city")); + List authors = authorMapper.selectList(authorWrapper); + System.out.println(authors); + // case2: hasParent查询,返回的是相关的子文档 所以查询用子文档实体及其mapper LambdaEsQueryWrapper commentWrapper = new LambdaEsQueryWrapper<>(); + commentWrapper.like(Comment::getCommentContent, "test"); // 字段名称你也可以不用FieldUtils.val,直接传入字符串也行 - commentWrapper.hasParent("document", w -> w.match("content", "father").or().match("content", "content")); + commentWrapper.hasParent("document", w -> w.match("content", "father")); List comments = commentMapper.selectList(commentWrapper); System.out.println(comments); + // case2.1: 孙子查爹的情况 + LambdaEsQueryWrapper contactWrapper = new LambdaEsQueryWrapper<>(); + contactWrapper.hasParent("author", w -> w.eq(FieldUtils.val(Author::getAuthorName), "cat")); + List contacts = contactMapper.selectList(contactWrapper); + System.out.println(contacts); + + // case2.2: 2.1的简写 + LambdaEsQueryWrapper contactWrapper1 = new LambdaEsQueryWrapper<>(); + // hasParent之所以可以不指定parentType简写是因为框架可以通过@Join注解中指定的父子关系自动推断出其父type,因此用户可以不指定父type直接查询,但hasChild不能简写,因为一个父亲可能有多个孩子,但一个孩子只能有一个亲爹 + contactWrapper1.hasParent(w -> w.eq(FieldUtils.val(Author::getAuthorName), "cat")); + List contacts1 = contactMapper.selectList(contactWrapper1); + System.out.println(contacts1); + // case3: parentId查询,返回的是相关的子文档,与case2类似,所以查询用子文档实体及其mapper commentWrapper = new LambdaEsQueryWrapper<>(); commentWrapper.parentId("doc-1", "comment"); List commentList = commentMapper.selectList(commentWrapper); System.out.println(commentList); + + contactWrapper = new LambdaEsQueryWrapper<>(); + contactWrapper.parentId("author-2", "contact"); + List contactList = contactMapper.selectList(contactWrapper); + System.out.println(contactList); } @Test @@ -100,13 +177,19 @@ public class JoinTest { Document document = new Document(); document.setEsId("doc-1"); document.setTitle("我是隔壁老王标题"); - documentMapper.updateById(document); + documentMapper.updateById(FIXED_ROUTING, document); + + Contact contact = new Contact(); + contact.setContactId("contact-2"); + contact.setAddress("update address"); + contactMapper.updateById(FIXED_ROUTING, contact); // case2: 父文档/子文档 根据各自条件更新 Comment comment = new Comment(); - comment.setCommentContent("我是隔壁老王的评论"); + comment.setCommentContent("update comment content"); LambdaEsUpdateWrapper wrapper = new LambdaEsUpdateWrapper<>(); - wrapper.match(Comment::getCommentContent, "comment"); + wrapper.eq(Comment::getCommentContent, "test1"); + wrapper.routing(FIXED_ROUTING); commentMapper.update(comment, wrapper); } @@ -114,11 +197,12 @@ public class JoinTest { @Order(4) public void testDelete() { // case1: 父文档/子文档 根据各自的id删除 - documentMapper.deleteById("doc-1"); + documentMapper.deleteById(FIXED_ROUTING, "doc-1"); //case2: 父文档/子文档 根据各自条件删除 LambdaEsQueryWrapper wrapper = new LambdaEsQueryWrapper<>(); - wrapper.match(Comment::getCommentContent, "comment"); + wrapper.like(Comment::getCommentContent, "test") + .routing(FIXED_ROUTING); commentMapper.delete(wrapper); } diff --git a/easy-es-test/src/test/resources/application.yml b/easy-es-test/src/test/resources/application.yml index 69d30eaa..d7fc1524 100644 --- a/easy-es-test/src/test/resources/application.yml +++ b/easy-es-test/src/test/resources/application.yml @@ -16,7 +16,7 @@ easy-es: field-strategy: not_empty refresh-policy: immediate enable-track-total-hits: true -# index-prefix: dev_ + # index-prefix: dev_ dynamic: datasource: ds1: