list = new ArrayList<>();
+ for (MultiGetItemResponse itemResponse : multiGetResponse.getResponses()) {
+ if (itemResponse.isFailed()) {
+ log.error("通过id获取文档失败", itemResponse.getFailure().getFailure());
+ } else {
+ T entity = toPojo(itemResponse.getResponse(), clazz);
+ if (entity != null) {
+ list.add(entity);
+ }
+ }
+ }
+ return list;
+ }
+
+ public long count(String index, String type, SearchSourceBuilder builder) throws IOException {
+ SearchResponse response = query(index, type, builder);
+ if (response == null || response.status() != RestStatus.OK) {
+ return 0L;
+ }
+ SearchHits searchHits = response.getHits();
+ return searchHits.getTotalHits();
+ }
+
+ public long count(String index, String type, QueryBuilder queryBuilder) throws IOException {
+ SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
+ searchSourceBuilder.query(queryBuilder);
+ return count(index, type, searchSourceBuilder);
+ }
+
+ public SearchResponse query(String index, String type, SearchSourceBuilder builder) throws IOException {
+ SearchRequest request = new SearchRequest(index).types(type);
+ request.source(builder);
+ return client.search(request, RequestOptions.DEFAULT);
+ }
+
+ public SearchResponse query(SearchRequest request) throws IOException {
+ return client.search(request, RequestOptions.DEFAULT);
+ }
+
+ /**
+ * from+size 分页
+ *
+ * 注:在深分页的场景下,效率很低(一般超过 1万条数据就不适用了)
+ */
+ public PageData pojoPage(String index, String type, SearchSourceBuilder builder, Class clazz)
+ throws IOException {
+ SearchResponse response = query(index, type, builder);
+ if (response == null || response.status() != RestStatus.OK) {
+ return null;
+ }
+
+ List content = toPojoList(response, clazz);
+ SearchHits searchHits = response.getHits();
+ int from = builder.from();
+ int size = builder.size();
+ int page = from / size + (from % size == 0 ? 0 : 1) + 1;
+ return new PageData<>(page, size, searchHits.getTotalHits(), content);
+ }
+
+ /**
+ * from+size 分页
+ *
+ * 注:在深分页的场景下,效率很低(一般超过 1万条数据就不适用了)
+ */
+ public PageData pojoPage(String index, String type, int from, int size, QueryBuilder queryBuilder,
+ Class clazz) throws IOException {
+ SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
+ searchSourceBuilder.query(queryBuilder);
+ searchSourceBuilder.from(from);
+ searchSourceBuilder.size(size);
+ return pojoPage(index, type, searchSourceBuilder, clazz);
+ }
+
+ /**
+ * search after 分页
+ */
+ public ScrollData pojoPageByScrollId(String index, String type, String scrollId,
+ int size,
+ QueryBuilder queryBuilder, Class clazz) throws IOException {
+
+ SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
+ searchSourceBuilder.size(size);
+ searchSourceBuilder.sort(BaseEsEntity.DOC_ID, SortOrder.ASC);
+ if (StrUtil.isNotBlank(scrollId)) {
+ BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
+ boolQueryBuilder.must(queryBuilder).must(QueryBuilders.rangeQuery(BaseEsEntity.DOC_ID).gt(scrollId));
+ searchSourceBuilder.query(boolQueryBuilder);
+ } else {
+ searchSourceBuilder.query(queryBuilder);
+ }
+
+ SearchResponse response = query(index, type, searchSourceBuilder);
+ if (response == null || response.status() != RestStatus.OK) {
+ return null;
+ }
+ List content = toPojoList(response, clazz);
+ ScrollData scrollData = new ScrollData<>();
+ scrollData.setSize(size);
+ scrollData.setTotal(response.getHits().getTotalHits());
+ scrollData.setContent(content);
+ if (CollectionUtil.isNotEmpty(content)) {
+ T lastEntity = content.get(content.size() - 1);
+ scrollData.setScrollId(lastEntity.getDocId());
+ }
+ return scrollData;
+ }
+
+ /**
+ * 首次滚动查询批量查询,但是不适用与搜索,仅用于批查询
+ **/
+ public ScrollData pojoScrollBegin(String index, String type, SearchSourceBuilder searchBuilder,
+ Class clazz) throws IOException {
+
+ int scrollTime = 10;
+ final Scroll scroll = new Scroll(TimeValue.timeValueSeconds(scrollTime));
+ SearchRequest request = new SearchRequest(index);
+ request.types(type);
+ request.source(searchBuilder);
+ request.scroll(scroll);
+ SearchResponse response = client.search(request, RequestOptions.DEFAULT);
+ if (response == null || response.status() != RestStatus.OK) {
+ return null;
+ }
+ List content = toPojoList(response, clazz);
+ ScrollData scrollData = new ScrollData<>();
+ scrollData.setSize(searchBuilder.size());
+ scrollData.setTotal(response.getHits().getTotalHits());
+ scrollData.setScrollId(response.getScrollId());
+ scrollData.setContent(content);
+ return scrollData;
+ }
+
+ /**
+ * 知道ScrollId之后,后续根据scrollId批量查询
+ **/
+ public ScrollData pojoScroll(String scrollId, SearchSourceBuilder searchBuilder, Class clazz)
+ throws IOException {
+
+ int scrollTime = 10;
+ final Scroll scroll = new Scroll(TimeValue.timeValueSeconds(scrollTime));
+ SearchScrollRequest request = new SearchScrollRequest(scrollId);
+ request.scroll(scroll);
+ SearchResponse response = client.scroll(request, RequestOptions.DEFAULT);
+ if (response == null || response.status() != RestStatus.OK) {
+ return null;
+ }
+ List content = toPojoList(response, clazz);
+ ScrollData scrollData = new ScrollData<>();
+ scrollData.setSize(searchBuilder.size());
+ scrollData.setTotal(response.getHits().getTotalHits());
+ scrollData.setScrollId(response.getScrollId());
+ scrollData.setContent(content);
+ return scrollData;
+ }
+
+ public boolean pojoScrollEnd(String scrollId) throws IOException {
+ ClearScrollRequest request = new ClearScrollRequest();
+ request.addScrollId(scrollId);
+ ClearScrollResponse response = client.clearScroll(request, RequestOptions.DEFAULT);
+ if (response != null) {
+ return response.isSucceeded();
+ }
+ return false;
+ }
+
+ public T toPojo(GetResponse response, Class clazz) {
+ if (null == response || StrUtil.isBlank(response.getSourceAsString())) {
+ return null;
+ } else {
+ return JsonUtil.toBean(response.getSourceAsString(), clazz);
+ }
+ }
+
+ public List toPojoList(SearchResponse response, Class clazz) {
+ if (response == null || response.status() != RestStatus.OK) {
+ return new ArrayList<>(0);
+ }
+ if (ArrayUtil.isEmpty(response.getHits().getHits())) {
+ return new ArrayList<>(0);
+ }
+ return Stream.of(response.getHits().getHits())
+ .map(hit -> JsonUtil.toBean(hit.getSourceAsString(), clazz))
+ .collect(Collectors.toList());
+ }
+
+ public Map toMap(T entity) {
+ return JsonUtil.toMap(JsonUtil.toString(entity));
+ }
+
+ @Override
+ public synchronized void close() {
+ if (null == client) {
+ return;
+ }
+ IoUtil.close(client);
+ }
+
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/config/ElasticsearchConfig.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/config/ElasticsearchConfig.java
new file mode 100644
index 00000000..791bbf1c
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/config/ElasticsearchConfig.java
@@ -0,0 +1,44 @@
+package io.github.dunwu.javadb.elasticsearch.config;
+
+import cn.hutool.core.util.StrUtil;
+import io.github.dunwu.javadb.elasticsearch.ElasticsearchFactory;
+import io.github.dunwu.javadb.elasticsearch.ElasticsearchTemplate;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+/**
+ * ES 配置
+ *
+ * @author Zhang Peng
+ * @date 2024-02-07
+ */
+@Configuration
+@ComponentScan(value = "io.github.dunwu.javadb.elasticsearch.mapper")
+public class ElasticsearchConfig {
+
+ @Value("${es.hosts:#{null}}")
+ private String hostsConfig;
+
+ @Bean("restHighLevelClient")
+ @ConditionalOnMissingBean
+ public RestHighLevelClient restHighLevelClient() {
+ if (hostsConfig == null) {
+ return ElasticsearchFactory.newRestHighLevelClient();
+ } else {
+ List hosts = StrUtil.split(hostsConfig, ",");
+ return ElasticsearchFactory.newRestHighLevelClient(hosts);
+ }
+ }
+
+ @Bean("elasticsearchTemplate")
+ public ElasticsearchTemplate elasticsearchTemplate(RestHighLevelClient restHighLevelClient) {
+ return ElasticsearchFactory.newElasticsearchTemplate(restHighLevelClient);
+ }
+
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/config/EnableElasticsearch.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/config/EnableElasticsearch.java
new file mode 100644
index 00000000..c2c24479
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/config/EnableElasticsearch.java
@@ -0,0 +1,26 @@
+package io.github.dunwu.javadb.elasticsearch.config;
+
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.context.annotation.Import;
+
+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;
+
+/**
+ * 启动 Elasticsearch 配置注解
+ *
+ * @author Zhang Peng
+ * @date 2023-06-30
+ */
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@EnableAspectJAutoProxy(
+ proxyTargetClass = false
+)
+@Import({ ElasticsearchConfig.class })
+@Documented
+public @interface EnableElasticsearch {
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/constant/CodeMsg.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/constant/CodeMsg.java
new file mode 100644
index 00000000..96e46f6c
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/constant/CodeMsg.java
@@ -0,0 +1,15 @@
+package io.github.dunwu.javadb.elasticsearch.constant;
+
+/**
+ * 请求 / 应答状态接口
+ *
+ * @author Zhang Peng
+ * @since 2019-06-06
+ */
+public interface CodeMsg {
+
+ int getCode();
+
+ String getMsg();
+
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/constant/ResultCode.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/constant/ResultCode.java
new file mode 100644
index 00000000..d4822fb8
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/constant/ResultCode.java
@@ -0,0 +1,97 @@
+package io.github.dunwu.javadb.elasticsearch.constant;
+
+import cn.hutool.core.util.StrUtil;
+
+import java.util.stream.Stream;
+
+/**
+ * 系统级错误码
+ *
+ * @author Zhang Peng
+ * @see HTTP 状态码
+ * @see 腾讯开放平台错误码
+ * @see 新浪开放平台错误码
+ * @see 支付宝开放平台API
+ * @see 微信开放平台错误码
+ * @since 2019-04-11
+ */
+public enum ResultCode implements CodeMsg {
+
+ OK(0, "成功"),
+
+ PART_OK(1, "部分成功"),
+
+ FAIL(-1, "失败"),
+
+ // -----------------------------------------------------
+ // 系统级错误码
+ // -----------------------------------------------------
+
+ ERROR(1000, "服务器错误"),
+
+ PARAM_ERROR(1001, "参数错误"),
+
+ TASK_ERROR(1001, "调度任务错误"),
+
+ CONFIG_ERROR(1003, "配置错误"),
+
+ REQUEST_ERROR(1004, "请求错误"),
+
+ IO_ERROR(1005, "IO 错误"),
+
+ // -----------------------------------------------------
+ // 2000 ~ 2999 数据库错误
+ // -----------------------------------------------------
+
+ DATA_ERROR(2000, "数据库错误"),
+
+ // -----------------------------------------------------
+ // 3000 ~ 3999 三方错误
+ // -----------------------------------------------------
+
+ THIRD_PART_ERROR(3000, "三方错误"),
+
+ // -----------------------------------------------------
+ // 3000 ~ 3999 认证错误
+ // -----------------------------------------------------
+
+ AUTH_ERROR(4000, "认证错误");
+
+ private final int code;
+
+ private final String msg;
+
+ ResultCode(int code, String msg) {
+ this.code = code;
+ this.msg = msg;
+ }
+
+ @Override
+ public int getCode() {
+ return code;
+ }
+
+ @Override
+ public String getMsg() {
+ return msg;
+ }
+
+ public static String getNameByCode(int code) {
+ return Stream.of(ResultCode.values()).filter(item -> item.getCode() == code).findFirst()
+ .map(ResultCode::getMsg).orElse(null);
+ }
+
+ public static ResultCode getEnumByCode(int code) {
+ return Stream.of(ResultCode.values()).filter(item -> item.getCode() == code).findFirst().orElse(null);
+ }
+
+ public static String getTypeInfo() {
+ StringBuilder sb = new StringBuilder();
+ ResultCode[] types = ResultCode.values();
+ for (ResultCode type : types) {
+ sb.append(StrUtil.format("{}:{}, ", type.getCode(), type.getMsg()));
+ }
+ return sb.toString();
+ }
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/entity/BaseEsEntity.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/entity/BaseEsEntity.java
new file mode 100644
index 00000000..32206066
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/entity/BaseEsEntity.java
@@ -0,0 +1,37 @@
+package io.github.dunwu.javadb.elasticsearch.entity;
+
+import lombok.Data;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * ES 实体接口
+ *
+ * @author Zhang Peng
+ * @since 2023-06-28
+ */
+@Data
+@ToString
+public abstract class BaseEsEntity implements Serializable {
+
+ public static final String DOC_ID = "docId";
+
+ /**
+ * 获取版本
+ */
+ protected Long version;
+
+ protected Float hitScore;
+
+ public abstract String getDocId();
+
+ public static Map getPropertiesMap() {
+ Map map = new LinkedHashMap<>(1);
+ map.put(BaseEsEntity.DOC_ID, "keyword");
+ return map;
+ }
+
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/entity/User.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/entity/User.java
new file mode 100644
index 00000000..b21b229c
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/entity/User.java
@@ -0,0 +1,44 @@
+package io.github.dunwu.javadb.elasticsearch.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 短剧、长视频消费数据 ES 实体
+ *
+ * @author Zhang Peng
+ * @date 2024-04-02
+ */
+@Builder
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class User extends BaseEsEntity implements Serializable {
+
+ private String id;
+ private String name;
+ private Integer age;
+
+ @Override
+ public String getDocId() {
+ return id;
+ }
+
+ public static Map getPropertiesMap() {
+ Map map = new LinkedHashMap<>();
+ map.put(BaseEsEntity.DOC_ID, "keyword");
+ map.put("id", "long");
+ map.put("name", "keyword");
+ map.put("age", "integer");
+ return map;
+ }
+
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/entity/common/PageData.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/entity/common/PageData.java
new file mode 100644
index 00000000..e436f8b6
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/entity/common/PageData.java
@@ -0,0 +1,37 @@
+package io.github.dunwu.javadb.elasticsearch.entity.common;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 分页实体
+ *
+ * @author Zhang Peng
+ * @date 2023-06-28
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class PageData implements Serializable {
+
+ private int page;
+ private int size;
+ private long total;
+ private List content = new ArrayList<>();
+
+ public PageData(int page, int size, long total) {
+ this.total = total;
+ this.page = page;
+ this.size = size;
+ }
+
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/entity/common/ScrollData.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/entity/common/ScrollData.java
new file mode 100644
index 00000000..4f90cb85
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/entity/common/ScrollData.java
@@ -0,0 +1,30 @@
+package io.github.dunwu.javadb.elasticsearch.entity.common;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+/**
+ * Hbase 滚动数据实体
+ *
+ * @author Zhang Peng
+ * @date 2023-11-16
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class ScrollData implements Serializable {
+
+ private String scrollId;
+ private int size = 10;
+ private long total = 0L;
+ private Collection content;
+
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/exception/CodeMsgException.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/exception/CodeMsgException.java
new file mode 100644
index 00000000..98ab1995
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/exception/CodeMsgException.java
@@ -0,0 +1,128 @@
+package io.github.dunwu.javadb.elasticsearch.exception;
+
+import cn.hutool.core.util.StrUtil;
+import io.github.dunwu.javadb.elasticsearch.constant.CodeMsg;
+import io.github.dunwu.javadb.elasticsearch.constant.ResultCode;
+
+/**
+ * 基础异常
+ *
+ * @author Zhang Peng
+ * @since 2021-09-25
+ */
+public class CodeMsgException extends RuntimeException implements CodeMsg {
+
+ private static final long serialVersionUID = 6146660782281445735L;
+
+ /**
+ * 状态码
+ */
+ protected int code;
+
+ /**
+ * 响应信息
+ */
+ protected String msg;
+
+ /**
+ * 提示信息
+ */
+ protected String toast;
+
+ public CodeMsgException() {
+ this(ResultCode.FAIL);
+ }
+
+ public CodeMsgException(CodeMsg codeMsg) {
+ this(codeMsg.getCode(), codeMsg.getMsg());
+ }
+
+ public CodeMsgException(CodeMsg codeMsg, String msg) {
+ this(codeMsg.getCode(), msg, null);
+ }
+
+ public CodeMsgException(CodeMsg codeMsg, String msg, String toast) {
+ this(codeMsg.getCode(), msg, toast);
+ }
+
+ public CodeMsgException(String msg) {
+ this(ResultCode.FAIL, msg);
+ }
+
+ public CodeMsgException(int code, String msg) {
+ this(code, msg, msg);
+ }
+
+ public CodeMsgException(int code, String msg, String toast) {
+ super(msg);
+ setCode(code);
+ setMsg(msg);
+ setToast(toast);
+ }
+
+ public CodeMsgException(Throwable cause) {
+ this(cause, ResultCode.FAIL);
+ }
+
+ public CodeMsgException(Throwable cause, String msg) {
+ this(cause, ResultCode.FAIL, msg);
+ }
+
+ public CodeMsgException(Throwable cause, CodeMsg codeMsg) {
+ this(cause, codeMsg.getCode(), codeMsg.getMsg());
+ }
+
+ public CodeMsgException(Throwable cause, CodeMsg codeMsg, String msg) {
+ this(cause, codeMsg.getCode(), msg, null);
+ }
+
+ public CodeMsgException(Throwable cause, CodeMsg codeMsg, String msg, String toast) {
+ this(cause, codeMsg.getCode(), msg, toast);
+ }
+
+ public CodeMsgException(Throwable cause, int code, String msg) {
+ this(cause, code, msg, null);
+ }
+
+ public CodeMsgException(Throwable cause, int code, String msg, String toast) {
+ super(msg, cause);
+ setCode(code);
+ setMsg(msg);
+ setToast(toast);
+ }
+
+ @Override
+ public String getMessage() {
+ if (StrUtil.isNotBlank(msg)) {
+ return StrUtil.format("[{}]{}", code, msg);
+ }
+ return super.getMessage();
+ }
+
+ @Override
+ public int getCode() {
+ return code;
+ }
+
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ @Override
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public String getToast() {
+ return toast;
+ }
+
+ public void setToast(String toast) {
+ this.toast = toast;
+ }
+
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/exception/DefaultException.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/exception/DefaultException.java
new file mode 100644
index 00000000..14908e39
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/exception/DefaultException.java
@@ -0,0 +1,72 @@
+package io.github.dunwu.javadb.elasticsearch.exception;
+
+import io.github.dunwu.javadb.elasticsearch.constant.CodeMsg;
+import io.github.dunwu.javadb.elasticsearch.constant.ResultCode;
+
+/**
+ * 默认异常
+ *
+ * @author Zhang Peng
+ * @since 2021-12-30
+ */
+public class DefaultException extends CodeMsgException {
+
+ private static final long serialVersionUID = -7027578114976830416L;
+
+ public DefaultException() {
+ this(ResultCode.FAIL);
+ }
+
+ public DefaultException(CodeMsg codeMsg) {
+ this(codeMsg.getCode(), codeMsg.getMsg());
+ }
+
+ public DefaultException(CodeMsg codeMsg, String msg) {
+ this(codeMsg.getCode(), msg, null);
+ }
+
+ public DefaultException(CodeMsg codeMsg, String msg, String toast) {
+ this(codeMsg.getCode(), msg, toast);
+ }
+
+ public DefaultException(String msg) {
+ this(ResultCode.FAIL, msg);
+ }
+
+ public DefaultException(int code, String msg) {
+ this(code, msg, msg);
+ }
+
+ public DefaultException(int code, String msg, String toast) {
+ super(code, msg, toast);
+ }
+
+ public DefaultException(Throwable cause) {
+ this(cause, ResultCode.FAIL);
+ }
+
+ public DefaultException(Throwable cause, String msg) {
+ this(cause, ResultCode.FAIL, msg);
+ }
+
+ public DefaultException(Throwable cause, CodeMsg codeMsg) {
+ this(cause, codeMsg.getCode(), codeMsg.getMsg());
+ }
+
+ public DefaultException(Throwable cause, CodeMsg codeMsg, String msg) {
+ this(cause, codeMsg.getCode(), msg, null);
+ }
+
+ public DefaultException(Throwable cause, CodeMsg codeMsg, String msg, String toast) {
+ this(cause, codeMsg.getCode(), msg, toast);
+ }
+
+ public DefaultException(Throwable cause, int code, String msg) {
+ this(cause, code, msg, null);
+ }
+
+ public DefaultException(Throwable cause, int code, String msg, String toast) {
+ super(cause, code, msg, toast);
+ }
+
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/mapper/BaseDynamicEsMapper.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/mapper/BaseDynamicEsMapper.java
new file mode 100644
index 00000000..c75c6cc6
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/mapper/BaseDynamicEsMapper.java
@@ -0,0 +1,331 @@
+package io.github.dunwu.javadb.elasticsearch.mapper;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import io.github.dunwu.javadb.elasticsearch.ElasticsearchTemplate;
+import io.github.dunwu.javadb.elasticsearch.constant.ResultCode;
+import io.github.dunwu.javadb.elasticsearch.entity.BaseEsEntity;
+import io.github.dunwu.javadb.elasticsearch.entity.common.PageData;
+import io.github.dunwu.javadb.elasticsearch.entity.common.ScrollData;
+import io.github.dunwu.javadb.elasticsearch.exception.DefaultException;
+import lombok.extern.slf4j.Slf4j;
+import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.action.bulk.BulkResponse;
+import org.elasticsearch.action.get.GetResponse;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 动态 ES Mapper 基础类(以时间为维度动态创建、删除 index),用于数据量特别大,需要按照日期分片的索引。
+ *
+ * 注:使用此 Mapper 的索引、别名必须遵循命名格式:索引名 = 别名_yyyyMMdd
+ *
+ * @author Zhang Peng
+ * @date 2024-04-07
+ */
+@Slf4j
+public abstract class BaseDynamicEsMapper extends BaseEsMapper {
+
+ public BaseDynamicEsMapper(ElasticsearchTemplate elasticsearchTemplate) {
+ super(elasticsearchTemplate);
+ }
+
+ @Override
+ public boolean enableAutoCreateIndex() {
+ return true;
+ }
+
+ // ====================================================================
+ // 索引管理操作
+ // ====================================================================
+
+ public String getIndex(String day) {
+
+ String alias = getAlias();
+ if (StrUtil.isBlank(day)) {
+ String msg = StrUtil.format("【ES】获取 {} 索引失败!day 不能为空!", alias);
+ throw new DefaultException(ResultCode.PARAM_ERROR, msg);
+ }
+
+ DateTime date;
+ try {
+ date = DateUtil.parse(day, DatePattern.NORM_DATE_PATTERN);
+ } catch (Exception e) {
+ String msg = StrUtil.format("【ES】获取 {} 索引失败!day: {} 不符合日期格式 {}!",
+ alias, day, DatePattern.NORM_DATE_PATTERN);
+ throw new DefaultException(e, ResultCode.PARAM_ERROR, msg);
+ }
+
+ String formatDate = DateUtil.format(date, DatePattern.PURE_DATE_FORMAT);
+ return alias + "_" + formatDate;
+ }
+
+ public boolean isIndexExistsInDay(String day) {
+ if (StrUtil.isBlank(day)) {
+ return false;
+ }
+ String index = getIndex(day);
+ try {
+ return elasticsearchTemplate.isIndexExists(getIndex(day));
+ } catch (Exception e) {
+ log.error("【ES】判断索引是否存在异常!index: {}", index, e);
+ return false;
+ }
+ }
+
+ public String createIndexIfNotExistsInDay(String day) {
+ String index = getIndex(day);
+ String type = getType();
+ String alias = getAlias();
+ int shard = getShard();
+ int replica = getReplica();
+ return createIndex(index, type, alias, shard, replica);
+ }
+
+ public void deleteIndexInDay(String day) {
+ String index = getIndex(day);
+ try {
+ log.info("【ES】删除索引成功!index: {}", index);
+ elasticsearchTemplate.deleteIndex(index);
+ } catch (Exception e) {
+ log.error("【ES】删除索引异常!index: {}", index, e);
+ }
+ }
+
+ public void updateAliasInDay(String day) {
+ String index = getIndex(day);
+ String alias = getAlias();
+ try {
+ log.info("【ES】更新别名成功!alias: {} -> index: {}", alias, index);
+ elasticsearchTemplate.updateAlias(index, alias);
+ } catch (IOException e) {
+ log.error("【ES】更新别名异常!alias: {} -> index: {}", alias, index, e);
+ }
+ }
+
+ // ====================================================================
+ // CRUD 操作
+ // ====================================================================
+
+ public GetResponse getByIdInDay(String day, String id) {
+ String index = getIndex(day);
+ String type = getType();
+ try {
+ return elasticsearchTemplate.getById(index, type, id, null);
+ } catch (IOException e) {
+ log.error("【ES】根据ID查询异常!index: {}, type: {}, id: {}", index, type, id, e);
+ return null;
+ }
+ }
+
+ public T pojoByIdInDay(String day, String id) {
+ String index = getIndex(day);
+ String type = getType();
+ try {
+ return elasticsearchTemplate.pojoById(index, type, id, null, getEntityClass());
+ } catch (IOException e) {
+ log.error("【ES】根据ID查询POJO异常!index: {}, type: {}, id: {}", index, type, id, e);
+ return null;
+ }
+ }
+
+ public List pojoListByIdsInDay(String day, Collection ids) {
+ String index = getIndex(day);
+ String type = getType();
+ try {
+ return elasticsearchTemplate.pojoListByIds(index, type, ids, getEntityClass());
+ } catch (IOException e) {
+ log.error("【ES】根据ID查询POJO列表异常!index: {}, type: {}, ids: {}", index, type, ids, e);
+ return new ArrayList<>(0);
+ }
+ }
+
+ public long countInDay(String day, SearchSourceBuilder builder) {
+ String index = getIndex(day);
+ String type = getType();
+ try {
+ return elasticsearchTemplate.count(index, type, builder);
+ } catch (IOException e) {
+ log.error("【ES】获取匹配记录数异常!index: {}, type: {}", index, type, e);
+ return 0L;
+ }
+ }
+
+ public SearchResponse queryInDay(String day, SearchSourceBuilder builder) {
+ String index = getIndex(day);
+ String type = getType();
+ try {
+ return elasticsearchTemplate.query(index, type, builder);
+ } catch (IOException e) {
+ log.error("【ES】条件查询异常!index: {}, type: {}", index, type, e);
+ return null;
+ }
+ }
+
+ public PageData pojoPageInDay(String day, SearchSourceBuilder builder) {
+ String index = getIndex(day);
+ String type = getType();
+ try {
+ return elasticsearchTemplate.pojoPage(index, type, builder, getEntityClass());
+ } catch (IOException e) {
+ log.error("【ES】from + size 分页条件查询异常!index: {}, type: {}", index, type, e);
+ return null;
+ }
+ }
+
+ public ScrollData pojoPageByLastIdInDay(String day, String scrollId, int size, QueryBuilder queryBuilder) {
+ String index = getIndex(day);
+ String type = getType();
+ try {
+ return elasticsearchTemplate.pojoPageByScrollId(index, type, scrollId, size, queryBuilder, getEntityClass());
+ } catch (IOException e) {
+ log.error("【ES】search after 分页条件查询异常!index: {}, type: {}", index, type, e);
+ return null;
+ }
+ }
+
+ public ScrollData pojoScrollBeginInDay(String day, SearchSourceBuilder builder) {
+ String index = getIndex(day);
+ String type = getType();
+ try {
+ return elasticsearchTemplate.pojoScrollBegin(index, type, builder, getEntityClass());
+ } catch (IOException e) {
+ log.error("【ES】开启滚动分页条件查询异常!index: {}, type: {}", index, type, e);
+ return null;
+ }
+ }
+
+ /**
+ * 根据日期动态选择索引并更新
+ *
+ * @param day 日期,格式为:yyyy-MM-dd
+ * @param entity 待更新的数据
+ * @return /
+ */
+ public T saveInDay(String day, T entity) {
+ if (StrUtil.isBlank(day) || entity == null) {
+ return null;
+ }
+ String index = getIndex(day);
+ String type = getType();
+ try {
+ checkIndex(day);
+ checkData(entity);
+ return elasticsearchTemplate.save(index, getType(), entity);
+ } catch (IOException e) {
+ log.error("【ES】添加数据异常!index: {}, type: {}, entity: {}", index, type, JSONUtil.toJsonStr(entity), e);
+ return null;
+ }
+ }
+
+ /**
+ * 根据日期动态选择索引并批量更新
+ *
+ * @param day 日期,格式为:yyyy-MM-dd
+ * @param list 待更新的数据
+ * @return /
+ */
+ public boolean saveBatchInDay(String day, Collection list) {
+ if (StrUtil.isBlank(day) || CollectionUtil.isEmpty(list)) {
+ return false;
+ }
+ String index = getIndex(day);
+ String type = getType();
+ try {
+ checkIndex(day);
+ checkData(list);
+ return elasticsearchTemplate.saveBatch(index, type, list);
+ } catch (IOException e) {
+ log.error("【ES】批量添加数据异常!index: {}, type: {}, size: {}", index, type, list.size(), e);
+ return false;
+ }
+ }
+
+ public void asyncSaveBatchInDay(String day, Collection list) {
+ asyncSaveBatchInDay(day, list, DEFAULT_BULK_LISTENER);
+ }
+
+ public void asyncSaveBatchInDay(String day, Collection list, ActionListener listener) {
+ if (StrUtil.isBlank(day) || CollectionUtil.isEmpty(list)) {
+ return;
+ }
+ String index = getIndex(day);
+ String type = getType();
+ try {
+ checkIndex(day);
+ checkData(list);
+ elasticsearchTemplate.asyncSaveBatch(index, type, list, listener);
+ } catch (Exception e) {
+ log.error("【ES】异步批量添加数据异常!index: {}, type: {}, size: {}", index, type, list.size(), e);
+ }
+ }
+
+ public void asyncUpdateBatchIdsInDay(String day, Collection list) {
+ asyncUpdateBatchIdsInDay(day, list, DEFAULT_BULK_LISTENER);
+ }
+
+ public void asyncUpdateBatchIdsInDay(String day, Collection list, ActionListener listener) {
+ if (StrUtil.isBlank(day) || CollectionUtil.isEmpty(list)) {
+ return;
+ }
+ String index = getIndex(day);
+ String type = getType();
+ try {
+ checkData(list);
+ elasticsearchTemplate.asyncUpdateBatchIds(index, type, list, listener);
+ } catch (Exception e) {
+ log.error("【ES】异步批量更新数据异常!index: {}, type: {}, size: {}", index, type, list.size(), e);
+ }
+ }
+
+ public boolean deleteByIdInDay(String day, String id) {
+ if (StrUtil.isBlank(day) || StrUtil.isBlank(id)) {
+ return false;
+ }
+ String index = getIndex(day);
+ String type = getType();
+ try {
+ return elasticsearchTemplate.deleteById(index, type, id);
+ } catch (IOException e) {
+ log.error("【ES】根据ID删除数据异常!index: {}, type: {}, id: {}", index, type, id, e);
+ return false;
+ }
+ }
+
+ public boolean deleteBatchIdsInDay(String day, Collection ids) {
+ if (StrUtil.isBlank(day) || CollectionUtil.isEmpty(ids)) {
+ return false;
+ }
+ String index = getIndex(day);
+ String type = getType();
+ try {
+ return elasticsearchTemplate.deleteBatchIds(index, type, ids);
+ } catch (IOException e) {
+ log.error("【ES】根据ID批量删除数据异常!index: {}, type: {}, ids: {}", index, type, ids, e);
+ return false;
+ }
+ }
+
+ protected String checkIndex(String day) {
+ if (!enableAutoCreateIndex()) {
+ return getIndex(day);
+ }
+ String index = createIndexIfNotExistsInDay(day);
+ if (StrUtil.isBlank(index)) {
+ String msg = StrUtil.format("【ES】索引 {}_{} 找不到且创建失败!", getAlias(), day);
+ throw new DefaultException(ResultCode.ERROR, msg);
+ }
+ return index;
+ }
+
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/mapper/BaseEsMapper.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/mapper/BaseEsMapper.java
new file mode 100644
index 00000000..b125bea9
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/mapper/BaseEsMapper.java
@@ -0,0 +1,502 @@
+package io.github.dunwu.javadb.elasticsearch.mapper;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ReflectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import io.github.dunwu.javadb.elasticsearch.ElasticsearchTemplate;
+import io.github.dunwu.javadb.elasticsearch.constant.ResultCode;
+import io.github.dunwu.javadb.elasticsearch.entity.BaseEsEntity;
+import io.github.dunwu.javadb.elasticsearch.entity.common.PageData;
+import io.github.dunwu.javadb.elasticsearch.entity.common.ScrollData;
+import io.github.dunwu.javadb.elasticsearch.exception.DefaultException;
+import lombok.extern.slf4j.Slf4j;
+import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.action.bulk.BulkProcessor;
+import org.elasticsearch.action.bulk.BulkResponse;
+import org.elasticsearch.action.get.GetResponse;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * ES Mapper 基础类
+ *
+ * @author Zhang Peng
+ * @date 2023-06-27
+ */
+@Slf4j
+public abstract class BaseEsMapper implements EsMapper {
+
+ protected BulkProcessor bulkProcessor;
+
+ protected final ElasticsearchTemplate elasticsearchTemplate;
+
+ public BaseEsMapper(ElasticsearchTemplate elasticsearchTemplate) {
+ this.elasticsearchTemplate = elasticsearchTemplate;
+ }
+
+ public int getShard() {
+ return 5;
+ }
+
+ public int getReplica() {
+ return 1;
+ }
+
+ @Override
+ public RestHighLevelClient getClient() {
+ if (elasticsearchTemplate == null) {
+ return null;
+ }
+ return elasticsearchTemplate.getClient();
+ }
+
+ @Override
+ public synchronized BulkProcessor getBulkProcessor() {
+ if (bulkProcessor == null) {
+ bulkProcessor = elasticsearchTemplate.newAsyncBulkProcessor();
+ }
+ return bulkProcessor;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map getPropertiesMap() {
+ Class clazz = getEntityClass();
+ Method method;
+ try {
+ method = clazz.getMethod("getPropertiesMap");
+ } catch (NoSuchMethodException e) {
+ log.error("【ES】{} 中不存在 getPropertiesMap 方法!", clazz.getCanonicalName());
+ return new HashMap<>(0);
+ }
+
+ Object result = ReflectUtil.invokeStatic(method);
+ if (result == null) {
+ return new HashMap<>(0);
+ }
+ return (Map) result;
+ }
+
+ // ====================================================================
+ // 索引管理操作
+ // ====================================================================
+
+ @Override
+ public boolean isIndexExists() {
+ String index = getIndex();
+ try {
+ return elasticsearchTemplate.isIndexExists(index);
+ } catch (Exception e) {
+ log.error("【ES】判断索引是否存在异常!index: {}", index, e);
+ return false;
+ }
+ }
+
+ @Override
+ public String createIndexIfNotExists() {
+ String index = getIndex();
+ String type = getType();
+ String alias = getAlias();
+ int shard = getShard();
+ int replica = getReplica();
+ return createIndex(index, type, alias, shard, replica);
+ }
+
+ protected String createIndex(String index, String type, String alias, int shard, int replica) {
+ try {
+ if (elasticsearchTemplate.isIndexExists(index)) {
+ return index;
+ }
+ elasticsearchTemplate.createIndex(index, type, alias, shard, replica);
+ log.info("【ES】创建索引成功!index: {}, type: {}, alias: {}, shard: {}, replica: {}",
+ index, type, alias, shard, replica);
+ Map propertiesMap = getPropertiesMap();
+ if (MapUtil.isNotEmpty(propertiesMap)) {
+ elasticsearchTemplate.setMapping(index, type, propertiesMap);
+ log.error("【ES】设置索引 mapping 成功!index: {}, type: {}, propertiesMap: {}",
+ index, type, JSONUtil.toJsonStr(propertiesMap));
+ }
+ return index;
+ } catch (Exception e) {
+ log.error("【ES】创建索引异常!index: {}, type: {}, alias: {}, shard: {}, replica: {}",
+ index, type, alias, shard, replica, e);
+ return null;
+ }
+ }
+
+ @Override
+ public void deleteIndex() {
+ String index = getIndex();
+ try {
+ log.info("【ES】删除索引成功!index: {}", index);
+ elasticsearchTemplate.deleteIndex(index);
+ } catch (Exception e) {
+ log.error("【ES】删除索引异常!index: {}", index, e);
+ }
+ }
+
+ @Override
+ public void updateAlias() {
+ String index = getIndex();
+ String alias = getAlias();
+ try {
+ log.info("【ES】更新别名成功!alias: {} -> index: {}", alias, index);
+ elasticsearchTemplate.updateAlias(index, alias);
+ } catch (Exception e) {
+ log.error("【ES】更新别名异常!alias: {} -> index: {}", alias, index, e);
+ }
+ }
+
+ @Override
+ public Set getIndexSet() {
+ String alias = getAlias();
+ try {
+ return elasticsearchTemplate.getIndexSet(alias);
+ } catch (Exception e) {
+ log.error("【ES】获取别名的所有索引异常!alias: {}", alias, e);
+ return new HashSet<>(0);
+ }
+ }
+
+ // ====================================================================
+ // CRUD 操作
+ // ====================================================================
+
+ @Override
+ public GetResponse getById(String id) {
+ return getById(id, null);
+ }
+
+ @Override
+ public GetResponse getById(String id, Long version) {
+ String index = getIndex();
+ String type = getType();
+ try {
+ return elasticsearchTemplate.getById(index, type, id, version);
+ } catch (Exception e) {
+ log.error("【ES】根据ID查询异常!index: {}, type: {}, id: {}, version: {}", index, type, id, version, e);
+ return null;
+ }
+ }
+
+ @Override
+ public T pojoById(String id) {
+ return pojoById(id, null);
+ }
+
+ @Override
+ public T pojoById(String id, Long version) {
+ String index = getIndex();
+ String type = getType();
+ try {
+ return elasticsearchTemplate.pojoById(index, type, id, version, getEntityClass());
+ } catch (Exception e) {
+ log.error("【ES】根据ID查询POJO异常!index: {}, type: {}, id: {}, version: {}", index, type, id, version, e);
+ return null;
+ }
+ }
+
+ @Override
+ public List pojoListByIds(Collection ids) {
+ String index = getIndex();
+ String type = getType();
+ try {
+ return elasticsearchTemplate.pojoListByIds(index, type, ids, getEntityClass());
+ } catch (Exception e) {
+ log.error("【ES】根据ID查询POJO列表异常!index: {}, type: {}, ids: {}", index, type, ids, e);
+ return new ArrayList<>(0);
+ }
+ }
+
+ @Override
+ public long count(SearchSourceBuilder builder) {
+ String index = getIndex();
+ String type = getType();
+ try {
+ return elasticsearchTemplate.count(index, type, builder);
+ } catch (Exception e) {
+ log.error("【ES】获取匹配记录数异常!index: {}, type: {}", index, type, e);
+ return 0L;
+ }
+ }
+
+ @Override
+ public SearchResponse query(SearchSourceBuilder builder) {
+ String index = getIndex();
+ String type = getType();
+ try {
+ return elasticsearchTemplate.query(index, type, builder);
+ } catch (Exception e) {
+ log.error("【ES】条件查询异常!index: {}, type: {}", index, type, e);
+ return null;
+ }
+ }
+
+ @Override
+ public PageData pojoPage(SearchSourceBuilder builder) {
+ String index = getIndex();
+ String type = getType();
+ try {
+ return elasticsearchTemplate.pojoPage(index, type, builder, getEntityClass());
+ } catch (Exception e) {
+ log.error("【ES】from + size 分页条件查询异常!index: {}, type: {}", index, type, e);
+ return null;
+ }
+ }
+
+ @Override
+ public ScrollData pojoPageByLastId(String scrollId, int size, QueryBuilder queryBuilder) {
+ String index = getIndex();
+ String type = getType();
+ try {
+ return elasticsearchTemplate.pojoPageByScrollId(index, type, scrollId, size, queryBuilder,
+ getEntityClass());
+ } catch (Exception e) {
+ log.error("【ES】search after 分页条件查询异常!index: {}, type: {}", index, type, e);
+ return null;
+ }
+ }
+
+ @Override
+ public ScrollData pojoScrollBegin(SearchSourceBuilder builder) {
+ String index = getIndex();
+ String type = getType();
+ try {
+ return elasticsearchTemplate.pojoScrollBegin(index, type, builder, getEntityClass());
+ } catch (Exception e) {
+ log.error("【ES】开启滚动分页条件查询异常!index: {}, type: {}", index, type, e);
+ return null;
+ }
+ }
+
+ @Override
+ public ScrollData pojoScroll(String scrollId, SearchSourceBuilder builder) {
+ try {
+ return elasticsearchTemplate.pojoScroll(scrollId, builder, getEntityClass());
+ } catch (Exception e) {
+ log.error("【ES】滚动分页条件查询异常!scrollId: {}", scrollId, e);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean pojoScrollEnd(String scrollId) {
+ try {
+ return elasticsearchTemplate.pojoScrollEnd(scrollId);
+ } catch (Exception e) {
+ log.error("【ES】关闭滚动分页条件查询异常!scrollId: {}", scrollId, e);
+ return false;
+ }
+ }
+
+ @Override
+ public T save(T entity) {
+ if (entity == null) {
+ return null;
+ }
+ String index = getIndex();
+ String type = getType();
+ try {
+ checkIndex();
+ checkData(entity);
+ return elasticsearchTemplate.save(index, type, entity);
+ } catch (Exception e) {
+ log.error("【ES】添加数据异常!index: {}, type: {}, entity: {}", index, type, JSONUtil.toJsonStr(entity), e);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean saveBatch(Collection list) {
+ if (CollectionUtil.isEmpty(list)) {
+ return false;
+ }
+ String index = getIndex();
+ String type = getType();
+ try {
+ checkIndex();
+ checkData(list);
+ return elasticsearchTemplate.saveBatch(index, type, list);
+ } catch (Exception e) {
+ log.error("【ES】批量添加数据异常!index: {}, type: {}, size: {}", index, type, list.size(), e);
+ return false;
+ }
+ }
+
+ @Override
+ public void asyncSaveBatch(Collection list) {
+ asyncSaveBatch(list, DEFAULT_BULK_LISTENER);
+ }
+
+ @Override
+ public void asyncSaveBatch(Collection list, ActionListener listener) {
+ if (CollectionUtil.isEmpty(list)) {
+ return;
+ }
+ String index = getIndex();
+ String type = getType();
+ try {
+ checkIndex();
+ checkData(list);
+ elasticsearchTemplate.asyncSaveBatch(index, getType(), list, listener);
+ } catch (Exception e) {
+ log.error("【ES】异步批量添加数据异常!index: {}, type: {}, size: {}", index, type, list.size(), e);
+ }
+ }
+
+ @Override
+ public T updateById(T entity) {
+ if (entity == null) {
+ return null;
+ }
+ String index = getIndex();
+ String type = getType();
+ try {
+ checkData(entity);
+ return elasticsearchTemplate.updateById(index, type, entity);
+ } catch (Exception e) {
+ log.error("【ES】更新数据异常!index: {}, type: {}", index, type, e);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean updateBatchIds(Collection list) {
+ if (CollectionUtil.isEmpty(list)) {
+ return false;
+ }
+ String index = getIndex();
+ String type = getType();
+ try {
+ checkData(list);
+ return elasticsearchTemplate.updateBatchIds(index, type, list);
+ } catch (Exception e) {
+ log.error("【ES】批量更新数据异常!index: {}, type: {}, size: {}", index, type, list.size(), e);
+ return false;
+ }
+ }
+
+ @Override
+ public void asyncUpdateBatchIds(Collection list) {
+ asyncUpdateBatchIds(list, DEFAULT_BULK_LISTENER);
+ }
+
+ @Override
+ public void asyncUpdateBatchIds(Collection list, ActionListener listener) {
+ if (CollectionUtil.isEmpty(list)) {
+ return;
+ }
+ String index = getIndex();
+ String type = getType();
+ try {
+ checkData(list);
+ elasticsearchTemplate.asyncUpdateBatchIds(index, type, list, listener);
+ } catch (Exception e) {
+ log.error("【ES】异步批量更新数据异常!index: {}, type: {}, size: {}", index, type, list.size(), e);
+ }
+ }
+
+ @Override
+ public boolean deleteById(String id) {
+ if (StrUtil.isBlank(id)) {
+ return false;
+ }
+ String index = getIndex();
+ String type = getType();
+ try {
+ return elasticsearchTemplate.deleteById(index, type, id);
+ } catch (Exception e) {
+ log.error("【ES】根据ID删除数据异常!index: {}, type: {}, id: {}", index, type, id, e);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean deleteBatchIds(Collection ids) {
+ if (CollectionUtil.isEmpty(ids)) {
+ return false;
+ }
+ String index = getIndex();
+ String type = getType();
+ try {
+ return elasticsearchTemplate.deleteBatchIds(index, type, ids);
+ } catch (Exception e) {
+ log.error("【ES】根据ID批量删除数据异常!index: {}, type: {}, ids: {}", index, type, ids, e);
+ return false;
+ }
+ }
+
+ @Override
+ public void asyncDeleteBatchIds(Collection ids) {
+ asyncDeleteBatchIds(ids, DEFAULT_BULK_LISTENER);
+ }
+
+ @Override
+ public void asyncDeleteBatchIds(Collection ids, ActionListener listener) {
+ if (CollectionUtil.isEmpty(ids)) {
+ return;
+ }
+ String index = getIndex();
+ String type = getType();
+ try {
+ elasticsearchTemplate.asyncDeleteBatchIds(index, type, ids, listener);
+ } catch (Exception e) {
+ log.error("【ES】异步根据ID批量删除数据异常!index: {}, type: {}, ids: {}", index, type, ids, e);
+ }
+ }
+
+ protected String checkIndex() {
+ if (!enableAutoCreateIndex()) {
+ return getIndex();
+ }
+ String index = createIndexIfNotExists();
+ if (StrUtil.isBlank(index)) {
+ String msg = StrUtil.format("【ES】索引 {} 找不到且创建失败!", index);
+ throw new DefaultException(ResultCode.ERROR, msg);
+ }
+ return index;
+ }
+
+ protected void checkData(Collection list) {
+ if (CollectionUtil.isEmpty(list)) {
+ String msg = StrUtil.format("【ES】写入 {} 失败!list 不能为空!", getIndex());
+ throw new DefaultException(ResultCode.PARAM_ERROR, msg);
+ }
+ }
+
+ protected void checkData(T entity) {
+ if (entity == null) {
+ String msg = StrUtil.format("【ES】写入 {} 失败!entity 不能为空!", getIndex());
+ throw new DefaultException(ResultCode.PARAM_ERROR, msg);
+ }
+ }
+
+ protected final ActionListener DEFAULT_BULK_LISTENER = new ActionListener() {
+ @Override
+ public void onResponse(BulkResponse response) {
+ if (response != null && !response.hasFailures()) {
+ log.info("【ES】异步批量写数据成功!index: {}, type: {}", getIndex(), getType());
+ } else {
+ log.warn("【ES】异步批量写数据失败!index: {}, type: {}", getIndex(), getType());
+ }
+ }
+
+ @Override
+ public void onFailure(Exception e) {
+ log.error("【ES】异步批量写数据异常!index: {}, type: {}", getIndex(), getType());
+ }
+ };
+
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/mapper/EsMapper.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/mapper/EsMapper.java
new file mode 100644
index 00000000..5630d664
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/mapper/EsMapper.java
@@ -0,0 +1,142 @@
+package io.github.dunwu.javadb.elasticsearch.mapper;
+
+import cn.hutool.core.collection.CollectionUtil;
+import io.github.dunwu.javadb.elasticsearch.entity.BaseEsEntity;
+import io.github.dunwu.javadb.elasticsearch.entity.common.PageData;
+import io.github.dunwu.javadb.elasticsearch.entity.common.ScrollData;
+import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.action.bulk.BulkProcessor;
+import org.elasticsearch.action.bulk.BulkResponse;
+import org.elasticsearch.action.get.GetResponse;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * ES Mapper
+ *
+ * @author Zhang Peng
+ * @date 2023-06-27
+ */
+public interface EsMapper {
+
+ /**
+ * 获取别名
+ */
+ String getAlias();
+
+ /**
+ * 获取索引名
+ */
+ String getIndex();
+
+ /**
+ * 获取索引类型
+ */
+ String getType();
+
+ /**
+ * 获取分片数
+ */
+ int getShard();
+
+ /**
+ * 获取副本数
+ */
+ int getReplica();
+
+ /**
+ * 获取实体类型
+ */
+ Class getEntityClass();
+
+ /**
+ * 如果开启,添加 ES 数据时,如果索引不存在,会自动创建索引
+ */
+ default boolean enableAutoCreateIndex() {
+ return false;
+ }
+
+ RestHighLevelClient getClient();
+
+ BulkProcessor getBulkProcessor();
+
+ boolean isIndexExists();
+
+ String createIndexIfNotExists();
+
+ void deleteIndex();
+
+ void updateAlias();
+
+ Set getIndexSet();
+
+ GetResponse getById(String id);
+
+ GetResponse getById(String id, Long version);
+
+ T pojoById(String id);
+
+ T pojoById(String id, Long version);
+
+ List pojoListByIds(Collection ids);
+
+ default Map pojoMapByIds(Collection ids) {
+ List list = pojoListByIds(ids);
+ if (CollectionUtil.isEmpty(list)) {
+ return new HashMap<>(0);
+ }
+
+ Map map = new HashMap<>(list.size());
+ for (T entity : list) {
+ map.put(entity.getDocId(), entity);
+ }
+ return map;
+ }
+
+ long count(SearchSourceBuilder builder);
+
+ SearchResponse query(SearchSourceBuilder builder);
+
+ PageData pojoPage(SearchSourceBuilder builder);
+
+ ScrollData pojoPageByLastId(String scrollId, int size, QueryBuilder queryBuilder);
+
+ ScrollData pojoScrollBegin(SearchSourceBuilder builder);
+
+ ScrollData pojoScroll(String scrollId, SearchSourceBuilder builder);
+
+ boolean pojoScrollEnd(String scrollId);
+
+ T save(T entity);
+
+ boolean saveBatch(Collection list);
+
+ void asyncSaveBatch(Collection list);
+
+ void asyncSaveBatch(Collection list, ActionListener listener);
+
+ T updateById(T entity);
+
+ boolean updateBatchIds(Collection list);
+
+ void asyncUpdateBatchIds(Collection list);
+
+ void asyncUpdateBatchIds(Collection list, ActionListener listener);
+
+ boolean deleteById(String id);
+
+ boolean deleteBatchIds(Collection ids);
+
+ void asyncDeleteBatchIds(Collection ids);
+
+ void asyncDeleteBatchIds(Collection ids, ActionListener listener);
+
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/mapper/UserEsMapper.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/mapper/UserEsMapper.java
new file mode 100644
index 00000000..de6d1b7c
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/mapper/UserEsMapper.java
@@ -0,0 +1,47 @@
+package io.github.dunwu.javadb.elasticsearch.mapper;
+
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import io.github.dunwu.javadb.elasticsearch.ElasticsearchTemplate;
+import io.github.dunwu.javadb.elasticsearch.entity.User;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+/**
+ * open_applet_consume_yyyyMMdd ES Mapper
+ *
+ * @author Zhang Peng
+ * @date 2023-06-27
+ */
+@Slf4j
+@Component
+public class UserEsMapper extends BaseDynamicEsMapper {
+
+ public UserEsMapper(ElasticsearchTemplate elasticsearchTemplate) {
+ super(elasticsearchTemplate);
+ }
+
+ @Override
+ public String getAlias() {
+ return "user";
+ }
+
+ @Override
+ public String getIndex() {
+ String date = DateUtil.format(new Date(), DatePattern.PURE_DATE_FORMAT);
+ return getAlias() + "_" + date;
+ }
+
+ @Override
+ public String getType() {
+ return "_doc";
+ }
+
+ @Override
+ public Class getEntityClass() {
+ return User.class;
+ }
+
+}
diff --git a/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/util/JsonUtil.java b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/util/JsonUtil.java
new file mode 100644
index 00000000..dabe0df5
--- /dev/null
+++ b/codes/javadb/elasticsearch/elasticsearch6/src/main/java/io/github/dunwu/javadb/elasticsearch/util/JsonUtil.java
@@ -0,0 +1,99 @@
+package io.github.dunwu.javadb.elasticsearch.util;
+
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * JSON 工具类
+ *
+ * @author Zhang Peng
+ * @date 2023-06-29
+ */
+@Slf4j
+public class JsonUtil {
+
+ private static final ObjectMapper MAPPER =
+ JsonMapper.builder()
+ .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
+ .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
+ .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
+ .serializationInclusion(JsonInclude.Include.ALWAYS)
+ .build();
+
+ public static List toList(String json, Class clazz) {
+ if (StrUtil.isBlank(json)) {
+ return null;
+ }
+ JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, clazz);
+ try {
+ return MAPPER.readValue(json, javaType);
+ } catch (Exception e) {
+ log.error("反序列化失败!json: {}, msg: {}", json, e.getMessage());
+ }
+ return null;
+ }
+
+ public static Map toMap(String json) {
+ if (StrUtil.isBlank(json)) {
+ return new HashMap<>(0);
+ }
+ try {
+ return MAPPER.readValue(json, new TypeReference