数据准备
ElasticSearch 实现分词全文检索 - 测试数据准备
深分页 Scroll
ES 对 from + size 有限制,两者之和不能超过1W
from + size 在 ES 查询数据的方式:
- 第一步:将用户指定的关键词进行分词
- 第二步:将词汇去词库中进行检索,得到多个文档id
- 第三步:去各个分片中拉取指定的数据【耗时较长】
- 第四步:根据score(匹配度)将数据进行排序,【耗时较长】
- 第五步:根据 from 的值,将查询到的数据舍弃一部分
- 第六步:返回结果
Scroll + size 在 ES 查询数据的方式:
- 第一步:将用户指定的关键词进行分词
- 第二步:将词汇去分词库中进行检索,得到多个文档的id
- 第三步:将文档的id存放在一个ES的上下文中(设定保存时间,过期后移除)
- 第四步:根据你指定的Size的个数去ES中检索指定个数的数据,拿到数据的文档id,会从上下文中移除
- 第五步:如果需要下一页数据,直接去ES的上下文中找后续的内容
- 第六步:循环第四步和第五步
Scroll查询方式,不适合做实时的查询,类似数据库中的游标,每次都是从数据文档中的ID去获取,效果高了,但文档中的ID(第二步)不是实时更新的,一般后台管理的方式用 Scroll 比较方便
Scroll不适合支持那种实时的和用户交互的前端分页工作,其主要用途用于从ES集群分批拉取大量结果集的情况,一般都是offline的应用场景。 比如需要将非常大的结果集拉取出来,存放到其他系统处理,或者需要做大索引的reindex等等。
# scroll 查询,返回第一页数据,并且将文档id信息存放在ES上下文中,指定生存时间 1m POST /sms-logs-index/_search?scroll=1m { "query": { "match_all": {} }, "size": 2, "sort": [ { "fee": { # 指定排序 "order": "desc" } } ] } } # 根据scroll查询下一页数据,【第一步设置了1分钟,所以1分钟以后再执行就没有数据了】 POST /_search/scroll { "scroll_id":"FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFEQ1VkNuSVlCR2xMYVQ1OExzNU1tAAAAAAADNlcWMEt3d2xrY3hRWGFoZFlwM01ZdnlCdw==", #根据上一步查的结果提到scroll_id "scroll":"1m" #生存时间 } # 删除scroll在ES上下文中的数据 DELETE /_search/scroll/FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFEQ1VkNuSVlCR2xMYVQ1OExzNU1tAAAAAAADNlcWMEt3d2xrY3hRWGFoZFlwM01ZdnlCdw==
Java
@Test void scrollQuery() throws Exception { String indexName = "sms-logs-index"; RestHighLevelClient client = ESClient.getClient(); //1. 创建SearchRequest对象 SearchRequest request = new SearchRequest(indexName); //2. 指定scroll信息 request.scroll(TimeValue.timeValueMinutes(2L)); //1分钟过期 //2. 指定查询条件 SearchSourceBuilder builder = new SearchSourceBuilder(); builder.size(4); builder.sort("fee", SortOrder.DESC); builder.query(QueryBuilders.matchAllQuery()); request.source(builder); //4. 获取返回结果 scrollid,source SearchResponse resp = client.search(request, RequestOptions.DEFAULT); String scrollId = resp.getScrollId(); System.out.println("-------首页----------"); for (SearchHit hit : resp.getHits().getHits()) { System.out.println(hit.getSourceAsMap()); } while (true) { System.out.println("ScrollId =>" + scrollId); //5. 循环 - 创建SearchScrollRequest SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId); //6. 指定 ScrollId scrollRequest.scroll(TimeValue.timeValueMinutes(1L)); //7. 执行查询获取返回结果 SearchResponse scrollResp = client.scroll(scrollRequest, RequestOptions.DEFAULT); //8. 判断是否查询到了数据,输出 SearchHit[] hits = scrollResp.getHits().getHits(); if (hits != null && hits.length > 0) { System.out.println("----------下一页---------"); for (SearchHit hit : hits) { System.out.println(hit.getSourceAsMap()); } } else { //9. 判断没有查询到数据 - 退出循环 System.out.println("----------下一页---------"); break; } } //10. 创建 ClearScrollRequest ClearScrollRequest clearScrollRequest = new ClearScrollRequest(); //11. 指定 ScrollId clearScrollRequest.addScrollId(scrollId); //12. 删除 ScrollId ClearScrollResponse clearScrollResponse = client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT); //13. 输出结果 System.out.println("删除scroll: " + clearScrollResponse.isSucceeded()); }