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 */