ElasticSearch深度分页 Scroll + SearchAfter查询

深度分页

引出: SQL语句 分页查询limit 10000, 10 会查出10010条数据,然后去掉前10000条, 解决:可以使用流式查询(fetch size) 或按照id升序,每次id大于上一次查询结果的最大值。

同样,es中存在类似的问题 即深度分页 解决: scroll滚动查询,或者使用Search-After查询

DSL语句:

POST http://xxx:9200/enterprise_wechat_test.alias/_search?scroll=1m

{

"query": { "match_all": {}},

"size": 10

}

参数 scroll,表示暂存搜索结果的时间

返回一个 _scroll_id,_scroll_id 用来下次取数据用

1m表示scroll的context会保持一分钟,在这一分钟内都可以通过scrollid继续读取命中的文档。elasticsearch的scroll是流式读取。

POST http://xxx:9200/_search/scroll=1m

{

"scroll": "DXF1ZXJ5QW5kRmV0Y2gBAAAAABflI0EWdWZjRzZsdmVSZjZ4V2hDbV95QmVsUQ=="

}

这里的 scroll_id 即 上一次遍历取回的 _scroll_id 或者是初始化返回的 _scroll_id,同样的,需要带 scroll 参数。

注意,每次都要传参数 scroll,刷新搜索结果的缓存时间。另外,不需要指定 index 和 type。

Scroll查询java代码:

public List<MsgInfoVo> queryScroll(MsgQueryInfo queryParam) throws Exception {
        // 创建search请求
        SearchRequest searchRequest = new SearchRequest(indexName).types(typeName);
        // 构建检索条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQuery = new BoolQueryBuilder();
        addTermFilter(boolQuery, "city", queryParam.getCity());
        sourceBuilder.query(boolQuery);

        // 排序
        SortBuilder id4Sort = SortBuilders.fieldSort("id4Sort").order(SortOrder.ASC);
        sourceBuilder.sort(id4Sort);

        sourceBuilder.size(3000);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

        searchRequest.source(sourceBuilder);
        searchRequest.scroll(TimeValue.timeValueMinutes(1L));

        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("highLevelClient IOException {}." , e);
        }

        // 返回结果集
        List<MsgInfoVo> list = new ArrayList<>();

        // 若数据量不足3000则执行一次; 若大于3000则每3000条查询一次并且返回
        do{
            SearchHits hits = searchResponse.getHits();
            for (SearchHit hit : hits) {
                MsgInfoVo miv = new MsgInfoVo();
                Map<String, Object> map = hit.getSourceAsMap();
                String msgId = formatString(map.get("id4Sort"));
                miv.setMsgId(msgId);
                // System.out.println("miv = " + miv);
                list.add(miv);
            }

            //获取scroll_id并再次查询
            SearchScrollRequest scrollRequest = new SearchScrollRequest(searchResponse.getScrollId());
            scrollRequest.scroll(TimeValue.timeValueSeconds(3000));
            searchResponse = client.searchScroll(scrollRequest);

        } while (searchResponse.getHits().getHits().length != 0);

        return list;
    }

SearchAfter查询Java代码:

public MsgInfoHitsVo querySearchAfter(MsgQueryInfo queryParam) throws Exception {
        // 创建search请求
        SearchRequest searchRequest = new SearchRequest(indexName).types(typeName);
        // 构建检索条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQuery = new BoolQueryBuilder();
        addTermFilter(boolQuery, "city", queryParam.getCity());
        // 消息id的检索 - 使用id4Sort; 不使用msgId
        addTermFilter(boolQuery, "id4Sort", queryParam.getMsgId());
        sourceBuilder.query(boolQuery);

        // 排序
        SortBuilder id4Sort = SortBuilders.fieldSort("id4Sort").order(SortOrder.ASC);
        sourceBuilder.sort(id4Sort);

        int page = queryParam.getPage();
        int pageSize = queryParam.getPageSize();

        int start = 0;
        if ((page - 1) > 0) {
            start = (page - 1) * pageSize;
        }
        sourceBuilder.from(0);
        sourceBuilder.size(pageSize);
        sourceBuilder.searchAfter(new Object[]{"18059"});
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        if (queryParam.isDebug()){
            log.info("sourceBuilder: " + sourceBuilder);
        }
        searchRequest.source(sourceBuilder);

        SearchResponse searchResponse = null;
        try {
            searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("highLevelClient IOException {}." , e);
        }
        if (null == searchResponse){
            return null;
        }
        SearchHits hits = searchResponse.getHits();
        SearchHit[] lastResult = searchResponse.getHits().getHits();
        Object[] sortValues1 = lastResult[lastResult.length - 1].getSortValues();
        System.out.println("最后一条数据sort_id为:"+ Arrays.toString(sortValues1));

        long totalHits = hits.getTotalHits();
        SearchHit[] searchHits = hits.getHits();

        for (SearchHit hit : searchHits) {
            MsgInfoVo miv = new MsgInfoVo();
            Map<String, Object> map = hit.getSourceAsMap();
            miv.setMsgId(formatString(map.get("id4Sort")));
            System.out.println(miv);
        }
        return null;
    }
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Elasticsearch 中,深度分页功能的使用需要考虑到性能问题。一般来说,建议不要使用过深的分页,以避免对 Elasticsearch 的性能造成负面影响。 以下是在 Elasticsearch 中使用深度分页的方法: 1. 使用 scroll API 进行深度分页查询 scroll API 可以在内存中存储搜索上下文,而不是在每个请求之间重新计算。这使得在大数据集上进行深度分页查询变得更加有效。 示例代码: ``` SearchRequest searchRequest = new SearchRequest("indexName"); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.matchAllQuery()); searchSourceBuilder.size(100); searchRequest.source(searchSourceBuilder); SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); String scrollId = searchResponse.getScrollId(); SearchHit[] searchHits = searchResponse.getHits().getHits(); while (searchHits != null && searchHits.length > 0) { SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId); scrollRequest.scroll(TimeValue.timeValueMinutes(1L)); SearchResponse scrollResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT); scrollId = scrollResponse.getScrollId(); searchHits = scrollResponse.getHits().getHits(); // Do something with searchHits } ClearScrollRequest clearScrollRequest = new ClearScrollRequest(); clearScrollRequest.addScrollId(scrollId); client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT); ``` 在上面的示例中,size 参数设置为 100,表示每次检索返回 100 个结果。scroll API 的 scroll 参数设置为 1 分钟,表示在这段时间内保持搜索上下文。 2. 使用 search_after 参数进行深度分页查询 search_after 参数可以用来指定上一次搜索的最后一个结果,以便从下一个结果开始进行分页查询。 示例代码: ``` SearchRequest searchRequest = new SearchRequest("indexName"); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.matchAllQuery()); searchSourceBuilder.size(100); searchRequest.source(searchSourceBuilder); SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); SearchHit[] searchHits = searchResponse.getHits().getHits(); while (searchHits != null && searchHits.length > 0) { SearchHit lastHit = searchHits[searchHits.length - 1]; searchSourceBuilder.searchAfter(lastHit.getSortValues()); searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); searchHits = searchResponse.getHits().getHits(); // Do something with searchHits } ``` 在上面的示例中,size 参数设置为 100,表示每次检索返回 100 个结果。search_after 参数使用上一次搜索的最后一个结果的排序值。 总之,深度分页查询Elasticsearch 中的实现需要考虑性能问题,建议使用 scroll API 或 search_after 参数来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值