這幾天寫了一個關於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 求和執行結果: