这里提供一个通用的操作接口和文档的方法类,大多数的使用场景都已经覆盖,话不多说,开整:
- 首先,创建一个自定义注解,结合elasticsearch提供的注解一起,配合实体类操作文档和索引。
package com.cqvip.innocence.common.annotation;
import java.lang.annotation.*;
/**
* @ClassName DocumentId
* @Description es文档数据的id标识
* @Author Innocence
* @Date 2021/6/16 11:34
* @Version 1.0
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DocumentId {
}
2.写一个与文档对应的DTO(这里只是示例,具体的实体根据对应需求建立)
package com.cqvip.innocence.project.model.entity;
import com.cqvip.innocence.common.annotation.DocumentId;
import lombok.Data;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.io.Serializable;
/**
* @ClassName EsTestInfo
* @Description es测试数据
* @Author Innocence
* @Date 2021/6/16 13:57
* @Version 1.0
*/
@Data
@Document(indexName = "test_info")
public class EsTestInfo implements Serializable {
private static final long serialVersionUID = 6735913456541605819L;
@DocumentId
@Field(type = FieldType.Keyword)
private String infoId;
private String name;
@Field(type = FieldType.Integer)
private Integer age;
@Field(type = FieldType.Keyword)
private String sex;
@Field(type = FieldType.Keyword)
private String[] roles;
}
3.索引操作的接口和实现
package com.cqvip.innocence.project.esservice;
/**
* @ClassName IndexService
* @Description 索引的增删改查
* @Author Innocence
* @Date 2021/3/1 14:06
* @Version 1.0
*/
public interface IndexService<T>{
/**
* 根据类创建索引
* @author Innocence
* @date 2021/6/16
* @param clazz
* @return java.lang.Boolean
*/
Boolean createIndexByClass(Class<T> clazz);
/**
* 根据名称创建索引
* 不建议使用,因为很多设置需要自己写配置文件
* @author Innocence
* @date 2021/3/1
* @param indexName
* @return java.lang.Boolean
*/
Boolean createIndexByName(String indexName);
/**
* 根据索引名判断索引是否存在
* @author Innocence
* @date 2021/6/16
* @param indexName 索引名
* @return java.lang.Boolean
*/
Boolean isIndexExist(String indexName);
/**
* 根据索引名删除索引
* @author Innocence
* @date 2021/6/16
* @param indexName 索引名
* @return java.lang.Boolean
*/
Boolean deleteIndexByName(String indexName);
/**
* 获取被标记的索引名
* @author Innocence
* @date 2021/6/16
* @param clazz
* @return java.lang.String
*/
String getIndexName(Class<T> clazz);
}
package com.cqvip.innocence.project.esservice.impl;
import cn.hutool.core.util.StrUtil;
import com.cqvip.innocence.project.esservice.IndexService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.stereotype.Service;
import java.lang.annotation.Annotation;
/**
* @ClassName IndexServiceImpl
* @Description 抽象
* @Author Innocence
* @Date 2021/3/1 14:24
* @Version 1.0
*/
@Service
public class IndexServiceImpl<T> implements IndexService<T> {
@Autowired
private ElasticsearchRestTemplate restTemplate;
@Override
public String getIndexName(Class<T> clazz){
Annotation documentAnnotation = clazz.getDeclaredAnnotation(Document.class);
if(documentAnnotation==null){
return null;
}
String indexName = ((Document) documentAnnotation).indexName();
if (StrUtil.isNotBlank(indexName)){
return indexName;
}
return null;
}
@Override
public Boolean createIndexByClass(Class<T> clazz) {
Boolean indexExist = isIndexExist(getIndexName(clazz));
if (indexExist){
return false;
}
IndexOperations indexOps = restTemplate.indexOps(clazz);
boolean result1 = indexOps.create();
boolean result2 = indexOps.putMapping(indexOps.createMapping(clazz));
return result1&result2;
}
@Override
public Boolean createIndexByName(String indexName) {
Boolean indexExist = isIndexExist(indexName);
if (indexExist){
return false;
}
IndexOperations indexOps = restTemplate.indexOps(IndexCoordinates.of(indexName));
return indexOps.create();
}
@Override
public Boolean isIndexExist(String indexName) {
IndexOperations indexOps = restTemplate.indexOps(IndexCoordinates.of(indexName));
return indexOps.exists();
}
@Override
public Boolean deleteIndexByName(String indexName) {
IndexOperations indexOps = restTemplate.indexOps(IndexCoordinates.of(indexName));
return indexOps.delete();
}
}
4.文档操作的接口和实现类
package com.cqvip.innocence.project.esservice;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.query.Query;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* @ClassName DocumentService
* @Description 适用于本项目的数据的增删改查
* @Author Innocence
* @Date 2021/3/1 15:43
* @Version 1.0
*/
public interface DocumentService<T> {
/**
* 设置高亮字段
* @author Innocence
* @date 2021/3/2
* @param fields 需要设置的高亮字段
* @return org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder
*/
HighlightBuilder getHighlightBuilder(String[] fields);
/**
* 根据id判断文档是否存在于指定索引中
* @author Innocence
* @date 2021/3/1
* @param id
* @param clazz
* @return java.lang.Boolean
*/
Boolean isExist(String id, Class<T> clazz);
/**
* 单条数据插入
* @author Innocence
* @date 2021/3/1
* @param t 需插入的数据
* @return java.lang.String 返回文档id
*/
String saveByEntity(T t);
/**
* 批量插入
* @author Innocence
* @date 2021/3/1
* @param entities 待插入的数据实体集合
* @return java.util.List<java.lang.String> 返回idList
*/
List<String> saveBatchByEntities(List<T> entities) throws Exception;
/**
* 单条数据更新
* @author Innocence
* @date 2021/3/1
* @param t 需要更新的实体数据
* @return void
*/
void updateByEntity(T t);
/**
* 批量更新
* @author Innocence
* @date 2021/3/1
* @param entities 待更新的数据实体集合
* @return void
*/
void updateByEntities(List<T> entities);
/**
* 根据id删除数据
* @author Innocence
* @date 2021/3/1
* @param id
* @param clazz
* @return java.lang.String 被删除的id
*/
String deleteById(String id, Class<T> clazz);
/**
* 根据id批量删除数据
* @author Innocence
* @date 2021/3/12
* @param ids
* @param clazz
* @return void
*/
void deleteByIds(List<String> ids, Class<T> clazz);
/**
* 根据条件删除数据
* @author Innocence
* @date 2021/3/1
* @param query 条件构造器
* @param clazz
* @return void
*/
void deleteByQuery(Query query, Class<T> clazz);
/**
* 根据id查询数据 (基于注解形式设置了索引mapping)
* @author Innocence
* @date 2021/3/1
* @param clazz
* @return T
*/
T getEntityById(String id,Class<T> clazz);
/**
* 根据id批量查询数据 (基于注解形式设置了索引mapping)
* @author Innocence
* @date 2021/8/24
* @return java.util.List<T>
*/
List<T> getEntityByIds(List<String> ids, Class<T> clazz);
/**
* 查询符合条件的总条数
* @author Innocence
* @date 2021/3/2
* @return java.lang.Long
*/
Long getCount(Query query,Class<T> clazz);
/**
* 查询符合条件的实体list
* @author Innocence
* @date 2021/3/2
* @param query 构建的查询条件 主要使用NativeSearchQuery 来进行构造
* @param isHighLight 是否映射高亮(true映射高亮,否则不映射)
* @return java.util.List<T>
*/
List<T> getInfoList(Query query, Class<T> clazz,Boolean isHighLight);
/**
* 查询符合条件的分页
* @author Innocence
* @date 2021/3/2
* @param query 构建的查询条件 主要使用NativeSearchQuery 来进行构造
* @return org.springframework.data.domain.Page<T>
*/
Map<String,Object> getPageList(Query query, PageRequest pageRequest,Class<T> clazz);
/**
* 根据条件获取聚类信息
* springboot-data 封装的聚类查询速度很慢,这里直接用client操作
* @author Innocence
* @date 2021/3/19
* @param query, clazz
* @return java.util.Map<java.lang.String,java.util.List<? extends org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket>>
*/
Map<String, List<? extends Terms.Bucket>> getFacetByQuery(SearchSourceBuilder query, Class<T> clazz) throws IOException;
}
package com.cqvip.innocence.project.esservice.impl;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.cqvip.innocence.common.annotation.DocumentId;
import com.cqvip.innocence.common.exception.ElasticServiceException;
import com.cqvip.innocence.project.esservice.DocumentService;
import com.cqvip.innocence.project.esservice.IndexService;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.lucene.search.IndexSearcher;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.*;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.lang.reflect.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @ClassName DocumentServiceImpl
* @Description TODO
* @Author Innocence
* @Date 2021/3/1 16:27
* @Version 1.0
*/
@Service
public class DocumentServiceImpl<T> implements DocumentService<T> {
@Autowired
private ElasticsearchRestTemplate restTemplate;
@Qualifier("highLevelClient")
@Autowired
private RestHighLevelClient highLevelClient;
@Autowired
private IndexService<T> indexService;
/**
* 反射获取es实体代表id的字段名
* @author Innocence
* @date 2021/6/17
* @param clazz
* @return java.lang.String
*/
private String getPrimaryNameByClass(Class<T> clazz){
Field[] fields = clazz.getDeclaredFields();
for (Field f:fields) {
DocumentId id = f.getAnnotation(DocumentId.class);
if (id != null){
return f.getName();
}
}
return null;
}
/**
* 根据注解获取实体类关联es属性的id值
* @author Innocence
* @date 2021/6/16
* @return java.lang.String
*/
private String getPrimaryValueByEntity(T t){
Class<T> clazz = (Class<T>) t.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field f:fields) {
Class<?> type = f.getType();
if (type.getTypeName().equals(String.class.getTypeName())){
DocumentId id = f.getAnnotation(DocumentId.class);
if (id != null){
try {
String replace = f.getName()
.replace(f.getName().substring(0, 1), f.getName().substring(0, 1).toUpperCase());
Method method = clazz.getMethod("get" + replace);
String invoke = (String) method.invoke(t);
if (StrUtil.isNotBlank(invoke)){
return invoke;
}else {
return null;
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
return null;
}
/**
* 类上无索引名的统一异常处理
* @author Innocence
* @date 2021/8/6
* @param clazz
* @return void
*/
private String indexNameExceptionHandler(Class<T> clazz){
String indexName = indexService.getIndexName(clazz);
if (StrUtil.isBlank(indexName)){
throw new ElasticServiceException("The index name on the current class does not exist!");
}else {
return indexName;
}
}
/**
* 映射高亮字段到原生属性
* @author Innocence
* @date 2021/3/2
* @param searchHits
* @return java.util.List<T>
*/
private <T> List<T> mappingHighlight(List<SearchHit<T>> searchHits){
List<T> infoList = new ArrayList<>();
for (SearchHit<T> searchHit : searchHits) {
T content = searchHit.getContent();
Map<String, List<String>> highlightFields = searchHit.getHighlightFields();
for (Map.Entry<String, List<String>> entry : highlightFields.entrySet()) {
try {
PropertyUtils.setProperty(content,entry.getKey(),entry.getValue().get(0));
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
}
infoList.add(content);
}
return infoList;
}
@Override
public HighlightBuilder getHighlightBuilder(String[] fields) {
HighlightBuilder highlightBuilder = new HighlightBuilder();
for (String field : fields) {
highlightBuilder.field(field);
}
highlightBuilder.requireFieldMatch(false); //如果要多个字段高亮,这项要为false
highlightBuilder.preTags("<span style=\"color:red\">");
highlightBuilder.postTags("</span>");
//下面这两项,如果你要高亮如文字内容等有很多字的字段,必须配置,不然会导致高亮不全,文章内容缺失等
highlightBuilder.fragmentSize(800000); //最大高亮分片数
highlightBuilder.numOfFragments(0); //从第一个分片获取高亮片段
return highlightBuilder;
}
@Override
public Boolean isExist(String id,Class<T> clazz) {
String name = indexNameExceptionHandler(clazz);
return restTemplate.exists(id, IndexCoordinates.of(name));
}
@Override
public String saveByEntity(T t) {
String id = getPrimaryValueByEntity(t);
String indexName = indexNameExceptionHandler((Class<T>) t.getClass());
if (StrUtil.isBlank(id)){
throw new ElasticServiceException("Document id cannot be empty!");
}
IndexQuery build = new IndexQueryBuilder().withId(id).withObject(t).build();
String index = restTemplate.index(build, IndexCoordinates.of(indexName));
//业务需要,新增后马上刷新,ElasticsearchRestTemplate是默认不立即刷新(立即刷新会影响性能)
restTemplate.indexOps(t.getClass()).refresh();
return index;
}
@Override
public List<String> saveBatchByEntities(List<T> entities){
Class<T> clazz =(Class<T>) entities.get(0).getClass();
String indexName = indexNameExceptionHandler(clazz);
List<IndexQuery> queryList = new ArrayList<>();
for (T item:entities){
String id = getPrimaryValueByEntity(item);
if (StrUtil.isBlank(id)){
throw new ElasticServiceException("Document id cannot be empty!");
}
IndexQuery build = new IndexQueryBuilder().withId(id).withObject(item).build();
queryList.add(build);
}
List<String> idList = restTemplate.bulkIndex(queryList, IndexCoordinates.of(indexName));
restTemplate.indexOps(IndexCoordinates.of(indexName)).refresh();
return idList;
}
@Override
public void updateByEntity(T t) {
String indexName = indexNameExceptionHandler((Class<T>) t.getClass());
Document document = Document.parse(JSON.toJSONString(t));
document.setId(getPrimaryValueByEntity(t));
UpdateQuery build = UpdateQuery.builder(document.getId())
.withRefresh(UpdateQuery.Refresh.Wait_For) //更新后立即刷新可见(影响性能,按需注释或使用)
.withDocument(document)
.build();
restTemplate.update(build, IndexCoordinates.of(indexName));
}
@Override
public void updateByEntities(List<T> entities) {
Class<T> clazz =(Class<T>) entities.get(0).getClass();
String indexName = indexNameExceptionHandler(clazz);
List<UpdateQuery> updateQueries = new ArrayList<>();
entities.forEach(item->{
Document document = Document.parse(JSON.toJSONString(item));
document.setId(getPrimaryValueByEntity(item));
UpdateQuery build = UpdateQuery.builder(document.getId())
.withRefresh(UpdateQuery.Refresh.Wait_For)
// .withDocAsUpsert(true) //不加默认false。true表示更新时不存在就插入
.withDocument(document)
.build();
updateQueries.add(build);
});
restTemplate.bulkUpdate(updateQueries,IndexCoordinates.of(indexName));
}
@Override
public String deleteById(String id, Class<T> clazz) {
String indexName = indexNameExceptionHandler(clazz);
return restTemplate.delete(id,IndexCoordinates.of(indexName));
}
@Override
public void deleteByIds(List<String> ids, Class<T> clazz) {
String indexName = indexNameExceptionHandler(clazz);
StringQuery query = new StringQuery(QueryBuilders.termsQuery(getPrimaryNameByClass(clazz), ids).toString());
restTemplate.delete(query,clazz,IndexCoordinates.of(indexName));
}
@Override
public void deleteByQuery(Query query,Class<T> clazz) {
String indexName = indexNameExceptionHandler(clazz);
restTemplate.delete(query,clazz,IndexCoordinates.of(indexName));
}
@Override
public T getEntityById(String id,Class<T> clazz) {
return restTemplate.get(id,clazz);
}
@Override
public List<T> getEntityByIds(List<String> ids, Class<T> clazz) {
String indexName = indexNameExceptionHandler(clazz);
NativeSearchQuery build = new NativeSearchQueryBuilder().withIds(ids).build();
return restTemplate.multiGet(build, clazz, IndexCoordinates.of(indexName));
}
@Override
public Long getCount(Query query,Class<T> clazz) {
return restTemplate.count(query,clazz);
}
@Override
public List<T> getInfoList(Query query,Class<T> clazz,Boolean isHighLight) {
query.setTrackTotalHits(true);
SearchHits<T> search = restTemplate.search(query,clazz);
if (isHighLight){
return mappingHighlight(search.getSearchHits());
}
List<T> infoList = new ArrayList<>();
for (SearchHit<T> searchHit : search){
infoList.add(searchHit.getContent());
}
return infoList;
}
@Override
public Map<String, Object> getPageList(Query query, PageRequest pageRequest,Class<T> clazz) {
query.setTrackTotalHits(true);
query.setPageable(pageRequest);
SearchHits<T> search = restTemplate.search(query,clazz);
Aggregations aggregations = search.getAggregations();
List<SearchHit<T>> searchHits = search.getSearchHits();
List<T> esSourceInfos = mappingHighlight(searchHits);
Page infos = new PageImpl(
esSourceInfos,
pageRequest,
search.getTotalHits());
Map<String, Object> map = new HashMap<>();
map.put("page",infos);
map.put("ag",formatFacet(aggregations));
return map;
}
@Override
public Map<String, List<? extends Terms.Bucket>> getFacetByQuery(SearchSourceBuilder query, Class<T> clazz) throws IOException {
String indexName = indexNameExceptionHandler(clazz);
SearchRequest request = new SearchRequest(indexName);
SearchSourceBuilder builder = query;
request.source(builder);
SearchResponse response = highLevelClient.search(request, RequestOptions.DEFAULT);
Aggregations aggregations = response.getAggregations();
return formatFacet(aggregations);
}
/**
* 格式化聚类数据
* @author Innocence
* @date 2021/3/18
* @param aggregations
* @return java.util.Map<java.lang.String,java.util.List<java.util.Map<java.lang.String,java.lang.Object>>>
*/
private Map<String, List<? extends Terms.Bucket>> formatFacet(Aggregations aggregations){
if (aggregations == null){
return null;
}
Map<String, List<? extends Terms.Bucket>> map = new HashMap<>();
List<Aggregation> list = aggregations.asList();
list.forEach(item->{
ParsedStringTerms newItem = (ParsedStringTerms) item;
String name = newItem.getName();
List<? extends Terms.Bucket> buckets = newItem.getBuckets();
map.put(name,buckets);
});
return map;
}
}
5.使用示例,使用@Autowired自动注入,并且带上泛型类
package com.cqvip.innocence.tests;
import com.cqvip.innocence.project.esservice.DocumentService;
import com.cqvip.innocence.project.esservice.IndexService;
import com.cqvip.innocence.project.model.entity.EsTestInfo;
import org.apache.commons.lang3.builder.ToStringExclude;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName EsTest
* @Description TODO
* @Author Innocence
* @Date 2021/6/16 14:05
* @Version 1.0
*/
@SpringBootTest
public class EsTest {
@Autowired
private IndexService<EsTestInfo> indexService;
@Autowired
private DocumentService<EsTestInfo> documentService;
@Test
public void indexOperation(){
Boolean index = indexService.createIndexByClass(EsTestInfo.class);
System.out.println("index = " + index);
}
@Test
public void saveByEntity(){
EsTestInfo esTestInfo = new EsTestInfo();
esTestInfo.setAge(20);
esTestInfo.setInfoId("test_02");
esTestInfo.setName("一库一库");
esTestInfo.setSex("男");
String entity = null;
try {
entity = documentService.saveByEntity(esTestInfo);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("entity = " + entity);
}
@Test
public void isExist(){
Boolean test_01 = documentService.isExist("test_01", EsTestInfo.class);
System.out.println("test_01 = " + test_01);
}
@Test
public void updateByEntity(){
EsTestInfo info = documentService.getEntityById("test_01", EsTestInfo.class);
info.setName(info.getName()+"_update");
documentService.updateByEntity(info);
}
@Test
public void deleteById(){
String deleteById = documentService.deleteById("test_01", EsTestInfo.class);
System.out.println("deleteById = " + deleteById);
}
@Test
public void saveBatchByEntities(){
List<EsTestInfo> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
EsTestInfo info = new EsTestInfo();
info.setInfoId("test_batch_"+(i+1)+"");
info.setName("name_batch_"+(i+1)+"");
info.setSex("N");
info.setAge((i+1)*10);
info.setRoles(new String[]{"管理员","超级管理员"});
list.add(info);
}
try {
documentService.saveBatchByEntities(list);
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void getInfoList(){
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
.must(QueryBuilders.wildcardQuery("infoId","test_batch_*"));
NativeSearchQuery build = queryBuilder.withQuery(boolQuery).build();
List<EsTestInfo> infoList = documentService.getInfoList(build,EsTestInfo.class,true);
}
@Test
public void count(){
Long count = documentService.getCount(new NativeSearchQueryBuilder().build(),EsTestInfo.class);
System.out.println("count = " + count);
}
@Test
public void deleteByIds(){
ArrayList<String> strings = new ArrayList<>();
strings.add("test_01");
strings.add("test_02");
Class<EsTestInfo> clazz = EsTestInfo.class;
documentService.deleteByIds(strings, clazz);
}
@Test
public void update() {
EsTestInfo info = new EsTestInfo();
info.setName("wait_for");
info.setInfoId("test_batch_wait_for_2");
documentService.saveByEntity(info);
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
.must(QueryBuilders.wildcardQuery("infoId","test_batch_*"));
NativeSearchQuery build = queryBuilder.withQuery(boolQuery).build();
List<EsTestInfo> infoList = documentService.getInfoList(build,EsTestInfo.class);
System.out.println("infoList = " + infoList);
}
}