众所周知,ElasticSearch版本表更周期比较短,每次版本的迭代,都会出现一些功能上的大变动,项目中一旦使用了ES的版本,不敢轻易进行版本升级,因为变动点太多,很可能涉及到操作API的变更。下面来说下ES7.X上的变动点:
为什么移除类型: 起初,我们说"索引"和关系数据库的“库”是相似的,“类型”和“表”是对等的。
这是一个不正确的对比,导致了不正确的假设。在关系型数据库里,"表"是相互独立的,一个“表”里的列和另外一个“表”的同名列没有关系,互不影响。但在类型里字段不是这样的。
在一个Elasticsearch索引里,所有不同类型的同名字段内部使用的是同一个lucene字段存储。这可能导致一些问题,例如你希望同一个索引中"deleted"字段在一个类型里是存储日期值,在另外一个类型里存储布尔值。
最后,在同一个索引中,存储仅有小部分字段相同或者全部字段都不相同的文档,会导致数据稀疏,影响Lucene有效压缩数据的能力。
因为这些原因,我们决定从Elasticsearch中移除类型的概念。
简单来说,一般我们在计算文本相关性的时候,会通过倒排索引的方式进行查询,通过倒排索引已经要比全量遍历节约大量时间,但是有时候仍然很慢。
原因是很多时候我们其实只是想要top n个结果,一些结果明显较差的也进行了复杂的相关性计算,
而weak-and算法通过计算每个词的贡献上限来估计文档的相关性上限,从而建立一个阈值对倒排中的结果进行减枝,从而得到提速的效果。
之前写过 TransportClient 操作ES的文章,里面也包括ES的安装教程,有兴趣的可以查看一下。
链接地址:https://blog.csdn.net/li521wang/article/details/83792552
这里主要讲一下SpringBoot项目集成RestHighLevelClient操作ES7.X。内容包含根据实体动态创建ES索引(包含ES基础类型与内嵌文档类型)、CRUD、同步批量操作、异步批量操作、删除索引、封装查询等常用方法的集成。适合做为项目中的工具类。
在项目的pom依赖中增加ElasticSearch7.4.0的依赖
<dependency>
<groupId>org.elasticsearchgroupId>
<artifactId>elasticsearchartifactId>
<version>7.4.0version>
dependency>
<dependency>
<groupId>org.elasticsearch.clientgroupId>
<artifactId>elasticsearch-rest-high-level-clientartifactId>
<version>7.4.0version>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-lang3artifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
在application.properties增加ElasticSearch配置文件,主要配置ES的集群名称、Host、用户名密码等信息
【注】 如果想增加ES的认证,需要安装x-pack插件,这里不再描述。
data.elasticsearch.cluster-name=my-application
data.elasticsearch.host=127.0.0.1:9200
data.elasticsearch.username=
data.elasticsearch.password=
data.elasticsearch.repositories.enable=true
data.elasticsearch.maxresout=1000000
创建ES的配置类ESConfig,获取application.properties中的配置信息创建自定义RestClientBuilder、RestHighLevelClient的Spring Bean对象
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.Objects;
/**
* ES配置类 - RestClient
*
* @author [email protected]
* @date 2019/11/16 19:27
**/
@Slf4j
@Configuration
public class ESConfig {
/**
* 地址位数(ip:端口按:分割长度为2)
*/
private static final int ADDRESS_LENGTH = 2;
/**
* 协议名称
*/
private static final String HTTP_SCHEME = "http";
/**
* 权限验证
*/
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
/**
* ES地址信息(ip:port)
*/
@Value("${data.elasticsearch.host}")
private String[] address;
@Value("${data.elasticsearch.maxresout}")
private String maxresout;
/**
* 用户名
*/
@Value("${data.elasticsearch.username}")
private String userName;
/**
* 密码
*/
@Value("${data.elasticsearch.password}")
private String password;
/**
* 配置RestClient构造器
*
* @date 2019/12/10 15:49
* @author [email protected]
**/
@Bean
public RestClientBuilder restClientBuilder() {
HttpHost[] hosts = Arrays.stream(address)
.map(this::makeHttpHost)
.filter(Objects::nonNull)
.toArray(HttpHost[]::new);
log.info("hosts:{}", Arrays.toString(hosts));
//配置权限验证
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
return RestClient.builder(hosts).setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider));
}
/**
* 处理请求地址
*
* @param esAddress: ES地址信息
* @date 2019/12/10 15:46
* @author [email protected]
**/
private HttpHost makeHttpHost(String esAddress) {
assert StringUtils.isNotEmpty(esAddress);
String[] address = esAddress.split(":");
if (address.length == ADDRESS_LENGTH) {
String ip = address[0];
int port = Integer.parseInt(address[1]);
return new HttpHost(ip, port, HTTP_SCHEME);
} else {
return null;
}
}
/**
* 配置RestHighLevelClient Bean
*
* @date 2019/12/10 15:45
* @author [email protected]
**/
@Bean(name = "highLevelClient")
public RestHighLevelClient highLevelClient(@Autowired RestClientBuilder restClientBuilder) {
return new RestHighLevelClient(restClientBuilder);
}
}
创建常量类ElasticSearchConstant ,根据阿里巴巴开发手册中的规定,代码中不允许出现魔法值。
/**
* ES常量类
*
* @author [email protected]
* @version V1.0
* @date 2019/12/11 10:39
**/
public interface ElasticSearchConstant {
/**
* 查询多字段分割标识
*/
String SPLIT_FLAG = ",";
/**
* 排序规则-升序
*/
String SORT_ASC = "asc";
/**
* 排序规则-降序
*/
String DESC_ASC = "desc";
}
创建自定义注解FieldInfo,配置实体类型、分词规则 。
import java.lang.annotation.*;
/**
* 自定义ES注解,配置实体类型、分词规则
*
* @author [email protected]
* @date 2019/12/10 17:07
**/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FieldInfo {
/**
* @string text ,keyword
* @Number long, integer, short, byte, double, float, half_float, scaled_float
* @date date
* @date_nanos date_nanos
* @Range integer_range, float_range, long_range, double_range, date_range
* @binary binary
* @Nested nested
*/
String type() default "string";
/**
* 分词器选择 0. not_analyzed 1. ik_smart 2. ik_max_word
*/
int participle() default 0;
}
创建解析实体后存放实体实体类型、分词规则的对象 FieldMapping.java
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 解析实体后存放实体实体类型、分词规则的对象
*
* @author [email protected]
* @date 2019/12/10 17:07
**/
@Data
@NoArgsConstructor
public class FieldMapping {
/**
* 名称
*/
private String field;
/**
* 类型
*/
private String type;
/**
* 分词器选择 0. not_analyzed 1. ik_smart 2. ik_max_word
*/
private int participle;
/**
* 嵌套集合
*/
private List<FieldMapping> fieldMappingList;
public FieldMapping(String field, String type, int participle) {
this.field = field;
this.type = type;
this.participle = participle;
}
}
解析实体上注解信息的工具类 FieldMappingUtils.java
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
/**
* Field工具类
*
* @author lixiang
* @version V1.0
* @date 2019/12/10 17:14
**/
@Slf4j
public class FieldMappingUtils {
/**
* 获取实体注解上的FieldMapping
*
* @param clazz: 对象的class
* @return 实体的FieldMapping集合
* @date 2019/12/10 17:15
* @author [email protected]
**/
public static List<FieldMapping> getFieldInfo(Class clazz) {
return getFieldInfo(clazz, null);
}
/**
* 获取实体注解上的FieldMapping
*
* @param clazz: 对象的class
* @param fieldName: 类名称
* @return 实体的FieldMapping集合
* @date 2019/12/10 17:15
* @author [email protected]
**/
public static List<FieldMapping> getFieldInfo(Class clazz, String fieldName) {
// 返回Class中所有的字段(包括私有字段)
Field[] fields = clazz.getDeclaredFields();
// 创建FieldMapping集合
List<FieldMapping> fieldMappingList = new ArrayList<>();
for (Field field : fields) {
// 获取字段上的FieldInfo对象
FieldInfo fieldInfo = field.getAnnotation(FieldInfo.class);
if (fieldInfo == null) {
continue;
}
if ("object".equals(fieldInfo.type()) || "nested".equals(fieldInfo.type())) {
// 字段FieldInfo注解的type是object
// 获取字段的类型
Class fc = field.getType();
// 判断指定的Class对象是否为基本类型
// (有九种预定义的Class对象代表的八个基本类型和void。这些都是由Java虚拟机创建的,并且具有相同的名称,
// 它们代表即boolean, byte, char, short, int, long, float, 和double 等原始类型)。
if (fc.isPrimitive()) {
// 获取Class的类名称(基本类型)
String name = field.getName();
if (StringUtils.isNotBlank(fieldName)) {
name = name + "." + fieldName;
}
fieldMappingList.add(new FieldMapping(name, fieldInfo.type(), fieldInfo.participle()));
} else {
// 判断是否为List
if (fc.isAssignableFrom(List.class)) {
log.debug("List类型:{}", field.getName());
// 得到泛型类型
Type gt = field.getGenericType();
ParameterizedType pt = (ParameterizedType) gt;
Class listClass = (Class) pt.getActualTypeArguments()[0];
// 循环获取集合里面的实体FieldMapping
List<FieldMapping> fieldMappings = getFieldInfo(listClass, field.getName());
FieldMapping fieldMapping = new FieldMapping(field.getName(), fieldInfo.type(), fieldInfo.participle());
fieldMapping.setFieldMappingList(fieldMappings);
fieldMappingList.add(fieldMapping);
} else {
// 循环获取集合里面的实体FieldMapping
List<FieldMapping> fieldMappings = getFieldInfo(fc, field.getName());
FieldMapping fieldMapping = new FieldMapping(field.getName(), fieldInfo.type(), fieldInfo.participle());
fieldMapping.setFieldMappingList(fieldMappings);
fieldMappingList.add(fieldMapping);
}
}
} else {
// 字段FieldInfo注解的type不是object
String name = field.getName();
fieldMappingList.add(new FieldMapping(name, fieldInfo.type(), fieldInfo.participle()));
}
}
return fieldMappingList;
}
}
/**
* 分页模型
*/
@Data
public class PageModelDTO implements Serializable {
private static final long serialVersionUID = 192274358354304398L;
/**
* 数据总条数
*/
private Long total;
/**
* 页码
*/
private Integer pageNum = 1;
/**
* 条数
*/
private Integer pageSize = 10;
/**
* 界定返回值类型 json;obj;
*/
private String resultType = "string";
/**
* 是否分页
*/
private Boolean isPage = true;
/**
* json格式返回值
*/
private List<String> jsonRsList = new ArrayList<String>();
/**
* 直接从ES获取的结果
*/
private SearchHit[] searchHits;
/**
* 区间查询参数
*/
private Map<String, RangConditionDTO> rangConditionMap = new HashMap<>();
/**
* 时间区间查询参数
*/
private Map<String, RangConditionsToTimeModelDTO> rangConditionsToTimeModelMap = new HashMap<>();
/**
* 模糊相等查询条件,多个查询条件","进行切割
*/
private Map<String, Object> likeSearchCondition = new HashMap<>();
/**
* or条件查询
*/
private Map<String, Object> orSearchCondition = new HashMap<>();
/**
* or条件查询
*/
private Map<String, Object> orLikeSearchCondition = new HashMap<>();
/**
* or条件查询集合类操作
*/
private List<Map<String, Object>> orSearchConditionList = new ArrayList<>();
/**
* 相等查询条件,多个查询条件","进行切割
*/
private Map<String, Object> equalsSearchCondition = new HashMap<>();
/**
* in 查询
*/
private Map<String, List> inSearchCondition = new HashMap<>();
/**
* 模糊不相等的条件,多个查询条件","进行切割
*/
private Map<String, Object> noLikeSearchConditioin = new HashMap<>();
/**
* 不相等的条件,多个查询条件","进行切割
*/
private Map<String, Object> noEqualsSearchConditioin = new HashMap<>();
/**
* 为空过滤
*/
private List<String> isNullConditioin = new ArrayList<>();
/**
* 不为空过滤
*/
private List<String> isNotNullConditioin = new ArrayList<>();
/**
* 排序字段,关键字asc,desc
*/
private Map<String, String> sortFileds = new LinkedHashMap<>();
/**
* 排序字段集合,方便对排序顺序的控制 关键字asc,desc
*/
private List<Map<String, String>> sortFiledsList = new ArrayList<>();
/**
* 高亮字段
*/
private List<String> hightFieldList = new ArrayList<>();
/**
* 去重字段
*/
private String collapseField;
/**
* 指定查询结果包含的字段
*/
private String[] fetchSourceIncludes;
/**
* 指定查询结果不包含的字段
*/
private String[] fetchSourceExcludes;
/**
* 分词字段
*/
private Map<String, Object> analyzersField = new HashMap<>();
public String getSortFileds(String key) {
return sortFileds.get(key);
}
public RangConditionDTO getRangConditionMap(String key) {
return rangConditionMap.get(key);
}
public RangConditionsToTimeModelDTO getRangConditionsToTimeModelMap(String key) {
return rangConditionsToTimeModelMap.get(key);
}
public void setJsonRsList(String json) {
this.jsonRsList.add(json);
}
}
/**
* 区间查询
*/
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class RangConditionDTO implements Serializable {
private static final long serialVersionUID = 4872669865514539066L;
/**
* 开始区间
*/
private String beginValue;
/**
* 结束区间
*/
private String endValue;
}
/**
* 时间区间查询
*/
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class RangConditionsToTimeModelDTO implements Serializable {
private static final long serialVersionUID = -1649669156877508723L;
/**
* 开始时间
*/
private Timestamp beginTime;
/**
* 结束时间
*/
private Timestamp endTime;
}
内涵方法有单条保存数据、批量保存数据(同步)、批量保存数据(异步)、修改数据、批量修改数据(同步)、根据主键删除数据、批量删除ES索引、删除索引、根据主键查询索引名称、搜索 支持多种搜索方式(分页、区间、模糊、OR、IN、过滤)
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.leimingtech.modules.constant.ElasticSearchConstant;
import com.leimingtech.modules.dto.PageModelDTO;
import com.leimingtech.modules.dto.RangConditionDTO;
import com.leimingtech.modules.dto.RangConditionsToTimeModelDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* ES操作工具类(RestClient版)
* 由于ES7.X移除了索引Type,此工具类只兼容ES7.X版本
*
* @author [email protected]
* @date 2019/12/11 9:50
**/
@Slf4j
@Component
public class EsDataUtils {
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* ES 保存数据
*
* @param indexName: 索引名称
* @param oid: 主键
* @param paramJson: 参数Json
* @param clazz: 保存实体的Class
* @return 保存结果
*/
public boolean saveData(String indexName, String oid, String paramJson, Class clazz) {
// 判断索引是否存在
boolean result = isIndexExists(indexName);
if (!result) {
boolean createResult = createIndexAndCreateMapping(indexName, FieldMappingUtils.getFieldInfo(clazz));
if (!createResult) {
log.info("索引[{}],主键[{}]创建失败", indexName, oid);
return false;
}
}
IndexRequest indexRequest = new IndexRequest(indexName);
indexRequest.id(oid);
indexRequest.source(paramJson, XContentType.JSON);
IndexResponse response = null;
try {
response = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
log.error("索引[{}],主键[{}]保存异常:{}", indexName, oid, e);
return false;
}
// 判断索引是新增还是修改 并对数量进行增加
if (IndexResponse.Result.CREATED.equals(response.getResult())) {
log.info("索引[{}],主键[{}]保存成功", indexName, oid);
return true;
} else if (IndexResponse.Result.UPDATED.equals(response.getResult())) {
log.info("索引[{}],主键[{}]修改成功", indexName, oid);
return true;
}
return false;
}
/**
* ES 批量保存数据(同步)
*
* @param indexName: 索引名称
* @param primaryKeyName: 主键名称
* @param paramListJson: 数据集合JSON
* @return 批量保存结果
* @date 2019/12/10 18:34
* @author [email protected]
**/
public boolean saveDataBatch(String indexName, String primaryKeyName, String paramListJson, Class clazz) {
// 判断索引是否存在
boolean result = isIndexExists(indexName);
if (!result) {
boolean createResult = createIndexAndCreateMapping(indexName, FieldMappingUtils.getFieldInfo(clazz));
if (!createResult) {
log.info("索引[{}]创建失败", indexName);
return false;
}
}
BulkRequest bulkRequest = packBulkIndexRequest(indexName, primaryKeyName, paramListJson);
try {
// 同步执行
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
if (bulk.hasFailures()) {
for (BulkItemResponse item : bulk.getItems()) {
log.error("索引[{}],主键[{}]更新操作失败,状态为:[{}],错误信息:{}", indexName, item.getId(),
item.status(), item.getFailureMessage());
}
return false;
}
// 记录索引新增与修改数量
Integer createdCount = 0;
Integer updatedCount = 0;
for (BulkItemResponse item : bulk.getItems()) {
if (IndexResponse.Result.CREATED.equals(item.getResponse().getResult())) {
createdCount++;
} else if (IndexResponse.Result.UPDATED.equals(item.getResponse().getResult())) {
updatedCount++;
}
}
log.info("索引[{}]批量同步更新成功,共新增[{}]个,修改[{}]个", indexName, createdCount, updatedCount);
} catch (IOException e) {
log.error("索引[{}]批量同步更新出现异常", indexName);
return false;
}
return true;
}
/**
* ES 批量保存数据(异步)
*
* @param indexName: 索引名称
* @param primaryKeyName: 主键名称
* @param paramListJson: 数据集合JSON
* @return 批量保存结果
* @date 2019/12/10 18:34
* @author [email protected]
**/
public boolean saveDataBatchAsync(String indexName, String primaryKeyName, String paramListJson) {
BulkRequest bulkRequest = packBulkIndexRequest(indexName, primaryKeyName, paramListJson);
try {
//异步执行
ActionListener<BulkResponse> listener = new ActionListener<BulkResponse>() {
@Override
public void onResponse(BulkResponse bulkResponse) {
if (bulkResponse.hasFailures()) {
for (BulkItemResponse item : bulkResponse.getItems()) {
log.error("索引[{}],主键[{}]更新操作失败,状态为:[{}],错误信息:{}", indexName, item.getId(),
item.status(), item.getFailureMessage());
}
}
}
// 失败操作
@Override
public void onFailure(Exception e) {
log.error("索引[{}]批量异步更新出现异常:{}", indexName, e);
}
};
restHighLevelClient.bulkAsync(bulkRequest, RequestOptions.DEFAULT, listener);
log.info("异步批量更新索引[{}]中", indexName);
} catch (Exception e) {
log.info("异步批量更新索引[{}]出现异常:{}", indexName, e);
return false;
}
return true;
}
/**
* ES 修改数据
*
* @param indexName: 索引名称
* @param oid: 主键
* @param paramJson: 参数JSON
* @return 修改结果
* @date 2019/12/10 19:10
* @author [email protected]
**/
public boolean updateData(String indexName, String oid, String paramJson) {
UpdateRequest updateRequest = new UpdateRequest(indexName, oid);
// 如果修改索引中不存在则进行新增
updateRequest.docAsUpsert(true);
updateRequest.doc(paramJson, XContentType.JSON);
try {
UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
log.info("索引[{}],主键:[{}]操作结果:[{}]", indexName, oid, updateResponse.getResult());
if (UpdateResponse.Result.CREATED.equals(updateResponse.getResult())) {
// 新增
log.info("索引:[{}],主键:[{}]新增成功", indexName, oid);
return true;
} else if (UpdateResponse.Result.UPDATED.equals(updateResponse.getResult())) {
// 修改
log.info("索引:[{}],主键:[{}]修改成功", indexName, oid);
return true;
} else if (UpdateResponse.Result.NOOP.equals(updateResponse.getResult())) {
// 无变化
log.info("索引:[{}] 主键:[{}] 无变化", indexName, oid);
return true;
}
} catch (IOException e) {
log.info("索引:[{}],主键:[{}] 更新异常:{}", indexName, oid, e);
return false;
}
return false;
}
/**
* ES 批量修改数据(同步)
*
* @param indexName: 索引名称
* @param primaryKeyName: 主键名称
* @param paramListJson: 数据集合JSON
* @return 批量修改结果
* @date 2019/12/10 18:34
* @author [email protected]
**/
public boolean updateDataBatch(String indexName, String primaryKeyName, String paramListJson) {
BulkRequest bulkRequest = packBulkUpdateRequest(indexName, primaryKeyName, paramListJson);
try {
// 同步执行
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
if (bulk.hasFailures()) {
for (BulkItemResponse item : bulk.getItems()) {
log.error("索引[{}],主键[{}]修改操作失败,状态为:[{}],错误信息:{}", indexName, item.getId(),
item.status(), item.getFailureMessage());
}
return false;
}
// 记录索引新增与修改数量
Integer createdCount = 0;
Integer updatedCount = 0;
for (BulkItemResponse item : bulk.getItems()) {
if (IndexResponse.Result.CREATED.equals(item.getResponse().getResult())) {
createdCount++;
} else if (IndexResponse.Result.UPDATED.equals(item.getResponse().getResult())) {
updatedCount++;
}
}
log.info("索引[{}]批量修改更新成功,共新增[{}]个,修改[{}]个", indexName, createdCount, updatedCount);
} catch (IOException e) {
log.error("索引[{}]批量修改更新出现异常", indexName);
return false;
}
return true;
}
/**
* ES 根据主键删除数据
*
* @param indexName: 索引名称
* @param oid: 主键ID
* @return 操作结果
* @date 2019/12/11 9:53
* @author [email protected]
**/
public boolean deleteDate(String indexName, String oid) {
DeleteRequest deleteRequest = new DeleteRequest(indexName);
deleteRequest.id(oid);
try {
DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
if (deleteResponse.getResult() == DocWriteResponse.Result.NOT_FOUND) {
log.error("索引[{}]主键[{}]删除失败", indexName, oid);
return false;
} else {
log.info("索引[{}]主键[{}]删除成功", indexName, oid);
return true;
}
} catch (IOException e) {
log.error("删除索引[{}]出现异常[{}]", indexName, e);
return false;
}
}
/**
* ES 批量删除ES索引
*
* @param indexName: 索引名称
* @param ids: 主键集合
* @return 操作结果
* @date 2019/12/10 20:46
* @author [email protected]
**/
public boolean bulkDelete(String indexName, List<Long> ids) {
BulkRequest bulkRequest = packBulkDeleteRequest(indexName, ids);
try {
// 同步执行
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
if (bulk.hasFailures()) {
for (BulkItemResponse item : bulk.getItems()) {
log.error("更新索引:{},主键:{}失败。信息为:{}", indexName, item.getId(), item.getFailureMessage());
}
return false;
}
// 记录索引新增与修改数量
Integer deleteCount = 0;
for (BulkItemResponse item : bulk.getItems()) {
if (DeleteResponse.Result.DELETED.equals(item.getResponse().getResult())) {
deleteCount++;
}
}
log.info("批量删除索引[{}]成功,共删除[{}]个", indexName, deleteCount);
} catch (IOException e) {
log.error("删除索引:{}批量保存数据出现异常:{}", indexName, e);
return false;
}
return true;
}
/**
* ES 删除索引
*
* @param indexName: 索引名称
* @return 操作结果
* @date 2019/12/11 16:59
* @author [email protected]
**/
public boolean deleteIndex(String indexName) {
// 判断索引是否存在
boolean result = isIndexExists(indexName);
if (!result) {
log.error("索引[{}]不存在删除索引失败", indexName);
return false;
}
try {
DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indexName);
deleteIndexRequest.indicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN);
AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
if (!acknowledgedResponse.isAcknowledged()) {
log.error("索引[{}]删除失败", indexName);
}
log.info("索引[{}]删除成功", indexName);
return true;
} catch (IOException e) {
log.error("索引[{}]删除异常:{}", indexName, e);
}
return false;
}
/**
* ES 根据主键查询索引名称
*
* @param indexName: 索引名称
* @param oid: 主键
* @return 返回数据JSON
* @date 2019/12/11 9:48
* @author [email protected]
**/
public String getDateById(String indexName, String oid) {
// 判断索引是否存在
if (!isIndexExists(indexName)) {
return "";
}
GetRequest getRequest = new GetRequest(indexName, oid);
try {
GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
String resultJson = getResponse.getSourceAsString();
log.debug("索引[{}]主键[{}]查询结果[{}]", indexName, oid, resultJson);
return resultJson;
} catch (IOException e) {
log.debug("索引[{}]主键[{}]查询异常:{}", indexName, oid, e);
return "";
}
}
/**
* ES 搜索 支持多种搜索方式(分页、区间、模糊、OR、IN、过滤)
*
* @param pageModel: 搜索对象封装的实体
* @param indexName: 索引名称
* @return 查询返回的实体
* @date 2019/12/11 10:00
* @author [email protected]
**/
public PageModelDTO queryData(PageModelDTO pageModel, String indexName) {
// 判断索引是否存在
if (!isIndexExists(indexName)) {
return pageModel;
}
// 获取页码、页面大小
int pageStart = 0;
int pageSize = 10000;
if (pageModel.getIsPage()) {
pageSize = pageModel.getPageSize();
pageStart = (pageModel.getPageNum() - 1) * pageSize;
}
// 封装boolBuilder
QueryBuilder boolBuilder = queryBuilder(pageModel);
// 封装SearchSourceBuilder
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder
// 设置查询关键词
.query(boolBuilder)
// 设置查询数据的位置,分页用
.from(pageStart)
// 设置查询结果集的最大条数
.size(pageSize)
// 不展示分析逻辑
.explain(false)
// 配置高亮
.highlighter(this.getHighlightBuilder(pageModel));
//指定查询包含的字段,指定查询不包含的字段
if (pageModel.getFetchSourceIncludes() != null || pageModel.getFetchSourceExcludes() != null) {
searchSourceBuilder.fetchSource(pageModel.getFetchSourceIncludes(), pageModel.getFetchSourceExcludes());
}
//排序list,以安排排序的优先级顺序
for (Map<String, String> map : pageModel.getSortFiledsList()) {
for (String sortKey : map.keySet()) {
if (ElasticSearchConstant.SORT_ASC.equalsIgnoreCase(pageModel.getSortFileds(sortKey))) {
searchSourceBuilder.sort(SortBuilders.fieldSort(sortKey).order(SortOrder.ASC));
} else if (ElasticSearchConstant.DESC_ASC.equalsIgnoreCase(pageModel.getSortFileds(sortKey))) {
searchSourceBuilder.sort(SortBuilders.fieldSort(sortKey).order(SortOrder.DESC));
}
}
}
// 设置排序字段
for (String sortKey : pageModel.getSortFileds().keySet()) {
if (ElasticSearchConstant.SORT_ASC.equalsIgnoreCase(pageModel.getSortFileds(sortKey))) {
searchSourceBuilder.sort(SortBuilders.fieldSort(sortKey).order(SortOrder.ASC));
} else if (ElasticSearchConstant.DESC_ASC.equalsIgnoreCase(pageModel.getSortFileds(sortKey))) {
searchSourceBuilder.sort(SortBuilders.fieldSort(sortKey).order(SortOrder.DESC));
}
}
if (StringUtils.isNotBlank(pageModel.getCollapseField())) {
searchSourceBuilder.collapse(new CollapseBuilder(pageModel.getCollapseField()));
}
SearchRequest searchRequest = new SearchRequest(indexName);
searchRequest.source(searchSourceBuilder);
// 设置查询类型
// 1.SearchType.DFS_QUERY_THEN_FETCH = 精确查询
// 2.SearchType.SCAN = 扫描查询,无序
// 3.SearchType.COUNT = 不设置的话,这个为默认值,
searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH);
SearchResponse response = null;
try {
response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits searchHits = response.getHits();
pageModel.setTotal(searchHits.getTotalHits().value);
log.debug("共匹配到:" + searchHits.getTotalHits().value + "条记录!");
SearchHit[] hits = searchHits.getHits();
pageModel.setSearchHits(hits);
for (SearchHit searchHit : hits) {
Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
String json = JSON.toJSONString(sourceAsMap);
pageModel.setJsonRsList(json);
}
} catch (IOException e) {
log.error("查询索引[{}]数据出现异常{}", indexName, e);
return pageModel;
}
return pageModel;
}
/**
* 拼接高亮字段
*
* @param pageModel
* @return
*/
private HighlightBuilder getHighlightBuilder(PageModelDTO pageModel) {
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("");
highlightBuilder.postTags("");
List<String> fields = pageModel.getHightFieldList();
for (String field : fields) {
highlightBuilder.field(field);
}
return highlightBuilder;
}
/**
* 拼接查询条件
*
* @param pageModel
* @return
*/
private BoolQueryBuilder queryBuilder(PageModelDTO pageModel) {
BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
// 区间查询
sectionSearch(pageModel, boolBuilder);
// 模糊查询
likeSearch(pageModel, boolBuilder);
// or拼接查询
BoolQueryBuilder orBoolQueryBuilder = QueryBuilders.boolQuery();
Map<String, Object> orSearchCondition = pageModel.getOrSearchCondition();
filterOrQuery(orSearchCondition, orBoolQueryBuilder);
boolBuilder.must(orBoolQueryBuilder);
// and (or)拼接查询
for (Map<String, Object> searchCondition : pageModel.getOrSearchConditionList()) {
BoolQueryBuilder termBoolQueryBuilder = QueryBuilders.boolQuery();
filterOrQuery(searchCondition, termBoolQueryBuilder);
boolBuilder.must(termBoolQueryBuilder);
}
// in查询
for (String key : pageModel.getInSearchCondition().keySet()) {
BoolQueryBuilder ptermsBoolQueryBuilder = QueryBuilders.boolQuery();
TermsQueryBuilder termQueryBuilder = QueryBuilders.termsQuery(key, pageModel.getInSearchCondition().get(key));
ptermsBoolQueryBuilder.should(termQueryBuilder);
TermsQueryBuilder termQueryBuilder2 = QueryBuilders.termsQuery(key + ".keyword", pageModel.getInSearchCondition().get(key));
ptermsBoolQueryBuilder.should(termQueryBuilder2);
boolBuilder.must(ptermsBoolQueryBuilder);
}
// 为空匹配
for (String key : pageModel.getIsNullConditioin()) {
ExistsQueryBuilder existsQueryBuilder = QueryBuilders.existsQuery(key);
boolBuilder.mustNot(existsQueryBuilder);
}
// 非空匹配
for (String key : pageModel.getIsNullConditioin()) {
ExistsQueryBuilder existsQueryBuilder = QueryBuilders.existsQuery(key);
boolBuilder.must(existsQueryBuilder);
}
return boolBuilder;
}
/**
* 区间查询
*
* @param pageModel
* @param boolBuilder
* @return
*/
private BoolQueryBuilder sectionSearch(PageModelDTO pageModel, BoolQueryBuilder boolBuilder) {
// 时间区间查询
for (String key : pageModel.getRangConditionsToTimeModelMap().keySet()) {
RangConditionsToTimeModelDTO rangConditionsToTimeModelMap = pageModel.getRangConditionsToTimeModelMap(key);
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(key);
if (null != rangConditionsToTimeModelMap.getBeginTime()) {
rangeQueryBuilder.gte(rangConditionsToTimeModelMap.getBeginTime().getTime());
}
if (null != rangConditionsToTimeModelMap.getEndTime()) {
rangeQueryBuilder.lte(rangConditionsToTimeModelMap.getEndTime().getTime());
}
// 包括下界
rangeQueryBuilder.includeLower(true);
// 包括上界
rangeQueryBuilder.includeUpper(false);
boolBuilder.must(rangeQueryBuilder);
log.debug("rangeQueryBuilder:" + rangeQueryBuilder);
}
// 其他数据区间查询
for (String key : pageModel.getRangConditionMap().keySet()) {
RangConditionDTO rangConditionMap = pageModel.getRangConditionMap(key);
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(key);
if (StringUtils.isNotBlank(rangConditionMap.getBeginValue())) {
rangeQueryBuilder.gte(rangConditionMap.getBeginValue());
}
if (StringUtils.isNotBlank(rangConditionMap.getEndValue())) {
rangeQueryBuilder.lte(rangConditionMap.getEndValue());
}
// 包括下界
rangeQueryBuilder.includeLower(true);
// 包括上界
rangeQueryBuilder.includeUpper(false);
boolBuilder.must(rangeQueryBuilder);
log.debug("rangeQueryBuilder:" + rangeQueryBuilder);
}
return boolBuilder;
}
/**
* 模糊匹配
*
* @param pageModel
* @param boolBuilder
* @return
*/
private BoolQueryBuilder likeSearch(PageModelDTO pageModel, BoolQueryBuilder boolBuilder) {
// 模糊匹配查询
for (String key : pageModel.getLikeSearchCondition().keySet()) {
Object machValue = pageModel.getLikeSearchCondition().get(key);
if (null == machValue) {
continue;
}
BoolQueryBuilder wildcardBoolQueryBuilder = QueryBuilders.boolQuery();
for (String value : machValue.toString().split(ElasticSearchConstant.SPLIT_FLAG)) {
WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery(key, "*" + value + "*");
wildcardBoolQueryBuilder.should(wildcardQueryBuilder);
}
boolBuilder.must(wildcardBoolQueryBuilder);
}
// 精确匹配查询
for (String key : pageModel.getEqualsSearchCondition().keySet()) {
Object machValue = pageModel.getEqualsSearchCondition().get(key);
if (null == machValue) {
continue;
}
BoolQueryBuilder ptermBoolQueryBuilder = QueryBuilders.boolQuery();
filterQuery(key, machValue, ptermBoolQueryBuilder);
boolBuilder.must(ptermBoolQueryBuilder);
}
// 精确过滤查询
for (String key : pageModel.getNoEqualsSearchConditioin().keySet()) {
Object noMachValue = pageModel.getNoEqualsSearchConditioin().get(key);
if (null == noMachValue) {
continue;
}
BoolQueryBuilder noBoolQueryBuilder = QueryBuilders.boolQuery();
filterQuery(key, noMachValue, noBoolQueryBuilder);
boolBuilder.mustNot(noBoolQueryBuilder);
}
//模糊过滤查询
for (String key : pageModel.getNoLikeSearchConditioin().keySet()) {
Object noMachValue = pageModel.getNoLikeSearchConditioin().get(key);
BoolQueryBuilder noBoolQueryBuilder = QueryBuilders.boolQuery();
if (null == noMachValue) {
continue;
}
for (String value : noMachValue.toString().split(ElasticSearchConstant.SPLIT_FLAG)) {
WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery(key, value);
noBoolQueryBuilder.should(wildcardQueryBuilder);
}
boolBuilder.mustNot(noBoolQueryBuilder);
}
return boolBuilder;
}
/**
* 或条件拼接
*
* @param orSearchCondition
* @param termBoolQueryBuilder
*/
private void filterOrQuery(Map<String, Object> orSearchCondition, BoolQueryBuilder termBoolQueryBuilder) {
for (String key : orSearchCondition.keySet()) {
Object value = orSearchCondition.get(key);
if (null == value) {
continue;
}
BoolQueryBuilder sueryBuilder = QueryBuilders.boolQuery();
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery(key + ".keyword", value);
sueryBuilder.should(termQueryBuilder);
TermQueryBuilder longtermQueryBuilder = QueryBuilders.termQuery(key, value);
sueryBuilder.should(longtermQueryBuilder);
termBoolQueryBuilder.should(sueryBuilder);
}
}
/**
* 非条件拼接
*
* @param key
* @param noMachValue
* @param noBoolQueryBuilder
*/
private void filterQuery(String key, Object noMachValue, BoolQueryBuilder noBoolQueryBuilder) {
for (String cv : noMachValue.toString().split(ElasticSearchConstant.SPLIT_FLAG)) {
BoolQueryBuilder termBoolQueryBuilder = QueryBuilders.boolQuery();
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery(key + ".keyword", cv);
termBoolQueryBuilder.should(termQueryBuilder);
TermQueryBuilder longtermQueryBuilder = QueryBuilders.termQuery(key, cv);
termBoolQueryBuilder.should(longtermQueryBuilder);
noBoolQueryBuilder.should(termBoolQueryBuilder);
}
}
/**
* 判断索引是否存在(ES7.X)
*
* @param indexName: 索引名称
* @return 是否存在结果
* @date 2019/12/10 17:01
* @author [email protected]
**/
public boolean isIndexExists(String indexName) {
boolean exists = false;
try {
GetIndexRequest getIndexRequest = new GetIndexRequest(indexName);
getIndexRequest.humanReadable(true);
exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
log.error("判断索引:{} 是否存在异常,异常内容:{}", indexName, e);
}
return exists;
}
/**
* 根据信息自动创建索引与mapping
* 构建mapping描述
*
* @param indexName 索引名称
* @param fieldMappingList 字段信息
* @return 创建结果
*/
private boolean createIndexAndCreateMapping(String indexName, List<FieldMapping> fieldMappingList) {
try {
// 开始封装ES索引的Mapping
XContentBuilder mapping = packESMapping(fieldMappingList, null);
mapping.endObject().endObject();
// 进行索引的创建
CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName);
createIndexRequest.mapping(mapping);
CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
boolean acknowledged = createIndexResponse.isAcknowledged();
if (acknowledged) {
log.info("索引:{}创建成功", indexName);
return true;
} else {
log.error("索引:{}创建失败", indexName);
return false;
}
} catch (IOException e) {
log.error("创建索引:{},出现异常:{}", indexName, e);
return false;
}
}
/**
* 获取XContentBuilder实体创建ES的Mapping
*
* @param fieldMappingList: 实体对象的类型集合
* @param mapping: XContentBuilder实体
* @return XContentBuilder实体
* @date 2019/12/12 16:50
* @author [email protected]
**/
private XContentBuilder packESMapping(List<FieldMapping> fieldMappingList, XContentBuilder mapping) throws IOException {
if (mapping == null) {
// 如果对象是空,首次进入,设置开始节点
mapping = XContentFactory.jsonBuilder()
.startObject()
.startObject("properties");
}
// 循环实体对象的类型集合封装ES的Mapping
for (FieldMapping info : fieldMappingList) {
String field = info.getField();
String dateType = info.getType();
// 类型为空默认设置为string
if (StringUtils.isBlank(dateType)) {
dateType = "string";
}
dateType = dateType.toLowerCase();
int participle = info.getParticiple();
if ("string".equals(dateType)) {
// 设置分词规则
if (participle == 0) {
mapping.startObject(field)
.field("type", "keyword")
.endObject();
} else if (participle == 1) {
mapping.startObject(field)
.field("type", "text")
.field("analyzer", "ik_smart")
.endObject();
} else if (participle == 2) {
mapping.startObject(field)
.field("type", "text")
.field("analyzer", "ik_max_word")
.endObject();
}
} else if ("text".equals(dateType)) {
// TODO lixiang 待优化
mapping.startObject(field).field("fielddata", true)
.field("type", dateType).startObject("fields").startObject("keyword")
.field("ignore_above", 256).field("type", "keyword")
.endObject().endObject().endObject();
} else if ("date".equals(dateType)) {
// 设置时间格式
mapping.startObject(field)
.field("type", dateType)
.field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis")
.endObject();
} else if ("float".equals(dateType) || "double".equals(dateType)) {
// 设置小数类型
mapping.startObject(field)
.field("type", "scaled_float")
.field("scaling_factor", 100)
.endObject();
} else if ("nested".equals(dateType)) {
// 设置聚合类型
mapping.startObject(field)
.field("type", dateType)
.startObject("properties");
// 由于nested类型内嵌文档,因此需要封装内嵌文档的Mapping
mapping = packESMapping(info.getFieldMappingList(), mapping);
// 设置尾部节点
mapping.endObject().endObject();
} else {
// object类型
if (info.getFieldMappingList() != null) {
// 内嵌文档循环封装(Java类对象中嵌套对象)
mapping.startObject(field).startObject("properties");
mapping = packESMapping(info.getFieldMappingList(), mapping);
mapping.endObject().endObject();
} else {
// 常量类型配置
mapping.startObject(field)
.field("type", dateType)
.field("index", true)
.endObject();
}
}
}
return mapping;
}
/**
* 创建mapping
*
* @param indexName 索引
* @param clazz 索引类型
*/
public boolean createIndexAndCreateMapping(String indexName, Class clazz, boolean dropOldIndex) {
if (this.isIndexExists(indexName)) {
if (dropOldIndex) {
if (deleteIndex(indexName)) {
return createIndexAndCreateMapping(indexName, FieldMappingUtils.getFieldInfo(clazz));
} else {
log.error("索引[{}]删除失败", indexName);
return false;
}
}
log.info("索引[{}]已经存在,忽略创建索引", indexName);
return true;
} else {
return createIndexAndCreateMapping(indexName, FieldMappingUtils.getFieldInfo(clazz));
}
}
/**
* 获取批量操作的Request
*
* @param indexName: 索引名称
* @param primaryKeyName: 主键名称
* @param paramListJson: 数据集合JSON
* @return BulkRequest对象
* @date 2019/12/10 18:53
* @author [email protected]
**/
private BulkRequest packBulkIndexRequest(String indexName, String primaryKeyName, String paramListJson) {
BulkRequest bulkRequest = new BulkRequest();
JSONArray jsonArray = JSONArray.parseArray(paramListJson);
if (jsonArray == null) {
return bulkRequest;
}
// 循环数据封装bulkRequest
jsonArray.forEach(obj -> {
Map<String, Object> map = (Map<String, Object>) obj;
IndexRequest indexRequest = new IndexRequest(indexName);
indexRequest.id(String.valueOf(map.get(primaryKeyName)));
indexRequest.source(JSON.toJSONString(obj), XContentType.JSON);
bulkRequest.add(indexRequest);
});
return bulkRequest;
}
/**
* 获取批量操作的Request
*
* @param indexName: 索引名称
* @param primaryKeyName: 主键名称
* @param paramListJson: 数据集合JSON
* @return BulkRequest对象
* @date 2019/12/10 18:53
* @author [email protected]
**/
private BulkRequest packBulkUpdateRequest(String indexName, String primaryKeyName, String paramListJson) {
BulkRequest bulkRequest = new BulkRequest();
JSONArray jsonArray = JSONArray.parseArray(paramListJson);
if (jsonArray == null) {
return bulkRequest;
}
jsonArray.forEach(obj -> {
Map<String, Object> map = (Map<String, Object>) obj;
UpdateRequest updateRequest = new UpdateRequest(indexName, String.valueOf(map.get(primaryKeyName)));
// 如果修改索引中不存在则进行新增
updateRequest.docAsUpsert(true);
updateRequest.doc(JSON.toJSONString(obj), XContentType.JSON);
bulkRequest.add(updateRequest);
});
return bulkRequest;
}
/**
* 获取批量操作的实体
*
* @param indexName: 索引名称
* @param ids: 主键ID集合
* @return BulkRequest批量操作对象
* @date 2019/12/10 20:53
* @author [email protected]
**/
private BulkRequest packBulkDeleteRequest(String indexName, List<Long> ids) {
BulkRequest bulkRequest = new BulkRequest();
ids.forEach(id -> {
DeleteRequest deleteRequest = new DeleteRequest(indexName);
deleteRequest.id(String.valueOf(id));
bulkRequest.add(deleteRequest);
});
return bulkRequest;
}
}
到此RestHighLevelClient操作Elasticsearch7.X的工具类已经封装完毕,项目中使用时可以将ES工具类单独抽取成一个独立的模块,需要使用的模块增加ES工具类模块的依赖集合。
感谢以下文章