编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

ES的searchAfter使用详解

wxchong 2024-12-14 15:37:13 开源技术 42 ℃ 0 评论

深度分页问题大致可以分为两类

  • 随机深度分页:随机跳转页面
  • 滚动深度分页:只能一页一页往下查询

在旧版本,ES为深度分页有scroll search 的方式,官方的建议不用于实时的请求,

每一个 scroll_id 不仅会占用大量的资源(特别是排序的请求),而且会生成历史快照,对于数据的变更不会反映到快照上。这种方式用于非实时处理大量数据的情况。

实时深度分页采用search_after 的方式,这是在 >= 5.0 版本提供的功能。

基本思想:searchAfter的方式通过维护一个实时游标来避免scroll的缺点,它可以用于实时请求和高并发场景。

每个文档具有一个唯一值的字段用作排序的仲裁器。建议使用字段_id,它包含每个文档的一个唯一值。

search_after缺点

不能随机跳转分页,只能一页一页向后翻,需要至少指定一个唯一不重复字段来排序。

它与滚动API非常相似,但与它不同,search_after参数是无状态的,它始终针对最新版本的搜索器进行解析。因此,排序顺序可能会在步行期间发生变化,具体取决于索引的更新和删除。



例子


searchAfter将from...size中的from用searchAfter的sort字段表示,通过不断更新此sort字段实现向下滚动效果,但是和from不同的是对数据更新不敏感。


使用search_after要求search_after查询值和sort值相同,即字段必须相同。

search-after使用需知:

1 使用search-after时不要修改你的query(search_after)和sort字段

2 排序后的每条数据都是含有自己的sort值的,且他们都可以作为你接下来的search-after的值。

3 请确保你的sort中最后得到的值是唯一的,比如时间排序,精确度不高导致sort有可能重复,那么使用search-after后得到的值可能不符合你的预期。有可能有丢失,官方提供的思路是将_doc等元数据也放入排序字段中,因为_doc等数据一般是唯一的,将其加入一般可以保证你的sort字段唯一。

public static void main(String[] args) {
        RestHighLevelClient restHighLevelClient = TestEsCreate.getEsClient("**.**.***.***:*****");//这里是创建RestHighLevelClient的实例,具体实现可以看我之前的代码
        SearchRequest searchRequest = new SearchRequest("logstash-applog-*");
        searchRequest.source(new SearchSourceBuilder().query(new BoolQueryBuilder()
                .filter(QueryBuilders.termQuery("projectName", "*******"))
                )
                .size(10)
                .sort("@timestamp", SortOrder.ASC)
                .sort("_doc", SortOrder.ASC)
        );
        try{
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            Object[] arrays = new Object[1];
            for(SearchHit hit : searchResponse.getHits().getHits()){
                Map<String, Object> map = hit.getSourceAsMap();
                System.out.println(JSONObject.toJSONString(map));
                
                System.out.println(hit.getSortValues()[0] + " " + hit.getSortValues()[1]); //打印sort值
                
                arrays = hit.getSortValues();
            }
            SearchRequest searchRequest_2 = new SearchRequest("logstash-applog-*");
            searchRequest_2.source(new SearchSourceBuilder().query(new BoolQueryBuilder()
                            .must(QueryBuilders.termQuery("projectName", "****"))
                    )
                    .size(10)
                    .sort("@timestamp", SortOrder.ASC)
                            .sort("_doc", SortOrder.ASC)
                    .searchAfter(arrays));
            SearchResponse searchResponse_2 = restHighLevelClient.search(searchRequest_2, RequestOptions.DEFAULT);
            for(SearchHit hit : searchResponse_2.getHits().getHits()){
                Map<String, Object> map = hit.getSourceAsMap();
                System.out.println(JSONObject.toJSONString(map));
                
                System.out.println(hit.getSortValues()[0]);
                
                arrays = hit.getSortValues();
            }
            SearchRequest searchRequest_3 = new SearchRequest("logstash-applog-*");
            searchRequest_3.source(new SearchSourceBuilder().query(new BoolQueryBuilder()
                            .must(QueryBuilders.termQuery("projectName", "****"))
                    )
                    .size(10)
                    .sort("@timestamp", SortOrder.ASC)
                    .sort("_doc", SortOrder.ASC)
                    .searchAfter(arrays));
            SearchResponse searchResponse_3 = restHighLevelClient.search(searchRequest_3, RequestOptions.DEFAULT);
            for(SearchHit hit : searchResponse_3.getHits().getHits()){
                Map<String, Object> map = hit.getSourceAsMap();
                System.out.println(JSONObject.toJSONString(map));
                System.out.println(hit.getSortValues()[0]);
            }
        }catch(Exception e){
            e.printStackTrace();
        }

    }


Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表