diff --git a/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/GroupField.java b/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/GroupField.java
new file mode 100755
index 0000000000000000000000000000000000000000..954e3d95f7d15d96f6a41369264087542aec3e04
--- /dev/null
+++ b/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/GroupField.java
@@ -0,0 +1,19 @@
+package org.dromara.easyes.annotation;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 数据分组字段
+ */
+@Inherited
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD})
+public @interface GroupField {
+
+ String dateFormat() default "yyyyMM";
+}
diff --git a/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Routing.java b/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Routing.java
new file mode 100755
index 0000000000000000000000000000000000000000..54a079d4094741c4e71d48dbe1c023d1d24eec4c
--- /dev/null
+++ b/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Routing.java
@@ -0,0 +1,22 @@
+package org.dromara.easyes.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 路由字段
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
+public @interface Routing {
+
+ /**
+ * defines how the routing is determined. Can be either the name of a property or a SpEL expression. See the reference
+ * documentation for examples how to use this annotation.
+ */
+ String value();
+}
diff --git a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/ClassPathMapperScanner.java b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/ClassPathMapperScanner.java
index 02d29a5a606bc2471b594402465f65dc8650a0cd..9de8f7e537451feb110ca937e29a96488e7a127f 100644
--- a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/ClassPathMapperScanner.java
+++ b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/ClassPathMapperScanner.java
@@ -18,8 +18,11 @@ import java.util.Set;
* Copyright © 2021 xpc1024 All Rights Reserved
**/
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
- public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
+
+ private Class extends BaseEsMapper> clazz;
+ public ClassPathMapperScanner(BeanDefinitionRegistry registry, Class extends BaseEsMapper> cls) {
super(registry, false);
+ this.clazz = cls;
}
public void registerFilters() {
@@ -65,6 +68,7 @@ public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
+ definition.getConstructorArgumentValues().addGenericArgumentValue(clazz.getName());
definition.setBeanClass(MapperFactoryBean.class);
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
diff --git a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/EsMapperScan.java b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/EsMapperScan.java
index 86b4971a1730da25fa2c851b636924dcb4c54f63..a0ddb4bc3305432e6a7bb5376cb102b44e96d1a1 100644
--- a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/EsMapperScan.java
+++ b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/EsMapperScan.java
@@ -1,6 +1,7 @@
package org.dromara.easyes.starter.register;
+import org.dromara.easyes.core.kernel.BaseEsMapperImpl;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@@ -15,5 +16,19 @@ import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Import(MapperScannerRegister.class)
public @interface EsMapperScan {
- String value();
+ String[] value();
+ String[] basePackages() default {};
+
+ Class>[] basePackageClasses() default {};
+
+ Class> baseMapperClass() default BaseEsMapperImpl.class;
+
+ /**
+ * Returns the postfix to be used when looking up custom repository implementations. Defaults to {@literal Impl}. So
+ * for a repository named {@code PersonRepository} the corresponding implementation class will be looked up scanning
+ * for {@code PersonRepositoryImpl}.
+ * need CustomRepositoryImplementationDetector to support custom implementations Repository
+ * @return {@literal Impl} by default.
+ */
+ String repositoryImplementationPostfix() default "Impl";
}
diff --git a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperFactoryBean.java b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperFactoryBean.java
index e9cf660725f06f757090229f83799c67544032a9..03ce74b25f1b1bfdb85c0be50db9d6a4a3f3cd84 100644
--- a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperFactoryBean.java
+++ b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperFactoryBean.java
@@ -1,8 +1,10 @@
package org.dromara.easyes.starter.register;
+import org.apache.lucene.util.IOUtils;
import org.dromara.easyes.annotation.EsDS;
import org.dromara.easyes.annotation.Intercepts;
import org.dromara.easyes.common.enums.ProcessIndexStrategyEnum;
+import org.dromara.easyes.common.exception.EasyEsException;
import org.dromara.easyes.common.utils.LogUtils;
import org.dromara.easyes.common.utils.RestHighLevelClientUtils;
import org.dromara.easyes.common.utils.TypeUtils;
@@ -10,6 +12,7 @@ import org.dromara.easyes.core.biz.EntityInfo;
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.kernel.BaseEsMapper;
import org.dromara.easyes.core.proxy.EsMapperProxy;
import org.dromara.easyes.core.toolkit.EntityInfoHelper;
import org.dromara.easyes.extension.context.Interceptor;
@@ -19,14 +22,21 @@ import org.dromara.easyes.starter.config.EasyEsConfigProperties;
import org.dromara.easyes.starter.factory.IndexStrategyFactory;
import org.dromara.easyes.starter.service.AutoProcessIndexService;
import org.elasticsearch.client.RestHighLevelClient;
+import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ReflectionUtils;
+import java.beans.Introspector;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Proxy;
+import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
import static org.dromara.easyes.common.constants.BaseEsConstants.ZERO;
import static org.dromara.easyes.common.utils.RestHighLevelClientUtils.DEFAULT_DS;
@@ -36,9 +46,9 @@ import static org.dromara.easyes.common.utils.RestHighLevelClientUtils.DEFAULT_D
*
* Copyright © 2021 xpc1024 All Rights Reserved
**/
-public class MapperFactoryBean implements FactoryBean {
+public class MapperFactoryBean implements FactoryBean {
private Class mapperInterface;
-
+ private Class repositoryBaseClass;
@Autowired
private RestHighLevelClientUtils restHighLevelClientUtils;
@@ -58,12 +68,13 @@ public class MapperFactoryBean implements FactoryBean {
public MapperFactoryBean(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
+ public MapperFactoryBean(Class mapperInterface, Class repositoryBaseClass) {
+ this.mapperInterface = mapperInterface;
+ this.repositoryBaseClass = repositoryBaseClass;
+ }
@Override
public T getObject() throws Exception {
-
- EsMapperProxy esMapperProxy = new EsMapperProxy<>(mapperInterface, new ConcurrentHashMap<>());
-
// 获取实体类
Class> entityClass = TypeUtils.getInterfaceT(mapperInterface, ZERO);
@@ -73,8 +84,15 @@ public class MapperFactoryBean implements FactoryBean {
//获取动态数据源 若未配置多数据源,则使用默认数据源
String restHighLevelClientId = Optional.ofNullable(mapperInterface.getAnnotation(EsDS.class)).map(EsDS::value).orElse(DEFAULT_DS);
RestHighLevelClient client = restHighLevelClientUtils.getClient(restHighLevelClientId);
- BaseCache.initCache(mapperInterface, entityClass, client);
+ // 优先加载自定义实现类
+ //BaseEsMapper esMapper = getCustomizedMapperImpl();
+
+ BaseEsMapper esMapper = getTargetRepositoryViaRef(client, entityClass);
+
+ BaseCache.initCache(mapperInterface, entityClass, esMapper);
+
+ EsMapperProxy esMapperProxy = new EsMapperProxy<>(mapperInterface, new ConcurrentHashMap<>());
// 创建代理
T t = (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, esMapperProxy);
@@ -98,6 +116,29 @@ public class MapperFactoryBean implements FactoryBean {
return interceptorChain.pluginAll(t);
}
+ private R getTargetRepositoryViaRef(Object... constructorArguments) {
+ return instantiateClass(repositoryBaseClass, constructorArguments);
+ }
+
+ private R getCustomizedMapperImpl() {
+ boolean detector = mapperInterface.getDeclaredMethods().length > 0 ||
+ applicationContext.getBean(getImplementationName()) != null;
+ if (detector) {
+ Map map = (Map) applicationContext.getBean(getImplementationName());
+ if (map.size() > 1) {
+ throw new EasyEsException("more than one mapper found");
+ }
+ if (map.size() == 1) {
+ return (R) map.values().iterator().next();
+ }
+ }
+ return null;
+ }
+
+ private String getImplementationName() {
+ return Introspector.decapitalize(mapperInterface.getSimpleName()) + "Impl";
+ }
+
@Override
public Class> getObjectType() {
return this.mapperInterface;
@@ -126,4 +167,27 @@ public class MapperFactoryBean implements FactoryBean {
return interceptorChainHolder.getInterceptorChain();
}
+ protected final R instantiateClass(Class> baseClass, Object... constructorArguments) {
+ Class[] constructorArgumentsType = null;
+ if (constructorArguments.length > 0) {
+ constructorArgumentsType = new Class[constructorArguments.length];
+ for (int i = 0; i < constructorArguments.length; i++) {
+ constructorArgumentsType[i] = constructorArguments[i].getClass();
+ }
+ }
+
+ //ClassUtils.getConstructorIfAvailable(baseClass, constructorArguments);
+ Optional> constructor;
+ try {
+ constructor = Optional.ofNullable(ReflectionUtils.accessibleConstructor(baseClass, constructorArgumentsType));
+ } catch (NoSuchMethodException e) {
+ throw new EasyEsException(e);
+ }
+ return constructor.map(it -> (R) BeanUtils.instantiateClass(it, constructorArguments))
+ .orElseThrow(() -> new IllegalStateException(String.format(
+ "No suitable constructor found on %s to match the given arguments: %s. Make sure you implement a constructor taking these",
+ baseClass, Arrays.stream(constructorArguments).map(Object::getClass).map(ClassUtils::getQualifiedName)
+ .collect(Collectors.joining(", ")))));
+ }
+
}
diff --git a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperScannerRegister.java b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperScannerRegister.java
index 088d4c2e48d3ec53d6e5845ef40f99363f634e38..39d77f80e4b7c5bf4fecd507d3636dbbf069ac8a 100644
--- a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperScannerRegister.java
+++ b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperScannerRegister.java
@@ -2,6 +2,9 @@ package org.dromara.easyes.starter.register;
import org.dromara.easyes.common.utils.EEVersionUtils;
import org.dromara.easyes.common.utils.LogUtils;
+import org.dromara.easyes.core.kernel.BaseEsMapper;
+import org.elasticsearch.common.util.set.Sets;
+import org.omg.CORBA.portable.Streamable;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
@@ -10,12 +13,18 @@ import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
+import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.stream.Collectors;
import static org.dromara.easyes.common.constants.BaseEsConstants.*;
@@ -28,12 +37,51 @@ import static org.dromara.easyes.common.constants.BaseEsConstants.*;
public class MapperScannerRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
private ResourceLoader resourceLoader;
private Environment environment;
+ private AnnotationMetadata configMetadata;
+ private AnnotationAttributes attributes;
+
+ private static final String BASE_PACKAGES = "basePackages";
+ private static final String BASE_PACKAGE_CLASSES = "basePackageClasses";
+ private static final String BASE_MAPPER_CLASS = "baseMapperClass";
+ public Set getBasePackages() {
+ String[] value = attributes.getStringArray("value");
+ String[] basePackages = attributes.getStringArray(BASE_PACKAGES);
+ Class>[] basePackageClasses = attributes.getClassArray(BASE_PACKAGE_CLASSES);
+
+ // Default configuration - return package of annotated class
+ if (value.length == 0 && basePackages.length == 0 && basePackageClasses.length == 0) {
+ String className = configMetadata.getClassName();
+ return Sets.newHashSet(ClassUtils.getPackageName(className));
+ }
+
+ Set packages = new HashSet<>();
+ packages.addAll(Arrays.asList(value));
+ packages.addAll(Arrays.asList(basePackages));
+
+ Arrays.stream(basePackageClasses)//
+ .map(ClassUtils::getPackageName)//
+ .forEach(it -> packages.add(it));
+
+ return packages;
+ }
+
+ public Class extends BaseEsMapper> getBaseMapperClass() {
+ return attributes.getClass(BASE_MAPPER_CLASS);
+ }
+
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
+ @Override
+ public void setEnvironment(Environment environment) {
+ this.environment = environment;
+ }
+ public Class extends Annotation> getAnnotation() {
+ return EsMapperScan.class;
+ }
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Boolean enable = Optional.ofNullable(environment.getProperty(ENABLE_PREFIX)).map(Boolean::parseBoolean).orElse(Boolean.TRUE);
@@ -108,15 +156,21 @@ public class MapperScannerRegister implements ImportBeanDefinitionRegistrar, Res
System.out.println("----------------------------------------------------------->");
}
- AnnotationAttributes mapperScanAttrs = AnnotationAttributes
- .fromMap(importingClassMetadata.getAnnotationAttributes(EsMapperScan.class.getName()));
- if (mapperScanAttrs != null) {
- registerBeanDefinitions(mapperScanAttrs, registry);
+ configMetadata = importingClassMetadata;
+ Map annotationAttributes = importingClassMetadata.getAnnotationAttributes(getAnnotation().getName());
+
+ if (annotationAttributes == null) {
+ throw new IllegalStateException(String.format("Unable to obtain annotation attributes for %s", getAnnotation()));
+ }
+
+ attributes = AnnotationAttributes.fromMap(annotationAttributes);
+ if (attributes != null) {
+ registerBeanDefinitions(attributes, registry);
}
}
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry) {
- ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
+ ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry, getBaseMapperClass());
// this check is needed in Spring 3.1
Optional.ofNullable(resourceLoader).ifPresent(scanner::setResourceLoader);
List basePackages = new ArrayList<>();
@@ -126,12 +180,8 @@ public class MapperScannerRegister implements ImportBeanDefinitionRegistrar, Res
.collect(Collectors.toList()));
scanner.registerFilters();
- scanner.doScan(StringUtils.toStringArray(basePackages));
- }
-
- @Override
- public void setEnvironment(Environment environment) {
- this.environment = environment;
+// scanner.doScan(StringUtils.toStringArray(basePackages));
+ scanner.doScan(StringUtils.toStringArray(getBasePackages()));
}
}
diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/biz/EntityInfo.java b/easy-es-core/src/main/java/org/dromara/easyes/core/biz/EntityInfo.java
index 6536320b1d8eafd1429c1374ff69141911ccf8a4..984f593949e92ca793fb878f96fcf3a55b92a0a9 100644
--- a/easy-es-core/src/main/java/org/dromara/easyes/core/biz/EntityInfo.java
+++ b/easy-es-core/src/main/java/org/dromara/easyes/core/biz/EntityInfo.java
@@ -47,6 +47,12 @@ public class EntityInfo {
* 配置的路由
*/
private String routing;
+ private Field routingField;
+ /**
+ * 配置的索引分片
+ */
+ private String indexShardPattern;
+ private Field groupField;
/**
* 新索引名(由EE在更新索引时自动创建)
*/
diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/cache/BaseCache.java b/easy-es-core/src/main/java/org/dromara/easyes/core/cache/BaseCache.java
index 0fea52d4e21c185a4b810fd0e362ad1e9cc66ead..aa958ba811f59dbc27148a9fb42063909bd7c390 100644
--- a/easy-es-core/src/main/java/org/dromara/easyes/core/cache/BaseCache.java
+++ b/easy-es-core/src/main/java/org/dromara/easyes/core/cache/BaseCache.java
@@ -1,9 +1,12 @@
package org.dromara.easyes.core.cache;
+import lombok.Getter;
+import lombok.Setter;
import org.dromara.easyes.common.constants.BaseEsConstants;
import org.dromara.easyes.common.utils.CollectionUtils;
import org.dromara.easyes.common.utils.ExceptionUtils;
import org.dromara.easyes.core.biz.EntityInfo;
+import org.dromara.easyes.core.kernel.BaseEsMapper;
import org.dromara.easyes.core.kernel.BaseEsMapperImpl;
import org.dromara.easyes.core.toolkit.EntityInfoHelper;
import org.dromara.easyes.core.toolkit.FieldUtils;
@@ -19,15 +22,20 @@ import java.util.concurrent.ConcurrentHashMap;
* Copyright © 2021 xpc1024 All Rights Reserved
**/
public class BaseCache {
+
+ @Getter
+ @Setter
+ private Class> mapperBaseClass;
/**
* 用于存放BaseEsMapper的所有实例
*/
- private static final Map, BaseEsMapperImpl>> baseEsMapperInstanceMap = new ConcurrentHashMap<>();
+ private static final Map, BaseEsMapper>> baseEsMapperInstanceMap = new ConcurrentHashMap<>();
/**
* 用于存放Es entity 中的字段的get/is方法
*/
private static final Map, Map> baseEsEntityMethodMap = new ConcurrentHashMap<>();
+
/**
* 初始化缓存
*
@@ -35,13 +43,12 @@ public class BaseCache {
* @param client es客户端
* @param entityClass 实体类
*/
- public static void initCache(Class> mapperInterface, Class> entityClass, RestHighLevelClient client) {
+ public static void initCache(Class> mapperInterface, Class> entityClass, BaseEsMapper mapper) {
// 初始化baseEsMapper的所有实现类实例
- BaseEsMapperImpl baseEsMapper = new BaseEsMapperImpl();
- baseEsMapper.setClient(client);
-
- baseEsMapper.setEntityClass(entityClass);
- baseEsMapperInstanceMap.put(mapperInterface, baseEsMapper);
+// BaseEsMapper baseEsMapper = new BaseEsMapperImpl(client, entityClass);
+// baseEsMapper.setClient(client);
+// baseEsMapper.setEntityClass(entityClass);
+ baseEsMapperInstanceMap.put(mapperInterface, mapper);
// 初始化entity中所有字段(注解策略生效)
Map invokeMethodsMap = initInvokeMethodsMap(entityClass);
@@ -89,7 +96,7 @@ public class BaseCache {
* @param mapperInterface mapper接口
* @return 实现类
*/
- public static BaseEsMapperImpl> getBaseEsMapperInstance(Class> mapperInterface) {
+ public static BaseEsMapper> getBaseEsMapperInstance(Class> mapperInterface) {
return Optional.ofNullable(baseEsMapperInstanceMap.get(mapperInterface))
.orElseThrow(() -> ExceptionUtils.eee("no such instance", mapperInterface));
}
diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/kernel/BaseEsMapperImpl.java b/easy-es-core/src/main/java/org/dromara/easyes/core/kernel/BaseEsMapperImpl.java
index 7f98d65b8b899cdf9effe03ea63583b7c169d1ef..1a90d233d7e6088a4b9f28ffd168d9f8aa3ce909 100644
--- a/easy-es-core/src/main/java/org/dromara/easyes/core/kernel/BaseEsMapperImpl.java
+++ b/easy-es-core/src/main/java/org/dromara/easyes/core/kernel/BaseEsMapperImpl.java
@@ -5,6 +5,9 @@ import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializeFilter;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
+import lombok.AllArgsConstructor;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.SneakyThrows;
import org.apache.http.util.EntityUtils;
@@ -16,6 +19,7 @@ import org.dromara.easyes.common.constants.BaseEsConstants;
import org.dromara.easyes.common.enums.EsQueryTypeEnum;
import org.dromara.easyes.common.enums.MethodEnum;
import org.dromara.easyes.common.enums.OrderTypeEnum;
+import org.dromara.easyes.common.exception.EasyEsException;
import org.dromara.easyes.common.utils.*;
import org.dromara.easyes.core.biz.*;
import org.dromara.easyes.core.cache.BaseCache;
@@ -61,11 +65,23 @@ import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.metrics.ParsedCardinality;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import java.beans.Introspector;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
@@ -86,14 +102,29 @@ public class BaseEsMapperImpl implements BaseEsMapper {
/**
* restHighLevel client
*/
- @Setter
+ @NonNull
private RestHighLevelClient client;
/**
* T 对应的类
*/
- @Setter
+// @Setter
+ @NonNull
private Class entityClass;
+ public BaseEsMapperImpl(RestHighLevelClient client) {
+ this.client = client;
+ Type type = getClass().getGenericSuperclass();
+ if (type instanceof ParameterizedType) {
+ ParameterizedType paramType = (ParameterizedType) type;
+ this.entityClass = (Class) paramType.getActualTypeArguments()[0];
+ }
+ }
+
+ public BaseEsMapperImpl(RestHighLevelClient client, Class entityType) {
+ this.client = client;
+ this.entityClass = entityType;
+ }
+
@Override
public Class getEntityClass() {
return entityClass;
@@ -329,24 +360,80 @@ public class BaseEsMapperImpl implements BaseEsMapper {
}
}
+ private String getIndexName(EntityInfo entityInfo, T entity) {
+ String baseIndexName = entityInfo.getIndexName();
+ String indexShardSlice;
+ try {
+ indexShardSlice = getIndexShardSlice(entityInfo, entity);
+ } catch (ParseException e) {
+ throw new EasyEsException(e);
+ }
+ return StringUtils.isBlank(indexShardSlice) ? baseIndexName :
+ baseIndexName.concat("-").concat(indexShardSlice);
+ }
+
+ private String getIndexShardSlice(EntityInfo entityInfo, T entity) throws ParseException {
+ String suffix = null;
+ if (Objects.nonNull(entityInfo.getGroupField())) {
+ String slicePattern = entityInfo.getIndexShardPattern();
+ DateFormat format = new SimpleDateFormat(slicePattern);
+ Object groupValue = getFieldValue(entity, entityInfo.getGroupField());
+ if (Objects.nonNull(groupValue)) {
+ if (groupValue instanceof Date) {
+ suffix = format.format(groupValue);
+ } else if (groupValue instanceof LocalDate) {
+ // 转换为java.util.Date
+ Date date = Date.from(((LocalDate) groupValue).atStartOfDay(ZoneId.systemDefault()).toInstant());
+ suffix = format.format(date);
+ } else if (groupValue instanceof String) {
+ suffix = format.format(format.parse(groupValue.toString()));
+ }
+ }
+ }
+ return suffix;
+ }
+
+ private E getFieldValue(T entity, Field field) {
+ try {
+ if (field.getModifiers() != Modifier.PUBLIC) {
+ field.setAccessible(true);
+ }
+ return (E) field.get(entity);
+ } catch (IllegalAccessException e) {
+ throw new EasyEsException(e);
+ }
+ }
+
@Override
public Integer insert(T entity) {
- return insert(null, null, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName());
+ EntityInfo entityInfo = EntityInfoHelper.getEntityInfo(entityClass);
+ // TODO: 2024/8/1 也可以根据@Routing注解支持
+ String routing = entityInfo.getRouting();
+ if (Objects.isNull(routing)) {
+ if (Objects.nonNull(entityInfo.getRoutingField())) {
+// routing = getFieldValue(entity, entityInfo.getRoutingField());
+// entityInfo.setRouting(routing);
+ entityInfo.setRouting(Introspector.decapitalize(entityInfo.getRoutingField().getName()));
+ }
+ }
+ return insert(entityInfo.getRouting(), entity);
}
@Override
public Integer insert(String routing, T entity) {
- return insert(routing, null, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName());
+ return insert(routing, null, entity);
}
@Override
public Integer insert(String routing, String parentId, T entity) {
- return insert(routing, parentId, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName());
+ EntityInfo entityInfo = EntityInfoHelper.getEntityInfo(entityClass);
+ String indexName = getIndexName(entityInfo, entity);
+ return insert(routing, parentId, entity, indexName);
}
@Override
public Integer insert(T entity, String... indexNames) {
- return insert(null, null, entity, indexNames);
+ return insert(null, entity, indexNames);
}
@Override
@@ -366,22 +453,26 @@ public class BaseEsMapperImpl implements BaseEsMapper {
@Override
public Integer insertBatch(Collection entityList) {
- return insertBatch(null, null, entityList, EntityInfoHelper.getEntityInfo(entityClass).getIndexName());
+ return insertBatch(null, entityList);
}
@Override
public Integer insertBatch(String routing, Collection entityList) {
- return insertBatch(routing, entityList, EntityInfoHelper.getEntityInfo(entityClass).getIndexName());
+ return insertBatch(routing, null, entityList);
}
@Override
public Integer insertBatch(String routing, String parentId, Collection entityList) {
- return insertBatch(routing, parentId, entityList, EntityInfoHelper.getEntityInfo(entityClass).getIndexName());
+ String[] indexNames = (String[]) entityList.stream()
+ .map(entity -> getIndexName(EntityInfoHelper.getEntityInfo(entityClass), entity))
+ .distinct()
+ .toArray();
+ return insertBatch(routing, parentId, entityList, indexNames);
}
@Override
public Integer insertBatch(Collection entityList, String... indexNames) {
- return insertBatch(null, null, entityList, indexNames);
+ return insertBatch(null, entityList, indexNames);
}
@Override
@@ -472,12 +563,14 @@ public class BaseEsMapperImpl implements BaseEsMapper {
@Override
public Integer updateById(T entity) {
- return updateById(null, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName());
+ String index = getIndexName(EntityInfoHelper.getEntityInfo(entityClass), entity);
+ return updateById(null, entity, index);
}
@Override
public Integer updateById(String routing, T entity) {
- return updateById(routing, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName());
+ String index = getIndexName(EntityInfoHelper.getEntityInfo(entityClass), entity);
+ return updateById(routing, entity, index);
}
@Override
diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/proxy/EsMapperProxy.java b/easy-es-core/src/main/java/org/dromara/easyes/core/proxy/EsMapperProxy.java
index 2f3335f44b5c9792ed3940863ef37f6b73432255..a0569de527ba8bcf6102caebb3a6f67b5456c29c 100644
--- a/easy-es-core/src/main/java/org/dromara/easyes/core/proxy/EsMapperProxy.java
+++ b/easy-es-core/src/main/java/org/dromara/easyes/core/proxy/EsMapperProxy.java
@@ -1,7 +1,7 @@
package org.dromara.easyes.core.proxy;
import org.dromara.easyes.core.cache.BaseCache;
-import org.dromara.easyes.core.kernel.BaseEsMapperImpl;
+import org.dromara.easyes.core.kernel.BaseEsMapper;
import java.io.Serializable;
import java.lang.invoke.MethodHandle;
@@ -66,7 +66,7 @@ public class EsMapperProxy implements InvocationHandler, Serializable {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- BaseEsMapperImpl> baseEsMapperInstance = BaseCache.getBaseEsMapperInstance(mapperInterface);
+ BaseEsMapper> baseEsMapperInstance = BaseCache.getBaseEsMapperInstance(mapperInterface);
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(baseEsMapperInstance, args);
}
@@ -75,7 +75,7 @@ public class EsMapperProxy implements InvocationHandler, Serializable {
return cachedInvoker(method, baseEsMapperInstance).invoke(proxy, method, args);
}
- private EsMapperMethodInvoker cachedInvoker(Method method, BaseEsMapperImpl> baseEsMapperInstance)
+ private EsMapperMethodInvoker cachedInvoker(Method method, BaseEsMapper> baseEsMapperInstance)
throws Throwable {
try {
return methodCache.computeIfAbsent(method, m -> {
@@ -119,9 +119,9 @@ public class EsMapperProxy implements InvocationHandler, Serializable {
private static class PlainMethodInvoker implements EsMapperMethodInvoker {
- private final BaseEsMapperImpl> baseEsMapperInstance;
+ private final BaseEsMapper> baseEsMapperInstance;
- public PlainMethodInvoker(BaseEsMapperImpl> baseEsMapperInstance) {
+ public PlainMethodInvoker(BaseEsMapper> baseEsMapperInstance) {
this.baseEsMapperInstance = baseEsMapperInstance;
}
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 c9b7ca4810163f2dd2f325a9e5be3662458bbb5b..e59f827de07bd39b22d35ec6f3f8e1aba3001b5c 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
@@ -29,6 +29,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static org.dromara.easyes.common.constants.BaseEsConstants.*;
+import static org.springframework.util.Assert.hasText;
/**
* 实体字段信息工具类
@@ -413,6 +414,33 @@ public class EntityInfoHelper {
entityInfo.getDistanceDecimalPlaces().add(distance.decimalPlaces());
hasAnnotation = true;
}
+
+ // 初始化封装GroupField注解信息
+ if (field.isAnnotationPresent(GroupField.class)) {
+ GroupField group = field.getAnnotation(GroupField.class);
+ if (group != null) {
+ entityInfo.setIndexShardPattern(group.dateFormat());
+ field.setAccessible(true);
+ entityInfo.setGroupField(field);
+ }
+ hasAnnotation = true;
+ }
+ // 初始化封装GroupField注解信息
+ Routing routingAnnotation = null;
+ if (field.isAnnotationPresent(Routing.class)) {
+ routingAnnotation = field.getAnnotation(Routing.class);
+ if (routingAnnotation != null) {
+ hasText(routingAnnotation.value(), "@Routing annotation must contain a non-empty value");
+ field.setAccessible(true);
+ entityInfo.setRoutingField(field);
+ }
+ hasAnnotation = true;
+ } else {
+ routingAnnotation = clazz.getAnnotation(Routing.class);
+ if (routingAnnotation != null) {
+ entityInfo.setRouting(routingAnnotation.value());
+ }
+ }
return hasAnnotation;
}
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 6e3d2327e348a23cdc539a10409234c7c72f8db0..c312822c685683c6a763ba6f595255302b77a5fc 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
@@ -26,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(nodes = {@Node(parentClass = Document.class, childClasses = {Author.class, Comment.class}), @Node(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等.
@@ -37,6 +37,7 @@ public class Document {
/**
* 文档标题,不指定类型默认被创建为keyword类型,可进行精确查询
*/
+ @Routing(value = "title")
private String title;
/**
* 副标题
@@ -66,6 +67,7 @@ public class Document {
/**
* 创建时间
*/
+ @GroupField
@IndexField(fieldType = FieldType.DATE, dateFormat = "yyyy-MM-dd HH:mm:ss")
private String gmtCreate;
/**
diff --git a/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/DocumentMapper.java b/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/DocumentMapper.java
index 59554b0928edb8ff3f9a11daa8c94f75061b4723..4fa3a109e465b1f2311efcb6feb8327f88aa2fae 100644
--- a/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/DocumentMapper.java
+++ b/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/DocumentMapper.java
@@ -24,4 +24,6 @@ public interface DocumentMapper extends BaseEsMapper {
return selectList(wrapper);
}
+ List testCustomerMethod(String id);
+
}
diff --git a/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/impl/DocumentMapperImpl.java b/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/impl/DocumentMapperImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ba984c1ee275a2531976eaa8af9cc2cc1230d42
--- /dev/null
+++ b/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/impl/DocumentMapperImpl.java
@@ -0,0 +1,27 @@
+package org.dromara.easyes.test.mapper.impl;
+
+import org.dromara.easyes.core.kernel.BaseEsMapperImpl;
+import org.dromara.easyes.test.entity.Document;
+import org.dromara.easyes.test.mapper.DocumentMapper;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.springframework.stereotype.Component;
+import org.springframework.test.context.jdbc.Sql;
+
+import java.util.List;
+
+/**
+ *
+ */
+@Component
+public class DocumentMapperImpl extends BaseEsMapperImpl implements DocumentMapper {
+
+ public DocumentMapperImpl(RestHighLevelClient client) {
+ super(client);
+ }
+
+ @Sql("SELECT * FROM easyes_document where esId = ?")
+ @Override
+ public List testCustomerMethod(String id) {
+ return null;
+ }
+}