1. 程式人生 > >SpringBoot 整合 Elasticsearch深度分頁查詢

SpringBoot 整合 Elasticsearch深度分頁查詢

es 查詢共有4種查詢型別

QUERY_AND_FETCH: 

   主節點將查詢請求分發到所有的分片中,各個分片按照自己的查詢規則即詞頻文件頻率進行打分排序,然後將結果返回給主節點,主節點對所有資料進行彙總排序然後再返回給客戶端,此種方式只需要和es互動一次。

      這種查詢方式存在資料量和排序問題,主節點會彙總所有分片返回的資料這樣資料量會比較大,二是各個分片上的規則可能不一致。

QUERY_THEN_FETCH: 

主節點將請求分發給所有分片,各個分片打分排序後將資料的id和分值返回給主節點,主節點收到後進行彙總排序再根據排序後的id到對應的節點讀取對應的資料再返回給客戶端,此種方式需要和es互動兩次。

      這種方式解決了資料量問題但是排序問題依然存在而且是es的預設查詢方式

DEF_QUERY_AND_FETCH 和 DFS_QUERY_THEN_FETCH: 

  將各個分片的規則統一起來進行打分。解決了排序問題但是DFS_QUERY_AND_FETCH仍然存在資料量問題,DFS_QUERY_THEN_FETCH兩種噢乖你問題都解決但是效率是最差的。

Maven依賴:

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>
spring-boot-starter-data-elasticsearch</artifactId>    <version>2.0.5.RELEASE</version>  </dependency>

yml配置:

spring:
    data:
        elasticsearch:
             cluster-name: elasticsearch
             cluster-nodes: 127.0.0.1:9300

測試程式碼:

  1 /**
  2  * @author 宮新程
  3  * 
@since 2018/10/24 12:29 4 */ 5 @RunWith(SpringRunner.class) 6 @SpringBootTest 7 @Slf4j 8 public class GoodsItemLaunchEsTest { 9 10 // 查詢條件 11 private static final String ES_SEARCH_ITEM_NAME = "itemName"; 12 private static final String ES_SEARCH_ITEM_MODEL = "itemModel"; 13 private static final String ES_SEARCH_PRODUCT_CODE = "productCode"; 14 private static final String ES_SEARCH_OPER_FLAG = "oper_flag"; 15 16 @Resource private ElasticsearchTemplate elasticsearchTemplate; 17 @Resource private EsManager esManager; 18 19 /** ES建立基礎表 */ 20 @Test 21 public void createIndex() { 22 elasticsearchTemplate.createIndex(EsGoodsItemLaunchDto.class); 23 elasticsearchTemplate.putMapping(EsGoodsItemLaunchDto.class); 24 } 25 26 /** ES刪除表 */ 27 @Test 28 public void deleteIndex() { 29 this.elasticsearchTemplate.deleteIndex(EsGoodsItemLaunchDto.class); 30 } 31 32 /** 插入測試資料 */ 33 @Test 34 public void insertData() { 35 36 List<IndexQuery> queryList = new ArrayList<>(); 37 38 for (int i = 0; i < 10000; i++) { 39 EsGoodsItemLaunchDto dto = new EsGoodsItemLaunchDto(); 40 dto.setId(i); 41 dto.setItemId(i); 42 dto.setItemSkuId(i); 43 dto.setItemName(i % 2 == 0 ? "洗衣機" + i : "空調" + i); 44 dto.setCustomerSellerCode("CustomerSellerCode" + i); 45 dto.setCustomerName("CustomerName" + i); 46 dto.setMemberSellerCode("MemberSellerCode" + i); 47 dto.setMemberName("MemberName" + i); 48 dto.setProductCode("ProductCode" + i); 49 dto.setItemModel("ItemModel" + i); 50 dto.setProductGroupName("ProductGroupName" + i); 51 dto.setProductGroupCode("ProductGroupCode" + i); 52 dto.setBrandId(i * 2); 53 dto.setBrandName("BrandName" + i); 54 dto.setGmCode(((int) Math.random() * 10000) + ""); 55 dto.setUpdateTime(new Date()); 56 dto.setMemberId(i * 5); 57 dto.setCustomerId(i * 6); 58 59 IndexQuery indexQuery = 60 new IndexQueryBuilder() 61 .withId(String.valueOf(dto.getId())) 62 .withObject(dto) 63 .withIndexName(esManager.index4r(EsGoodsItemLaunchDto.class)) 64 .build(); 65 66 queryList.add(indexQuery); 67 68 if (queryList.size() == 1000) { 69 this.elasticsearchTemplate.bulkIndex(queryList); 70 queryList.clear(); 71 } 72 } 73 74 // 必須加if判斷否則報異常: 75 // org.elasticsearch.action.ActionRequestValidationException: 76 // Validation Failed: 1:no requests added; 77 if (queryList.size() > 0) { 78 // 儲存剩餘資料 (沒被1000整除) 79 elasticsearchTemplate.bulkIndex(queryList); 80 } 81 } 82 83 @Test 84 public void search() { 85 int pageNum = 2; 86 int pageSize = 5; 87 88 BoolQueryBuilder filter = QueryBuilders.boolQuery(); 89 // 注意一定要小寫處理 toLowerCase() 90 String codeOrName = "ProductCode100".toLowerCase(); 91 BoolQueryBuilder boolQueryLike = QueryBuilders.boolQuery(); 92 // 分詞查詢 商品名稱 93 MultiMatchQueryBuilder queryBuilder = 94 QueryBuilders.multiMatchQuery(codeOrName, ES_SEARCH_ITEM_NAME); 95 // 商品型號 96 QueryBuilder itemModel = 97 QueryBuilders.wildcardQuery(ES_SEARCH_ITEM_MODEL, "*" + codeOrName + "*"); 98 // 產品編碼 99 QueryBuilder productCode = 100 QueryBuilders.wildcardQuery(ES_SEARCH_PRODUCT_CODE, "*" + codeOrName + "*"); 101 boolQueryLike.should(queryBuilder); 102 boolQueryLike.should(itemModel); 103 boolQueryLike.should(productCode); 104 filter.must(boolQueryLike); 105 106 // 判斷ES表的 oper_flag 不等於 D 107 filter.mustNot(QueryBuilders.termQuery(ES_SEARCH_OPER_FLAG, "D")); 108 109 SearchQuery searchQuery = new NativeSearchQuery(filter); 110 searchQuery.addIndices(esManager.index4r(EsGoodsItemLaunchDto.class)); 111 Pageable pageable = PageRequest.of(pageNum - 1, pageSize); 112 searchQuery.setPageable(pageable); 113 114 // 深度查詢分頁 115 Page<EsGoodsItemLaunchDto> result = 116 this.elasticsearchTemplate.startScroll(5000, searchQuery, EsGoodsItemLaunchDto.class); 117 118 for (int i = 0; i < pageNum - 1; i++) { 119 elasticsearchTemplate.continueScroll( 120 ((ScrolledPage) result).getScrollId(), 5000, EsGoodsItemLaunchDto.class); 121 } 122 123 log.info("====================================="); 124 result 125 .getContent() 126 .forEach( 127 (dto) -> { 128 log.info("ItemName:{}", dto.getItemName()); 129 }); 130 log.info("總記錄數:{}", result.getTotalElements()); 131 log.info("當前頁碼數:{}", pageNum); 132 log.info("每頁顯示條數:{}", pageSize); 133 log.info("====================================="); 134 } 135 } 136 /* 輸出結果: 137 <============================> 138 <ItemName:空調1001> 139 <ItemName:洗衣機1004> 140 <ItemName:洗衣機1006> 141 <ItemName:空調1007> 142 <ItemName:洗衣機1008> 143 <總記錄數:11> 144 <當前頁碼數:2> 145 <每頁顯示條數:5> 146 <============================> 147 */