这几天写了一个关于es的工具类,主要封装了业务中常用es的常用方法。
本文中使用到的elasticsearch版本6.7,但实际上也支持es7.x以上版本,因为主要是对springboot提供的:ElasticsearchRestTemplate 提供的API做的二次封装。目的是:让不懂es的开发人员新手也能轻松上手。
一、概述
整个工程分为es-api与es-server。
es-api为对外公共jar,包含了es映射实体;
es-server包含了具体的es工具类与Repository接口(下文会提到)。并通过necos配置中心统一管理es配置参数。
外部业务模块可引入es-api jar maven依赖,由Jar提供的入口,通过httpClient或feign调用(springcloud分布式项目)到es-server服务上的es工具类,得到需要的数据。
二、使用
这里仅以springcloud分布式项目简单为例
业务方用户服务user 引入es-api maven
/** * @author: shf * description: es-server feign接口 */ public interface EsServerClient { @PostMapping(value = "/queryList", produces = {"application/json"}) public <T> List<T> queryList(@RequestBody T t); }
在user服务中创建feignClient继承自es-api中的EsServerClient
@FeignClient(contextId = "esFeignClient", name = "es-server") public interface EsFeignClient extends EsServerClient { }
在user服务的代码中即可调用
@AutoWired public EsFeignClient esFeignClient; public void test() { //.......如业务方Dto与es映射实体转换 等省略 //.... EmployeeEs employee = new EmployeeEs(); List queryList = Stream.of(employee.new QueryRelation<String>("张三", EntityEs.SHOULD, 5F), employee.new QueryRelation<String>("李四", EntityEs.SHOULD, 20F)).collect(Collectors.toList()); employee.setFieldQueryMap(new EsMapUtil().put(EmployeeEs::getUserName, queryList).put(EmployeeEs::getUserAge, employee.new RangeRelation(20, EntityEs.GTE, null, null, EntityEs.MUST))); //排序查询 employee.setOrderMap(new EsMapUtil().put(EmployeeEs::getUserId, SortOrder.DESC)); List<EmployeeEs> employeeEs = esFeignClient.queryList(employee); //.....employeeEs与业务方Dto转换 }
三、具体实现
es-api 引入es依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> <exclusions> <exclusion> <groupId>org.elasticsearch.client</groupId> <artifactId>transport</artifactId> </exclusion> <exclusion> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>transport</artifactId> <version>6.7.0</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>6.7.0</version> </dependency>
排除后重新引入对应的Es版本6.7,避免因版本不一致导致的一些坑。
es-server 服务 application.yml配置es
spring: elasticsearch: rest: #ES的连接地址,多个地址用逗号分隔 uris: localhost:9200 username: kibana password: pass #连接超时时间 connection-timeout: 1000 #读取超时时间 read-timeout: 1000
一、映射实体
1、与ES mapping结构对应的映射实体:EmployeeEs
说明:
①设置的字段与该es对应的该索引完全对应,不存在多余字段。
②项目中引入了spring-boot-starter-data-elasticsearch,所以可直接使用注解形式设置索引信息与mapping结构信息,详见示例
③@JsonIgnoreProperties({"orderMap","pageNumber","pageSize","highlightFields","preTags","postTags","fieldQueryMap","scrollId","aggregationMap","multiLayerQueryList"})
作用:在保存文档到es时忽略父类EntityEs中的功能性字段,下文会提到
④@EsRepository(EmployeeEsRepository.class)
作用:通过注解过去该映射对应的Repository接口
/** * 员工对象 * <p> * 注解:@Document用来声明Java对象与ElasticSearch索引的关系 indexName 索引名称 type 索引类型 shards 主分区数量,默认5 * replicas 副本分区数量,默认1 createIndex 索引不存在时,是否自动创建索引,默认true */ @Setter @Getter @Builder @AllArgsConstructor @NoArgsConstructor @EsRepository(EmployeeEsRepository.class) @JsonIgnoreProperties({"orderMap","pageNumber","pageSize","highlightFields","preTags","postTags","fieldQueryMap","scrollId","aggregationMap","multiLayerQueryList"}) @Document(indexName = "employee_index", type = "employee_type", shards = 1, replicas = 0, createIndex = true) public class EmployeeEs extends EntityEs { @Id @Field(type = FieldType.Keyword) private Long userId; //@Field(type = FieldType.Text, analyzer = "ik_max_word") @MultiField(mainField = @Field(type = FieldType.Text, analyzer = "ik_max_word"), otherFields = @InnerField(suffix = "trueName", type = FieldType.Keyword)) private String userName; @Field(type = FieldType.Keyword) private String userCode; @Field(type = FieldType.Integer) private Integer userAge; @Field(type = FieldType.Keyword) private String userMobile; @Field(type = FieldType.Date) private Date birthDay; @Field(type = FieldType.Keyword) private String userSex; @Field(type = FieldType.Text, analyzer = "ik_max_word") private String remarks; }
2、Repository接口:EmployeeEsRepository
/** * @author: shf * description: 可根据映射实体设置自动生成mapping结构;支持bean的增删改查操作 * date: 2022/2/23 10:47 */ @Component public interface EmployeeEsRepository extends CrudRepository<EmployeeEs,Long> { }
二、功能性实体类:EntityEs
说明:
①所有字段非es索引中的mapping属性字段,均为功能性字段,如排序、高亮、分页设置和一些常量设置等,详见贴码
②所有es映射实体类均需继承该实体
/** * @author: shf description: 功能性字段(非mapping结构字段) * date: 2022/3/1 15:07 */ @Data public class EntityEs { /** * 组合多查询常量 */ /** * 文档 必须 匹配这些条件才能被查询到。相当于sql中的and */ public static String MUST = "must"; /** * 文档 必须不 匹配这些条件才能被查询到。相当于sql中的 not */ public static String MUST_NOT = "must_not"; /** * 如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分。相当于sql中的or */ public static String SHOULD = "should"; /** * 必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档 */ public static String FILTER = "filter"; /** * 至少匹配一项should子句 */ public static String MINIMUM_SHOULD_MATCH = "minimum_should_match"; /** * 多字段排序查询 */ public EsMapUtil orderMap; /** * 分页查询 */ public Integer pageNumber; public Integer pageSize; /** * 游标分页ID */ public String scrollId; /** * 游标分页ID有效期 单位:毫秒 */ public static Long scrollIdExpireTime = 1000 * 60 * 2L; /** * 游标分页ID最小有效期 单位:毫秒 */ public static Long scrollIdMinExpireTime = 1000L; /** * 高亮查询 */ public List<String> highlightFields; public String preTags; public String postTags; /** * 字段查询 */ public EsMapUtil fieldQueryMap; /** * 聚合查询,当前只支持单个字段分组聚合count与sum,只针对keyword类型字段有效 */ public EsMapUtil aggregationMap; public static String COUNT = "count"; public static String SUM = "sum"; /** * 多层(bool)查询 */ public List multiLayerQueryList; /** * 范围查询常量 */ public static String GT = "gt"; public static String GTE = "gte"; public static String LT = "lt"; public static String LTE = "lte"; @Data public class RangeRelation<T> { //String fieldKey; T fieldMinValue; String fieldMinMode; T fieldMaxValue; String fieldMaxMode; String queryMode; public RangeRelation(T fieldMinValue, String fieldMinMode, T fieldMaxValue, String fieldMaxMode, String queryMode) { this.fieldMinValue = fieldMinValue; this.fieldMinMode = fieldMinMode; this.fieldMaxValue = fieldMaxValue; this.fieldMaxMode = fieldMaxMode; this.queryMode = queryMode; } } @Data public class QueryRelation<T> { T fieldValue; String queryMode; Float boostValue; public QueryRelation(T fieldValue, String queryMode) { this.fieldValue = fieldValue; this.queryMode = queryMode; } public QueryRelation(T fieldValue, String queryMode, Float boostValue) { this.fieldValue = fieldValue; this.queryMode = queryMode; this.boostValue = boostValue; } } @Data public class MultiLayerRelation { String queryMode; EsMapUtil map; List<EntityEs.MultiLayerRelation> multiLayerList; public MultiLayerRelation(String queryMode, EsMapUtil map) { this.queryMode = queryMode; this.map = map; } public MultiLayerRelation(String queryMode, EsMapUtil map, List<MultiLayerRelation> multiLayerList) { this.queryMode = queryMode; this.map = map; this.multiLayerList = multiLayerList; } } }
三、小工具:EsMapUtil
说明:封装了一个map工具,编码简洁链式调用,应用了方法引用特性避免了字符串硬编码造成单词拼错的情况。
/** * @author: shf description: 函数式接口 便于方法引用获取实体字段名称 * date: 2022/3/4 13:41 */ @FunctionalInterface public interface IGetterFunction<T> extends Serializable{ Object get(T source); }
/** * @author: shf * description * date: 2019/11/13 18:30 */ public class EsMapUtil extends LinkedHashMap<String, Object> { public <T> EsMapUtil put(IGetterFunction<T> fn, Object value) { String key = getFieldName(fn); super.put(key, value); return this; } public <T> EsMapUtil putStr(String key, Object value) { super.put(key, value); return this; } private static Map<Class, SerializedLambda> CLASS_LAMDBA_CACHE = new ConcurrentHashMap<>(); /*** * 转换方法引用为属性名 * @param fn * @return */ public <T> String getFieldName(IGetterFunction<T> fn) { SerializedLambda lambda = getSerializedLambda(fn); String methodName = lambda.getImplMethodName(); String prefix = null; if (methodName.startsWith("get")) { prefix = "get"; } // 截取get之后的字符串并转换首字母为小写 return toLowerCaseFirstOne(methodName.replace(prefix, "")); } /** * 首字母转小写 * * @param s */ public String toLowerCaseFirstOne(String s) { if (Character.isLowerCase(s.charAt(0))) { return s; } else { return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString(); } } public static SerializedLambda getSerializedLambda(Serializable fn) { SerializedLambda lambda = CLASS_LAMDBA_CACHE.get(fn.getClass()); if (lambda == null) { try { Method method = fn.getClass().getDeclaredMethod("writeReplace"); method.setAccessible(Boolean.TRUE); lambda = (SerializedLambda) method.invoke(fn); CLASS_LAMDBA_CACHE.put(fn.getClass(), lambda); } catch (Exception e) { e.printStackTrace(); } } return lambda; } }
四、Es通用工具类EsService
/** * @author: shf description: es工具类,支持:分页(支持游标分页)、高亮(支持自定义标签)、范围查找、bool组合查询、多层bool套bool、多字段排序、加权重、聚合、二级字段查询 * date: 2022/2/23 10:54 */ @Component @Slf4j public class EsService { @Autowired private ElasticsearchRestTemplate restTemplate; @Autowired private ApplicationContext context; /** * 判断索引是否存在 * * @return boolean */ public <T> boolean indexExists(Class<T> clazz) { return restTemplate.indexExists(clazz); } /** * 判断索引是否存在 * * @param indexName 索引名称 * @return boolean */ public boolean indexExists(String indexName) { return restTemplate.indexExists(indexName); } /** * 创建索引(推荐使用:因为Java对象已经通过注解描述了Setting和Mapping) * * @return boolean */ public <T> boolean indexCreate(Class<T> clazz) { boolean createFlag = restTemplate.createIndex(clazz); boolean mappingFlag = restTemplate.putMapping(clazz); return createFlag && mappingFlag; } /** * 索引删除 * * @param indexName 索引名称 * @return boolean */ public boolean indexDelete(String indexName) { return restTemplate.deleteIndex(indexName); } /** * 新增数据 * * @param bean 数据对象 */ public <T> void saveBean(T bean) { EsRepository esRepositoryAnno = bean.getClass().getAnnotation(EsRepository.class); CrudRepository crudRepository = (CrudRepository) context.getBean(esRepositoryAnno.value()); crudRepository.save(bean); } /** * 批量新增数据 * * @param list 数据集合 */ public <T> void saveList(List<T> list) { if (CollectionUtils.isEmpty(list)) { return; } EsRepository esRepositoryAnno = list.get(0).getClass().getAnnotation(EsRepository.class); CrudRepository crudRepository = (CrudRepository) context.getBean(esRepositoryAnno.value()); crudRepository.saveAll(list); } /** * 根据对象删除数据,主键ID不能为空 * * @param bean 对象 */ public <T> void deleteByBean(T bean) { EsRepository esRepositoryAnno = bean.getClass().getAnnotation(EsRepository.class); CrudRepository crudRepository = (CrudRepository) context.getBean(esRepositoryAnno.value()); crudRepository.delete(bean); } /** * 根据对象集合,批量删除 * * @param beanList 对象集合 */ public <T> void deleteAllByBeanList(List<T> beanList) { if (CollectionUtils.isEmpty(beanList)) { return; } EsRepository esRepositoryAnno = beanList.get(0).getClass().getAnnotation(EsRepository.class); CrudRepository crudRepository = (CrudRepository) context.getBean(esRepositoryAnno.value()); crudRepository.deleteAll(beanList); } /** * 删除所有 */ public <T> void deleteAll(T bean) { EsRepository esRepositoryAnno = bean.getClass().getAnnotation(EsRepository.class); CrudRepository crudRepository = (CrudRepository) context.getBean(esRepositoryAnno.value()); crudRepository.deleteAll(); } /** * 修改数据 * * @param t 修改数据对象,ID不能为空 */ public <T> boolean updateByBean(T t) throws IllegalAccessException { Class clazz = t.getClass(); Field[] Fields = clazz.getDeclaredFields(); String beanId = null; String beanIdName = null; for (Field f : Fields) { f.setAccessible(true); if (f.isAnnotationPresent(org.springframework.data.annotation.Id.class)) { beanId = String.valueOf(f.get(t)); beanIdName = f.getName(); } } if (StringUtils.isBlank(beanId)) { log.warn("id不能为空"); return false; } if (Objects.isNull(restTemplate.queryForObject(GetQuery.getById(beanId), clazz))) { log.warn("该文档不存在"); return false; } Document annotation = (Document) clazz.getAnnotation(Document.class); UpdateRequest updateRequest = new UpdateRequest(); //冲突重试 updateRequest.retryOnConflict(1); updateRequest.doc(JSON.toJSONString(t), XContentType.JSON); //默认是_id来路由的,用来路由到不同的shard,会对这个值做hash,然后映射到shard。所以分片 updateRequest.routing(beanId); UpdateQuery query = new UpdateQueryBuilder().withIndexName(annotation.indexName()).withType(annotation.type()).withId(beanId) .withDoUpsert(false)//不加默认false。true表示更新时不存在就插入 .withClass(clazz).withUpdateRequest(updateRequest).build(); UpdateResponse updateResponse = restTemplate.update(query); if (!Objects.equals(updateResponse.getShardInfo().getSuccessful(), 1)) { return false; } return true; } /** * 根据bean ID 查询 * * @param beanId * @param clazz * @param <T> */ public <T> T queryBeanById(String beanId, Class<T> clazz) { return restTemplate.queryForObject(GetQuery.getById(beanId), clazz); } /** * 数据查询,返回List * * @return List<T> */ public <T> List<T> queryList(T t) throws IllegalAccessException, NoSuchFieldException { BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); Class clazz = (Class) t.getClass(); Map<String, Object> queryMap = (LinkedHashMap) clazz.getField("fieldQueryMap").get(t); Map<String, String> orderMap = (LinkedHashMap) clazz.getField("orderMap").get(t); List<String> highlightFields = (List<String>) clazz.getField("highlightFields").get(t); String preTags = StringUtils.isNotBlank((String) clazz.getField("preTags").get(t)) ? (String) clazz.getField("preTags").get(t) : "<em>"; String postTags = StringUtils.isNotBlank((String) clazz.getField("postTags").get(t)) ? (String) clazz.getField("postTags").get(t) : "</em>"; List<EntityEs.MultiLayerRelation> multiLayerQueryList = (List<EntityEs.MultiLayerRelation>) clazz.getField("multiLayerQueryList").get(t); String beanIdName = null; Field[] Fields = clazz.getDeclaredFields(); for (Field f : Fields) { f.setAccessible(true); if (f.isAnnotationPresent(org.springframework.data.annotation.Id.class)) { beanIdName = f.getName(); break; } } //构建组合查询(支持权重) getFieldQueryBuilder(boolQueryBuilder, clazz, queryMap); //处理多层bool查询 getNestQueryBuilder(boolQueryBuilder, clazz, multiLayerQueryList); log.info("打印语句:{}", boolQueryBuilder.toString()); NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder); //支持多字段排序查询 getOrderBuilder(beanIdName, orderMap, nativeSearchQueryBuilder); //支持多字段高亮查询并可自定义高亮标签规则 getHighLightBuilder(highlightFields, preTags, postTags, nativeSearchQueryBuilder); if (CollectionUtils.isEmpty(highlightFields)) { return restTemplate.queryForList(nativeSearchQueryBuilder.build(), clazz); } else { nativeSearchQueryBuilder.withPageable(PageRequest.of(0, 10000)); ScrolledPage page = restTemplate.startScroll(EntityEs.scrollIdMinExpireTime, nativeSearchQueryBuilder.build(), clazz, new SearchResultMapper() { @Override public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) { List<T> chunk = new ArrayList<>(); for (SearchHit searchHit : response.getHits()) { if (response.getHits().getHits().length <= 0) { return null; } try { T t = (T) JSON.parseObject(searchHit.getSourceAsString(), clazz); for (String fieldName : highlightFields) { Field f = clazz.getDeclaredField(fieldName); HighlightField highlightField = searchHit.getHighlightFields().get(fieldName); if (highlightField != null) { f.setAccessible(true); f.set(t, highlightField.fragments()[0].toString()); } } chunk.add(t); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } if (chunk.size() > 0) { return new AggregatedPageImpl<>((List<T>) chunk); } return null; } @Override public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) { return null; } }); return page.toList(); } } /** * 分页查询 * * @param t * @param <T> */ public <T> List<T> queryPage(T t) throws IllegalAccessException, NoSuchFieldException { BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); Class clazz = (Class) t.getClass(); Map<String, Object> queryMap = (LinkedHashMap) clazz.getField("fieldQueryMap").get(t); Map<String, String> orderMap = (LinkedHashMap) clazz.getField("orderMap").get(t); List<String> highlightFields = (List<String>) clazz.getField("highlightFields").get(t); String preTags = StringUtils.isNotBlank((String) clazz.getField("preTags").get(t)) ? (String) clazz.getField("preTags").get(t) : "<em>"; String postTags = StringUtils.isNotBlank((String) clazz.getField("postTags").get(t)) ? (String) clazz.getField("postTags").get(t) : "</em>"; Integer pageNumber = !Objects.isNull((Integer) clazz.getField("pageNumber").get(t)) ? (Integer) clazz.getField("pageNumber").get(t) : 0; Integer pageSize = !Objects.isNull((Integer) clazz.getField("pageSize").get(t)) ? (Integer) clazz.getField("pageSize").get(t) : 10; List<EntityEs.MultiLayerRelation> multiLayerQueryList = (List<EntityEs.MultiLayerRelation>) clazz.getField("multiLayerQueryList").get(t); String beanIdName = null; Field[] Fields = clazz.getDeclaredFields(); for (Field f : Fields) { f.setAccessible(true); if (f.isAnnotationPresent(org.springframework.data.annotation.Id.class)) { beanIdName = f.getName(); break; } } //构建组合查询(支持权重) getFieldQueryBuilder(boolQueryBuilder, clazz, queryMap); //处理多层bool查询 getNestQueryBuilder(boolQueryBuilder, clazz, multiLayerQueryList); NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder); //支持多字段排序查询 getOrderBuilder(beanIdName, orderMap, nativeSearchQueryBuilder); //支持多字段高亮查询并可自定义高亮标签规则 getHighLightBuilder(highlightFields, preTags, postTags, nativeSearchQueryBuilder); //分页查询 nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNumber, pageSize)); AggregatedPage page = null; if (CollectionUtils.isEmpty(highlightFields)) { page = restTemplate.queryForPage(nativeSearchQueryBuilder.build(), clazz); } else { page = restTemplate.queryForPage(nativeSearchQueryBuilder.build(), clazz, new SearchResultMapper() { @Override public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) { List<T> chunk = new ArrayList<>(); for (SearchHit searchHit : response.getHits()) { if (response.getHits().getHits().length <= 0) { return null; } try { T t = (T) JSON.parseObject(searchHit.getSourceAsString(), clazz); for (String fieldName : highlightFields) { Field f = clazz.getDeclaredField(fieldName); HighlightField highlightField = searchHit.getHighlightFields().get(fieldName); if (highlightField != null) { f.setAccessible(true); f.set(t, highlightField.fragments()[0].toString()); } } chunk.add(t); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } if (chunk.size() > 0) { return new AggregatedPageImpl<>((List<T>) chunk); } return null; } @Override public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) { return null; } }); } // 总记录数 long totalElements = page.getTotalElements(); // 总页数 int totalPages = page.getTotalPages(); // 当前页号 //int currentPage = page.getPageable().getPageNumber(); // 当前页数据集 List<T> beanList = page.toList(); List<T> content = page.getContent(); System.out.println(beanList); //TODO 根据项目中的分页封装类将以上数据设置后返回即可 return Lists.newArrayList(); } /** * 游标分页 * * @param t * @param <T> */ public <T> List<T> queryScrollPage(T t) throws IllegalAccessException, NoSuchFieldException { BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); Class clazz = (Class) t.getClass(); String scrollId = (String) clazz.getField("scrollId").get(t); if (StringUtils.isNotBlank(scrollId)) { ScrolledPage page = restTemplate.continueScroll(scrollId, EntityEs.scrollIdExpireTime, clazz); // 总记录数 long totalElements = page.getTotalElements(); // 总页数 //int totalPages = page.getTotalPages(); // 当前页号 //int currentPage = page.getPageable().getPageNumber(); // 当前页数据集 List<T> beanSet = page.toList(); List<T> content = page.getContent(); System.out.println("page.getScrollId:" + page.getScrollId()); System.out.println(beanSet); //TODO 根据项目中的分页封装类将以上数据设置后返回即可 return Lists.newArrayList(); } Map<String, Object> queryMap = (LinkedHashMap) clazz.getField("fieldQueryMap").get(t); Map<String, String> orderMap = (LinkedHashMap) clazz.getField("orderMap").get(t); List<String> highlightFields = (List<String>) clazz.getField("highlightFields").get(t); String preTags = StringUtils.isNotBlank((String) clazz.getField("preTags").get(t)) ? (String) clazz.getField("preTags").get(t) : "<em>"; String postTags = StringUtils.isNotBlank((String) clazz.getField("postTags").get(t)) ? (String) clazz.getField("postTags").get(t) : "</em>"; Integer pageNumber = !Objects.isNull((Integer) clazz.getField("pageNumber").get(t)) ? (Integer) clazz.getField("pageNumber").get(t) : 0; Integer pageSize = !Objects.isNull((Integer) clazz.getField("pageSize").get(t)) ? (Integer) clazz.getField("pageSize").get(t) : 10; List<EntityEs.MultiLayerRelation> multiLayerQueryList = (List<EntityEs.MultiLayerRelation>) clazz.getField("multiLayerQueryList").get(t); String beanIdName = null; Field[] Fields = clazz.getDeclaredFields(); for (Field f : Fields) { f.setAccessible(true); if (f.isAnnotationPresent(org.springframework.data.annotation.Id.class)) { beanIdName = f.getName(); break; } } //构建组合查询(支持权重) getFieldQueryBuilder(boolQueryBuilder, clazz, queryMap); //处理多层bool查询 getNestQueryBuilder(boolQueryBuilder, clazz, multiLayerQueryList); NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder); //支持多字段排序查询 getOrderBuilder(beanIdName, orderMap, nativeSearchQueryBuilder); //支持多字段高亮查询并可自定义高亮标签规则 getHighLightBuilder(highlightFields, preTags, postTags, nativeSearchQueryBuilder); //分页查询 nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNumber, pageSize)); ScrolledPage page = restTemplate.startScroll(EntityEs.scrollIdExpireTime, nativeSearchQueryBuilder.build(), clazz, new SearchResultMapper() { @Override public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) { List<T> chunk = new ArrayList<>(); for (SearchHit searchHit : response.getHits()) { if (response.getHits().getHits().length <= 0) { return null; } try { T t = (T) JSON.parseObject(searchHit.getSourceAsString(), clazz); for (String fieldName : highlightFields) { Field f = clazz.getDeclaredField(fieldName); HighlightField highlightField = searchHit.getHighlightFields().get(fieldName); if (highlightField != null) { f.setAccessible(true); f.set(t, highlightField.fragments()[0].toString()); } } chunk.add(t); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } if (chunk.size() > 0) { return new AggregatedPageImpl<>((List<T>) chunk); } return null; } @Override public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) { return null; } }); // 总记录数 long totalElements = page.getTotalElements(); // 总页数 //int totalPages = page.getTotalPages(); // 当前页号 //int currentPage = page.getPageable().getPageNumber(); // 当前页数据集 List<T> beanSet = page.toList(); List<T> content = page.getContent(); System.out.println("page.getScrollId:" + page.getScrollId()); System.out.println(beanSet); //TODO 根据项目中的分页封装类将以上数据设置后返回即可 return Lists.newArrayList(); } /** * 聚合查询 * * @param t * @param <T> */ public <T> Map queryForAggregation(T t) throws IllegalAccessException, NoSuchFieldException { BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); Class clazz = (Class) t.getClass(); Map<String, Object> queryMap = (LinkedHashMap) clazz.getField("fieldQueryMap").get(t); Integer pageNumber = !Objects.isNull(clazz.getField("pageNumber").get(t)) ? (Integer) clazz.getField("pageNumber").get(t) : 0; Integer pageSize = !Objects.isNull(clazz.getField("pageSize").get(t)) ? (Integer) clazz.getField("pageSize").get(t) : 1; Map<String, String> aggregationMap = (LinkedHashMap) clazz.getField("aggregationMap").get(t); List<EntityEs.MultiLayerRelation> multiLayerQueryList = (List<EntityEs.MultiLayerRelation>) clazz.getField("multiLayerQueryList").get(t); //构建组合查询(支持权重) getFieldQueryBuilder(boolQueryBuilder, clazz, queryMap); //处理多层bool查询 getNestQueryBuilder(boolQueryBuilder, clazz, multiLayerQueryList); NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder); //分页查询 nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNumber, pageSize)); // 创建聚合查询条件 String aggKey = null; String aggValue = null; for (Map.Entry<String, String> entry : aggregationMap.entrySet()) { aggKey = entry.getKey(); aggValue = entry.getValue(); if (Objects.equals(aggValue, EntityEs.COUNT)) { TermsAggregationBuilder agg = AggregationBuilders.terms(aggKey).field(aggKey); nativeSearchQueryBuilder.addAggregation(agg); break; } else if (Objects.equals(aggValue, EntityEs.SUM)) { SumAggregationBuilder agg = AggregationBuilders.sum(aggKey).field(aggKey); nativeSearchQueryBuilder.addAggregation(agg); break; } } AggregatedPage page = restTemplate.queryForPage(nativeSearchQueryBuilder.build(), clazz); // 取出聚合结果 Aggregations entitiesAggregations = page.getAggregations(); Map<String, Object> retMap = new HashMap<>(); if (Objects.equals(aggValue, EntityEs.COUNT)) { Terms terms = (Terms) entitiesAggregations.asMap().get(aggKey); // 遍历取出聚合字段列的值,与对应的数量 for (Terms.Bucket bucket : terms.getBuckets()) { // 聚合字段列的值 String keyAsString = bucket.getKeyAsString(); // 聚合字段对应的数量 long docCount = bucket.getDocCount(); log.info("keyAsString={},value={}", keyAsString, docCount); retMap.put(keyAsString, docCount); } } else if (Objects.equals(aggValue, EntityEs.SUM)) { Aggregation aggregation = entitiesAggregations.get(aggKey); retMap.put(aggregation.getName(), ((ParsedSum) aggregation).getValue()); } // 当前页数据集 List<T> beanList = page.toList(); System.out.println("当前页数据集:" + beanList); return retMap; } /** * 根据自定义查询条件批量查询 * * @param searchQuery * @param clazz * @param <T> */ public <T> List<T> queryListBySearchQuery(SearchQuery searchQuery, Class<T> clazz) { return restTemplate.queryForList(searchQuery, clazz); } /*------------------------------------------- private 私有方法 ----------------------------------------------*/ private void getNestQueryBuilder(BoolQueryBuilder boolQueryBuilder, Class clazz, List<EntityEs.MultiLayerRelation> multiLayerQueryList) throws NoSuchFieldException { if (!CollectionUtils.isEmpty(multiLayerQueryList)) { for (EntityEs.MultiLayerRelation r : multiLayerQueryList) { BoolQueryBuilder nestBoolQuery = QueryBuilders.boolQuery(); EsMapUtil nestMap = r.getMap(); getFieldQueryBuilder(nestBoolQuery, clazz, nestMap); if (Objects.equals(r.getQueryMode(), EntityEs.MUST)) { boolQueryBuilder.must(nestBoolQuery); } else if (Objects.equals(r.getQueryMode(), EntityEs.SHOULD)) { boolQueryBuilder.should(nestBoolQuery); } else if (Objects.equals(r.getQueryMode(), EntityEs.FILTER)) { boolQueryBuilder.filter(nestBoolQuery); } else if (Objects.equals(r.getQueryMode(), EntityEs.MUST_NOT)) { boolQueryBuilder.mustNot(nestBoolQuery); } //递归嵌套 if (!CollectionUtils.isEmpty(r.getMultiLayerList())) { //处理多层bool查询 getNestQueryBuilder(nestBoolQuery, clazz, r.getMultiLayerList()); } } } } /** * 构建组合查询(支持权重) * * @param boolQueryBuilder * @param clazz * @param queryMap */ private void getFieldQueryBuilder(BoolQueryBuilder boolQueryBuilder, Class clazz, Map<String, Object> queryMap) throws NoSuchFieldException { if (queryMap != null && queryMap.size() > 0) { for (Map.Entry<String, Object> entry : queryMap.entrySet()) { String k = entry.getKey(); List vList = new ArrayList(); if (entry.getValue() instanceof List) { vList = (ArrayList) entry.getValue(); } else { vList.add(entry.getValue()); } FieldType type = null; if (k.indexOf(".") == -1) { Field f = clazz.getDeclaredField(k); if (f.isAnnotationPresent(org.springframework.data.elasticsearch.annotations.Field.class)) { type = f.getAnnotation(org.springframework.data.elasticsearch.annotations.Field.class).type(); } else if (f.isAnnotationPresent(org.springframework.data.elasticsearch.annotations.MultiField.class)) { type = f.getAnnotation(org.springframework.data.elasticsearch.annotations.MultiField.class).mainField().type(); } } else { //如果字段Field type定义的是Keyword,走matchQuery效果也是term精确查询 type = FieldType.Text; } if (Objects.equals(type, FieldType.Text)) { for (Object o : vList) { if (o instanceof EntityEs.RangeRelation) { EntityEs.RangeRelation v = (EntityEs.RangeRelation) o; //范围查询 getRangeBuilder(boolQueryBuilder, k, v); } else if (o instanceof EntityEs.QueryRelation) { EntityEs.QueryRelation v = (EntityEs.QueryRelation) o; if (Objects.equals(v.getQueryMode(), EntityEs.MUST)) { if (Objects.isNull(v.getBoostValue())) { boolQueryBuilder.must(QueryBuilders.matchQuery(k, v.getFieldValue())); } else { boolQueryBuilder.must(QueryBuilders.matchQuery(k, v.getFieldValue()).boost(v.getBoostValue())); } } else if (Objects.equals(v.getQueryMode(), EntityEs.MUST_NOT)) { if (Objects.isNull(v.getBoostValue())) { boolQueryBuilder.mustNot(QueryBuilders.matchQuery(k, v.getFieldValue())); } else { boolQueryBuilder.mustNot(QueryBuilders.matchQuery(k, v.getFieldValue()).boost(v.getBoostValue())); } } else if (Objects.equals(v.getQueryMode(), EntityEs.SHOULD)) { if (Objects.isNull(v.getBoostValue())) { boolQueryBuilder.should(QueryBuilders.matchQuery(k, v.getFieldValue())); } else { boolQueryBuilder.should(QueryBuilders.matchQuery(k, v.getFieldValue()).boost(v.getBoostValue())); } } else if (Objects.equals(v.getQueryMode(), EntityEs.FILTER)) { if (Objects.isNull(v.getBoostValue())) { boolQueryBuilder.filter(QueryBuilders.matchQuery(k, v.getFieldValue())); } else { boolQueryBuilder.filter(QueryBuilders.matchQuery(k, v.getFieldValue()).boost(v.getBoostValue())); } } } } } else if (Objects.equals(type, FieldType.Keyword)) { for (Object o : vList) { if (o instanceof EntityEs.RangeRelation) { EntityEs.RangeRelation v = (EntityEs.RangeRelation) o; //范围查询 getRangeBuilder(boolQueryBuilder, k, v); } else if (o instanceof EntityEs.QueryRelation) { EntityEs.QueryRelation v = (EntityEs.QueryRelation) o; if (Objects.equals(v.getQueryMode(), EntityEs.MUST)) { if (Objects.isNull(v.getBoostValue())) { boolQueryBuilder.must(QueryBuilders.termQuery(k, v.getFieldValue())); } else { boolQueryBuilder.must(QueryBuilders.termQuery(k, v.getFieldValue()).boost(v.getBoostValue())); } } else if (Objects.equals(v.getQueryMode(), EntityEs.MUST_NOT)) { if (Objects.isNull(v.getBoostValue())) { boolQueryBuilder.mustNot(QueryBuilders.termQuery(k, v.getFieldValue())); } else { boolQueryBuilder.mustNot(QueryBuilders.termQuery(k, v.getFieldValue()).boost(v.getBoostValue())); } } else if (Objects.equals(v.getQueryMode(), EntityEs.SHOULD)) { if (Objects.isNull(v.getBoostValue())) { boolQueryBuilder.should(QueryBuilders.termQuery(k, v.getFieldValue())); } else { boolQueryBuilder.should(QueryBuilders.termQuery(k, v.getFieldValue()).boost(v.getBoostValue())); } } else if (Objects.equals(v.getQueryMode(), EntityEs.FILTER)) { if (Objects.isNull(v.getBoostValue())) { boolQueryBuilder.filter(QueryBuilders.termQuery(k, v.getFieldValue())); } else { boolQueryBuilder.filter(QueryBuilders.termQuery(k, v.getFieldValue()).boost(v.getBoostValue())); } } } } } else { for (Object o : vList) { if (o instanceof EntityEs.RangeRelation) { EntityEs.RangeRelation v = (EntityEs.RangeRelation) o; //范围查询 getRangeBuilder(boolQueryBuilder, k, v); } else if (o instanceof EntityEs.QueryRelation) { EntityEs.QueryRelation v = (EntityEs.QueryRelation) o; if (Objects.equals(v.getQueryMode(), EntityEs.MUST)) { if (Objects.isNull(v.getBoostValue())) { boolQueryBuilder.must(QueryBuilders.termQuery(k, v.getFieldValue())); } else { boolQueryBuilder.must(QueryBuilders.termQuery(k, v.getFieldValue()).boost(v.getBoostValue())); } } else if (Objects.equals(v.getQueryMode(), EntityEs.MUST_NOT)) { if (Objects.isNull(v.getBoostValue())) { boolQueryBuilder.mustNot(QueryBuilders.termQuery(k, v.getFieldValue())); } else { boolQueryBuilder.mustNot(QueryBuilders.termQuery(k, v.getFieldValue()).boost(v.getBoostValue())); } } else if (Objects.equals(v.getQueryMode(), EntityEs.SHOULD)) { if (Objects.isNull(v.getBoostValue())) { boolQueryBuilder.should(QueryBuilders.termQuery(k, v.getFieldValue())); } else { boolQueryBuilder.should(QueryBuilders.termQuery(k, v.getFieldValue()).boost(v.getBoostValue())); } } else if (Objects.equals(v.getQueryMode(), EntityEs.FILTER)) { if (Objects.isNull(v.getBoostValue())) { boolQueryBuilder.filter(QueryBuilders.termQuery(k, v.getFieldValue())); } else { boolQueryBuilder.filter(QueryBuilders.termQuery(k, v.getFieldValue()).boost(v.getBoostValue())); } } } } } } } } /** * 构建范围查询 * * @param boolQueryBuilder * @param k * @param v */ private void getRangeBuilder(BoolQueryBuilder boolQueryBuilder, String k, EntityEs.RangeRelation v) { if (Objects.equals(v.getQueryMode(), EntityEs.MUST)) { if (Objects.equals(v.getFieldMinMode(), EntityEs.GT)) { boolQueryBuilder.must(QueryBuilders.rangeQuery(k).gt(v.getFieldMinValue())); } else if (Objects.equals(v.getFieldMinMode(), EntityEs.GTE)) { boolQueryBuilder.must(QueryBuilders.rangeQuery(k).gte(v.getFieldMinValue())); } else if (Objects.equals(v.getFieldMinMode(), EntityEs.LT)) { boolQueryBuilder.must(QueryBuilders.rangeQuery(k).lt(v.getFieldMinValue())); } else if (Objects.equals(v.getFieldMinMode(), EntityEs.LTE)) { boolQueryBuilder.must(QueryBuilders.rangeQuery(k).lte(v.getFieldMinValue())); } if (Objects.equals(v.getFieldMaxMode(), EntityEs.GT)) { boolQueryBuilder.must(QueryBuilders.rangeQuery(k).gt(v.getFieldMaxValue())); } else if (Objects.equals(v.getFieldMaxMode(), EntityEs.GTE)) { boolQueryBuilder.must(QueryBuilders.rangeQuery(k).gte(v.getFieldMaxValue())); } else if (Objects.equals(v.getFieldMaxMode(), EntityEs.LT)) { boolQueryBuilder.must(QueryBuilders.rangeQuery(k).lt(v.getFieldMaxValue())); } else if (Objects.equals(v.getFieldMaxMode(), EntityEs.LTE)) { boolQueryBuilder.must(QueryBuilders.rangeQuery(k).lte(v.getFieldMaxValue())); } } else if (Objects.equals(v.getQueryMode(), EntityEs.MUST_NOT)) { if (Objects.equals(v.getFieldMinMode(), EntityEs.GT)) { boolQueryBuilder.mustNot(QueryBuilders.rangeQuery(k).gt(v.getFieldMinValue())); } else if (Objects.equals(v.getFieldMinMode(), EntityEs.GTE)) { boolQueryBuilder.mustNot(QueryBuilders.rangeQuery(k).gte(v.getFieldMinValue())); } else if (Objects.equals(v.getFieldMinMode(), EntityEs.LT)) { boolQueryBuilder.mustNot(QueryBuilders.rangeQuery(k).lt(v.getFieldMinValue())); } else if (Objects.equals(v.getFieldMinMode(), EntityEs.LTE)) { boolQueryBuilder.mustNot(QueryBuilders.rangeQuery(k).lte(v.getFieldMinValue())); } if (Objects.equals(v.getFieldMaxMode(), EntityEs.GT)) { boolQueryBuilder.mustNot(QueryBuilders.rangeQuery(k).gt(v.getFieldMaxValue())); } else if (Objects.equals(v.getFieldMaxMode(), EntityEs.GTE)) { boolQueryBuilder.mustNot(QueryBuilders.rangeQuery(k).gte(v.getFieldMaxValue())); } else if (Objects.equals(v.getFieldMaxMode(), EntityEs.LT)) { boolQueryBuilder.mustNot(QueryBuilders.rangeQuery(k).lt(v.getFieldMaxValue())); } else if (Objects.equals(v.getFieldMaxMode(), EntityEs.LTE)) { boolQueryBuilder.mustNot(QueryBuilders.rangeQuery(k).lte(v.getFieldMaxValue())); } } else if (Objects.equals(v.getQueryMode(), EntityEs.SHOULD)) { if (Objects.equals(v.getFieldMinMode(), EntityEs.GT)) { boolQueryBuilder.should(QueryBuilders.rangeQuery(k).gt(v.getFieldMinValue())); } else if (Objects.equals(v.getFieldMinMode(), EntityEs.GTE)) { boolQueryBuilder.should(QueryBuilders.rangeQuery(k).gte(v.getFieldMinValue())); } else if (Objects.equals(v.getFieldMinMode(), EntityEs.LT)) { boolQueryBuilder.should(QueryBuilders.rangeQuery(k).lt(v.getFieldMinValue())); } else if (Objects.equals(v.getFieldMinMode(), EntityEs.LTE)) { boolQueryBuilder.should(QueryBuilders.rangeQuery(k).lte(v.getFieldMinValue())); } if (Objects.equals(v.getFieldMaxMode(), EntityEs.GT)) { boolQueryBuilder.should(QueryBuilders.rangeQuery(k).gt(v.getFieldMaxValue())); } else if (Objects.equals(v.getFieldMaxMode(), EntityEs.GTE)) { boolQueryBuilder.should(QueryBuilders.rangeQuery(k).gte(v.getFieldMaxValue())); } else if (Objects.equals(v.getFieldMaxMode(), EntityEs.LT)) { boolQueryBuilder.should(QueryBuilders.rangeQuery(k).lt(v.getFieldMaxValue())); } else if (Objects.equals(v.getFieldMaxMode(), EntityEs.LTE)) { boolQueryBuilder.should(QueryBuilders.rangeQuery(k).lte(v.getFieldMaxValue())); } } else if (Objects.equals(v.getQueryMode(), EntityEs.FILTER)) { if (Objects.equals(v.getFieldMinMode(), EntityEs.GT)) { boolQueryBuilder.filter(QueryBuilders.rangeQuery(k).gt(v.getFieldMinValue())); } else if (Objects.equals(v.getFieldMinMode(), EntityEs.GTE)) { boolQueryBuilder.filter(QueryBuilders.rangeQuery(k).gte(v.getFieldMinValue())); } else if (Objects.equals(v.getFieldMinMode(), EntityEs.LT)) { boolQueryBuilder.filter(QueryBuilders.rangeQuery(k).lt(v.getFieldMinValue())); } else if (Objects.equals(v.getFieldMinMode(), EntityEs.LTE)) { boolQueryBuilder.filter(QueryBuilders.rangeQuery(k).lte(v.getFieldMinValue())); } if (Objects.equals(v.getFieldMaxMode(), EntityEs.GT)) { boolQueryBuilder.filter(QueryBuilders.rangeQuery(k).gt(v.getFieldMaxValue())); } else if (Objects.equals(v.getFieldMaxMode(), EntityEs.GTE)) { boolQueryBuilder.filter(QueryBuilders.rangeQuery(k).gte(v.getFieldMaxValue())); } else if (Objects.equals(v.getFieldMaxMode(), EntityEs.LT)) { boolQueryBuilder.filter(QueryBuilders.rangeQuery(k).lt(v.getFieldMaxValue())); } else if (Objects.equals(v.getFieldMaxMode(), EntityEs.LTE)) { boolQueryBuilder.filter(QueryBuilders.rangeQuery(k).lte(v.getFieldMaxValue())); } } } /** * 构建排序查询 * * @param beanIdName * @param orderMap * @param nativeSearchQueryBuilder */ private void getOrderBuilder(String beanIdName, Map orderMap, NativeSearchQueryBuilder nativeSearchQueryBuilder) { if (orderMap != null && orderMap.size() > 0) { orderMap.forEach((k, v) -> { nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort((String) k).order((SortOrder) v)); }); } else { //无指定排序字段默认按ID倒序 //nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(beanIdName).order(SortOrder.DESC)); } } /** * 构建高亮查询 * * @param highlightFields * @param preTags * @param postTags * @param nativeSearchQueryBuilder */ private void getHighLightBuilder(List<String> highlightFields, String preTags, String postTags, NativeSearchQueryBuilder nativeSearchQueryBuilder) { if (highlightFields != null && highlightFields.size() > 0) { List<HighlightBuilder.Field> highlightTitles = new ArrayList<>(); for (String title : highlightFields) { highlightTitles.add(new HighlightBuilder.Field(title).preTags(preTags).postTags(postTags)); } nativeSearchQueryBuilder.withHighlightFields(highlightTitles.stream().toArray(HighlightBuilder.Field[]::new)); } } }
五、单元测试
1、根据映射实体设置生成索引与mapping
说明:启动项目会自动加载自动创建,如需要手动创建可调用方法
@Test public void indexCreateTest() { System.out.println(esService.indexCreate(EmployeeEs.class)); }
2、删除指定索引
@Test public void indexDelete() { System.out.println(esService.indexDelete("employee_index")); }
3、判断指定索引是否存在
@Test public void indexExists() { System.out.println(esService.indexExists(EmployeeEs.class)); }
4、保存单个bean
@Autowired private EsService esService; @Test public void saveBean() { System.out.println("-----es测试start..."); EmployeeEs employee = EmployeeEs.builder().userId(9L).userName("张三1").userCode("abc").userAge(22).userMobile("12345678987") .remarks("今天天气好晴朗~").birthDay(new Date()).build(); esService.saveBean(employee); System.out.println("-----es测试end..."); }
5、批量保存
@Test public void saveList() throws ParseException { System.out.println("-----es测试start..."); EmployeeEs employee = EmployeeEs.builder().userId(1L).userName("张三").userCode("abc").userAge(18).userSex("男").userMobile("12345678987").birthDay(new SimpleDateFormat("yyyy-MM-dd").parse("1995-9-20")).build(); EmployeeEs employee1 = EmployeeEs.builder().userId(2L).userName("李四").userCode("abc").userAge(10).userSex("女").userMobile("12345678987").birthDay(new SimpleDateFormat("yyyy-MM-dd").parse("1995-6-20")).build(); EmployeeEs employee2 = EmployeeEs.builder().userId(3L).userName("王五").userCode("abc").userAge(10).userSex("男").userMobile("12345678987").birthDay(new SimpleDateFormat("yyyy-MM-dd").parse("1995-5-20")).build(); EmployeeEs employee3 = EmployeeEs.builder().userId(4L).userName("赵六").userCode("abc").userAge(20).userSex("男").userMobile("12345678987").birthDay(new SimpleDateFormat("yyyy-MM-dd").parse("1995-8-20")).build(); EmployeeEs employee4 = EmployeeEs.builder().userId(5L).userName("董七").userCode("abc").userAge(20).userSex("女").userMobile("12345678987").birthDay(new SimpleDateFormat("yyyy-MM-dd").parse("1995-8-20")).build(); esService.saveList(Lists.newArrayList(employee,employee1,employee2,employee3,employee4)); System.out.println("-----es测试end..."); }
6、根据ID删除指定Bean
@Test public void deleteByBean() { EmployeeEs employee = EmployeeEs.builder().userId(1L).build(); esService.deleteByBean(employee); }
7、批量删除
@Test public void deleteList() { EmployeeEs employee = EmployeeEs.builder().userId(1L).build(); EmployeeEs employee1 = EmployeeEs.builder().userId(2L).build(); esService.deleteAllByBeanList(Lists.newArrayList(employee,employee1)); }
8、删除该索引下所有数据
@Test public void deleteAll() { esService.deleteAll(EmployeeEs.class); }
9、修改数据(ID不能为空)
@Test public void updateTest() throws IllegalAccessException { System.out.println("-----es测试start..."); EmployeeEs employee = EmployeeEs.builder().userId(5L).userName("张一").userCode("abcD").userAge(19).build(); esService.updateByBean(employee); System.out.println("-----es测试end..."); }
10、根据ID查询指定Bean
@Test public void queryById() { EmployeeEs employeeEs = esService.queryBeanById("2", EmployeeEs.class); System.out.println(employeeEs); }
11、批量查询
/** * 涉及到了组合查询bool、权重、范围查找、排序 */ @Test public void queryList() throws IllegalAccessException, NoSuchFieldException { System.out.println("-----es测试start..."); EmployeeEs employee = new EmployeeEs(); List queryList = Stream.of(employee.new QueryRelation<String>("张三", EntityEs.SHOULD, 5F), employee.new QueryRelation<String>("李四", EntityEs.SHOULD, 20F)).collect(Collectors.toList()); employee.setFieldQueryMap(new EsMapUtil().put(EmployeeEs::getUserName, queryList).put(EmployeeEs::getUserAge, employee.new RangeRelation(20, EntityEs.GTE, null, null, EntityEs.MUST))); //排序查询 employee.setOrderMap(new EsMapUtil().put(EmployeeEs::getUserId, SortOrder.DESC)); List<EmployeeEs> employeeEs = esService.queryList(employee); System.out.println(employeeEs); System.out.println("-----es测试end..."); }
打印出来的语句:
查询结果:
12、二级属性查询
@Test public void querySecondList() throws IllegalAccessException, NoSuchFieldException { System.out.println("-----es测试start..."); EmployeeEs employee = new EmployeeEs(); employee.setFieldQueryMap(new EsMapUtil().putStr("userName.trueName", employee.new QueryRelation<String>("张三", EntityEs.MUST))); List<EmployeeEs> employeeEsList = esService.queryList(employee); System.out.println(employeeEsList); System.out.println("-----es测试end..."); }
先准备一条测试数据插入
看下上面查询语句的结果:
由于userName的二级属性trueName的类型是keyword,所以是term精确查找
对比:
@Test public void querySecondList() throws IllegalAccessException, NoSuchFieldException { System.out.println("-----es测试start..."); EmployeeEs employee = new EmployeeEs(); employee.setFieldQueryMap(new EsMapUtil().put(EmployeeEs::getUserName, employee.new QueryRelation<String>("张三", EntityEs.MUST))); //employee.setFieldQueryMap(new EsMapUtil().putStr("userName.trueName", employee.new QueryRelation<String>("张三", EntityEs.MUST))); List<EmployeeEs> employeeEsList = esService.queryList(employee); System.out.println(employeeEsList); System.out.println("-----es测试end..."); }
13、分页查询
@Test public void queryPage() throws IllegalAccessException, NoSuchFieldException { System.out.println("-----es测试start..."); EmployeeEs employee = new EmployeeEs(); List<EntityEs.QueryRelation> queryList = Stream.of(employee.new QueryRelation<String>("张三", EntityEs.SHOULD), employee.new QueryRelation<String>("李四", EntityEs.SHOULD)).collect(Collectors.toList()); employee.setFieldQueryMap(new EsMapUtil().put(EmployeeEs::getUserName, queryList)); //分页 employee.setPageNumber(0); employee.setPageSize(2); List<EmployeeEs> employeeEs = esService.queryPage(employee); System.out.println(employeeEs); System.out.println("-----es测试end..."); }
14、游标分页查询
@Test public void queryScrollPage() throws IllegalAccessException, NoSuchFieldException { System.out.println("-----es测试start..."); String scrollId = "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAADJEWMVFWMVBnb1ZSZDZsV1k2Y2JjLVlldw=="; EmployeeEs employee = new EmployeeEs(); if (StringUtils.isNotBlank(scrollId)) { //如果前端有传scrollId employee.setScrollId(scrollId); List<EmployeeEs> employeeEs = esService.queryScrollPage(employee); System.out.println(employeeEs); } else { List<EntityEs.QueryRelation> queryList = Stream.of(employee.new QueryRelation<String>("张三", EntityEs.SHOULD, 20F), employee.new QueryRelation<String>("李四", EntityEs.SHOULD)).collect(Collectors.toList()); employee.setFieldQueryMap(new EsMapUtil().put(EmployeeEs::getUserName, queryList)); //每页页数 employee.setPageSize(4); List<EmployeeEs> employeeEs = esService.queryScrollPage(employee); System.out.println(employeeEs); } System.out.println("-----es测试end..."); }
15、多层bool套bool查询
@Test public void queryMuiltiLayer() throws IllegalAccessException, NoSuchFieldException { System.out.println("-----es测试start..."); EmployeeEs employee = new EmployeeEs(); //多层bool查询 employee.setFieldQueryMap(new EsMapUtil().put(EmployeeEs::getUserSex, employee.new QueryRelation("男", EntityEs.MUST))); List<EntityEs.QueryRelation> multiLayerUserAgeList = Stream.of(employee.new QueryRelation(10, EntityEs.SHOULD), employee.new QueryRelation(18, EntityEs.SHOULD)).collect(Collectors.toList()); EntityEs.MultiLayerRelation multiLayerRelation = employee.new MultiLayerRelation(EntityEs.MUST, new EsMapUtil().put(EmployeeEs::getUserAge, multiLayerUserAgeList)); employee.setMultiLayerQueryList(Lists.newArrayList(multiLayerRelation)); List<EmployeeEs> userEs = esService.queryList(employee); System.out.println(userEs); System.out.println("-----es测试end..."); }
示例:查找年龄为10或者18岁的男性员工
//错误案例 EmployeeEs employee = new EmployeeEs(); List<EntityEs.QueryRelation> ageList = Lists.newArrayList(employee.new QueryRelation(10, EntityEs.SHOULD), employee.new QueryRelation(18, EntityEs.SHOULD)); employee.setFieldQueryMap(new EsMapUtil().put(EmployeeEs::getUserSex, employee.new QueryRelation("男", EntityEs.MUST)) .put(EmployeeEs::getUserAge,ageList) ); List<EmployeeEs> userEs = esService.queryList(employee); System.out.println(userEs);
结果年龄为20的赵六也被查询了出来。原因是:当should遇到must和filter时就不是或者了,而是应该的意思。可通过must嵌套一层解决。
修改为多层bool查询
EmployeeEs employee = new EmployeeEs(); //修改为多层bool查询 employee.setFieldQueryMap(new EsMapUtil().put(EmployeeEs::getUserSex, employee.new QueryRelation("男", EntityEs.MUST))); List<EntityEs.QueryRelation> multiLayerUserAgeList = Stream.of(employee.new QueryRelation(10, EntityEs.SHOULD), employee.new QueryRelation(18, EntityEs.SHOULD)).collect(Collectors.toList()); EntityEs.MultiLayerRelation multiLayerRelation = employee.new MultiLayerRelation(EntityEs.MUST, new EsMapUtil().put(EmployeeEs::getUserAge, multiLayerUserAgeList)); employee.setMultiLayerQueryList(Lists.newArrayList(multiLayerRelation)); List<EmployeeEs> userEs = esService.queryList(employee);
16、高亮查询
@Test public void highlightTest() throws NoSuchFieldException, IllegalAccessException { System.out.println("-----es测试start..."); EmployeeEs employee = new EmployeeEs(); employee.setFieldQueryMap(new EsMapUtil().put(EmployeeEs::getUserName, employee.new QueryRelation<String>("张三", EntityEs.MUST)) .put(EmployeeEs::getRemarks, employee.new QueryRelation<String>("天气", EntityEs.MUST))); employee.setHighlightFields(Lists.newArrayList("remarks")); //默认<em></em>,可自定义高亮标签 employee.setPreTags("<h1>"); employee.setPostTags("</h1>"); List<EmployeeEs> employeeEs = esService.queryList(employee); System.out.println(employeeEs); System.out.println("-----es测试end..."); }
17、聚合查询
@Test public void queryForAggregation() throws NoSuchFieldException, IllegalAccessException { System.out.println("-----queryForAggregation-es测试start..."); EmployeeEs employee = new EmployeeEs(); employee.setAggregationMap(new EsMapUtil().put(EmployeeEs::getUserAge, EntityEs.COUNT)); Map employeeEsAggMap = esService.queryForAggregation(employee); System.out.println("返回结果:" + employeeEsAggMap); System.out.println("-----queryForAggregation-es测试end..."); }
将上面的EntityEs.COUNT改为EntityEs.SUM 求和运行结果:
文章来源: 博客园
原文链接: https://www.cnblogs.com/better-farther-world2099/p/15992842.html
- 还没有人评论,欢迎说说您的想法!