一般搜索请求都是返回一"页"数据,无论数据量多大都一起返回给用户,Scroll API可以允许我们检索大量数据(甚至全部数据)。Scroll API允许我们做一个初始阶段搜索并且持续批量从Elasticsearch里拉取结果直到没有结果剩下。这有点像传统数据库里的cursors(游标)。

Scroll API的创建并不是为了实时的用户响应,而是为了处理大量的数据(Scrolling is not intended for real time user requests, but rather for processing large amounts of data)。从 scroll 请求返回的结果只是反映了 search 发生那一时刻的索引状态,就像一个快照(The results that are returned from a scroll request reflect the state of the index at the time that the initial search request was made, like a snapshot in time)。后续的对文档的改动(索引、更新或者删除)都只会影响后面的搜索请求。

1. 普通请求

假设我们想一次返回大量数据,下面代码中一次请求58000条数据:

  1.    /**
  2.     *  普通搜索
  3.     * @param client
  4.     */
  5.    public static void search(Client client) {
  6.        String index = "simple-index";
  7.        String type = "simple-type";
  8.        // 搜索条件
  9.        SearchRequestBuilder searchRequestBuilder = client.prepareSearch();
  10.        searchRequestBuilder.setIndices(index);
  11.        searchRequestBuilder.setTypes(type);
  12.        searchRequestBuilder.setSize(58000);
  13.        // 执行
  14.        SearchResponse searchResponse = searchRequestBuilder.get();
  15.        // 搜索结果
  16.        SearchHit[] searchHits = searchResponse.getHits().getHits();
  17.        for (SearchHit searchHit : searchHits) {
  18.            String source = searchHit.getSource().toString();
  19.            logger.info("--------- searchByScroll source {}", source);
  20.        } // for
  21.    }

运行结果:

  1. Caused by: QueryPhaseExecutionException[Result window is too large, from + size must be less than or equal to: [10000] but was [58000]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level parameter.]
  2. at org.elasticsearch.search.internal.DefaultSearchContext.preProcess(DefaultSearchContext.java:212)
  3. at org.elasticsearch.search.query.QueryPhase.preProcess(QueryPhase.java:103)
  4. at org.elasticsearch.search.SearchService.createContext(SearchService.java:676)
  5. at org.elasticsearch.search.SearchService.createAndPutContext(SearchService.java:620)
  6. at org.elasticsearch.search.SearchService.executeQueryPhase(SearchService.java:371)
  7. at org.elasticsearch.search.action.SearchServiceTransportAction$SearchQueryTransportHandler.messageReceived(SearchServiceTransportAction.java:368)
  8. at org.elasticsearch.search.action.SearchServiceTransportAction$SearchQueryTransportHandler.messageReceived(SearchServiceTransportAction.java:365)
  9. at org.elasticsearch.transport.TransportRequestHandler.messageReceived(TransportRequestHandler.java:33)
  10. at org.elasticsearch.transport.RequestHandlerRegistry.processMessageReceived(RequestHandlerRegistry.java:75)
  11. at org.elasticsearch.transport.TransportService$4.doRun(TransportService.java:376)
  12. at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:37)
  13. ... 3 more

从上面我们可以知道,搜索请求一次请求最大量为[10000]。我们的请求量已经超标,因此报错,异常信息提示我们请求大数据量的情况下使用Scroll API。

2. 使用Scroll API 请求

为了使用 scroll,初始搜索请求应该在查询中指定 scroll 参数,告诉 Elasticsearch 需要保持搜索的上下文环境多长时间(滚动时间)。

  1. searchRequestBuilder.setScroll(new TimeValue(60000));

下面代码中指定了查询条件以及滚动属性,如滚动的有效时长(使用setScroll()方法)。我们通过SearchResponse对象的getScrollId()方法获取滚动ID。滚动ID会在下一次请求中使用。

  1.    /**
  2.     * 使用scroll进行搜索
  3.     * @param client
  4.     */
  5.    public static String searchByScroll(Client client) {
  6.        String index = "simple-index";
  7.        String type = "simple-type";
  8.        // 搜索条件
  9.        SearchRequestBuilder searchRequestBuilder = client.prepareSearch();
  10.        searchRequestBuilder.setIndices(index);
  11.        searchRequestBuilder.setTypes(type);
  12.        searchRequestBuilder.setScroll(new TimeValue(30000));
  13.        // 执行
  14.        SearchResponse searchResponse = searchRequestBuilder.get();
  15.        String scrollId = searchResponse.getScrollId();
  16.        logger.info("--------- searchByScroll scrollID {}", scrollId);
  17.        SearchHit[] searchHits = searchResponse.getHits().getHits();
  18.        for (SearchHit searchHit : searchHits) {
  19.            String source = searchHit.getSource().toString();
  20.            logger.info("--------- searchByScroll source {}", source);
  21.        } // for
  22.        return scrollId;
  23.        
  24.    }

使用上面的请求返回的结果中的滚动ID,这个 ID 可以传递给 scroll API 来检索下一个批次的结果。这一次请求中不用添加索引和类型,这些都指定在了原始的 search 请求中。

每次返回下一个批次结果 直到没有结果返回时停止 即hits数组空时(Each call to the scroll API returns the next batch of results until there are no more results left to return, ie the hits array is empty)。

  1.    /**
  2.     *  通过滚动ID获取文档
  3.     * @param client
  4.     * @param scrollId
  5.     */
  6.    public static void searchByScrollId(Client client, String scrollId){
  7.        TimeValue timeValue = new TimeValue(30000);
  8.        SearchScrollRequestBuilder searchScrollRequestBuilder;
  9.        SearchResponse response;
  10.        // 结果
  11.        while (true) {
  12.            logger.info("--------- searchByScroll scrollID {}", scrollId);
  13.            searchScrollRequestBuilder = client.prepareSearchScroll(scrollId);
  14.            // 重新设定滚动时间
  15.            searchScrollRequestBuilder.setScroll(timeValue);
  16.            // 请求
  17.            response = searchScrollRequestBuilder.get();
  18.            // 每次返回下一个批次结果 直到没有结果返回时停止 即hits数组空时
  19.            if (response.getHits().getHits().length == 0) {
  20.                break;
  21.            } // if
  22.            // 这一批次结果
  23.            SearchHit[] searchHits = response.getHits().getHits();
  24.            for (SearchHit searchHit : searchHits) {
  25.                String source = searchHit.getSource().toString();
  26.                logger.info("--------- searchByScroll source {}", source);
  27.            } // for
  28.            // 只有最近的滚动ID才能被使用
  29.            scrollId = response.getScrollId();
  30.        } // while
  31.    }

备注:

初始搜索请求和每个后续滚动请求返回一个新的 滚动ID——只有最近的滚动ID才能被使用。(The initial search request and each subsequent scroll request returns a new_scroll_id — only the most recent _scroll_id should be used)

我每次后续滚动请求返回的滚动ID都是相同的,所以对上面的备注,不是很懂,有明白的可以告知,谢谢。

如果超过滚动时间,继续使用该滚动ID搜索数据,则会报错:

  1. Caused by: SearchContextMissingException[No search context found for id [2861]]
  2. at org.elasticsearch.search.SearchService.findContext(SearchService.java:613)
  3. at org.elasticsearch.search.SearchService.executeQueryPhase(SearchService.java:403)
  4. at org.elasticsearch.search.action.SearchServiceTransportAction$SearchQueryScrollTransportHandler.messageReceived(SearchServiceTransportAction.java:384)
  5. at org.elasticsearch.search.action.SearchServiceTransportAction$SearchQueryScrollTransportHandler.messageReceived(SearchServiceTransportAction.java:381)
  6. at org.elasticsearch.transport.TransportRequestHandler.messageReceived(TransportRequestHandler.java:33)
  7. at org.elasticsearch.transport.RequestHandlerRegistry.processMessageReceived(RequestHandlerRegistry.java:75)
  8. at org.elasticsearch.transport.TransportService$4.doRun(TransportService.java:376)
  9. at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:37)
  10. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
  11. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
  12. at java.lang.Thread.run(Thread.java:745)
3. 清除滚动ID

虽然当滚动有效时间已过,搜索上下文(Search Context)会自动被清除,但是一值保持滚动代价也是很大的,所以当我们不在使用滚动时要尽快使用Clear-Scroll API进行清除。

  1. /**
  2. * 清除滚动ID
  3. * @param client
  4. * @param scrollIdList
  5. * @return
  6. */
  7. public static boolean clearScroll(Client client, List<String> scrollIdList){
  8. ClearScrollRequestBuilder clearScrollRequestBuilder = client.prepareClearScroll();
  9. clearScrollRequestBuilder.setScrollIds(scrollIdList);
  10. ClearScrollResponse response = clearScrollRequestBuilder.get();
  11. return response.isSucceeded();
  12. }
  13. /**
  14. * 清除滚动ID
  15. * @param client
  16. * @param scrollId
  17. * @return
  18. */
  19. public static boolean clearScroll(Client client, String scrollId){
  20. ClearScrollRequestBuilder clearScrollRequestBuilder = client.prepareClearScroll();
  21. clearScrollRequestBuilder.addScrollId(scrollId);
  22. ClearScrollResponse response = clearScrollRequestBuilder.get();
  23. return response.isSucceeded();
  24. }
4. 参考:

https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-request-scroll.html

http://www.jianshu.com/p/14aa8b09c789

---------------------------------------

原文地址:

  https://blog.csdn.net/u014589856/article/details/78775233

[ElasticSearch]Java API 之 滚动搜索(Scroll API)的更多相关文章

  1. Elasticsearch java api 基本搜索部分详解

    文档是结合几个博客整理出来的,内容大部分为转载内容.在使用过程中,对一些疑问点进行了整理与解析. Elasticsearch java api 基本搜索部分详解 ElasticSearch 常用的查询 ...

  2. [搜索]ElasticSearch Java Api(一) -添加数据创建索引

    转载:http://blog.csdn.net/napoay/article/details/51707023 ElasticSearch JAVA API官网文档:https://www.elast ...

  3. Elasticsearch Java API的基本使用

    说明 在明确了ES的基本概念和使用方法后,我们来学习如何使用ES的Java API. 本文假设你已经对ES的基本概念已经有了一个比较全面的认识. 客户端 你可以用Java客户端做很多事情: 执行标准的 ...

  4. Elasticsearch Java Rest Client API 整理总结 (二) —— SearchAPI

    目录 引言 Search APIs Search API Search Request 可选参数 使用 SearchSourceBuilder 构建查询条件 指定排序 高亮请求 聚合请求 建议请求 R ...

  5. Elasticsearch Java API 很全的整理

    Elasticsearch 的API 分为 REST Client API(http请求形式)以及 transportClient API两种.相比来说transportClient API效率更高, ...

  6. 彻底搞懂 Elasticsearch Java API

    说明 在明确了ES的基本概念和使用方法后,我们来学习如何使用ES的Java API. 本文假设你已经对ES的基本概念已经有了一个比较全面的认识. 客户端 你可以用Java客户端做很多事情: 执行标准的 ...

  7. 第08章 ElasticSearch Java API

    本章内容 使用客户端对象(client object)连接到本地或远程ElasticSearch集群. 逐条或批量索引文档. 更新文档内容. 使用各种ElasticSearch支持的查询方式. 处理E ...

  8. Elasticsearch Java API深入详解

    0.题记 之前Elasticsearch的应用比较多,但大多集中在关系型.非关系型数据库与Elasticsearch之间的同步.以上内容完成了Elasticsearch所需要的基础数据量的供给.但想要 ...

  9. Elasticsearch java api 常用查询方法QueryBuilder构造举例

    转载:http://m.blog.csdn.net/u012546526/article/details/74184769 Elasticsearch java api 常用查询方法QueryBuil ...

随机推荐

  1. ES 25 - Elasticsearch的分页查询及其深分页问题 (deep paging)

    目录 1 分页查询方法 2 分页查询的deep paging问题 1 分页查询方法 在GET请求中拼接from和size参数 // 查询10条数据, 默认从第0条数据开始 GET book_shop/ ...

  2. c# 第36节 接口的声明

    本节内容: 1:字面理解接口 2:计算机的接口是什么呢 3:接口的声明 4:接口的注意点 1:字面理解接口 大家现在手机上,可能有很多app软件,比如天气预报的软件,有很多种,什么墨迹天气啊之类的等等 ...

  3. 有关csp自我反思

    首先说说体会把 这次前几个都是模拟,最后一道题以为自己可能会结果是半吊子根本不会,导致浪费了三个小时写第五题只有十分 如果不畏惧字符串而专心的写第三题的话,应该结果会不一样把.希望下次能好好考 第一题 ...

  4. oracle 数据库下所有表结构、数据量及缺失值统计

    表结构 SELECT t1.TABLE_NAME, t1.COLUMN_NAME, t1.DATA_TYPE || '(' || t1.DATA_LENGTH || ')', t2.COMMENTS ...

  5. keras 学习笔记(二) ——— data_generator

    data_generator 每次输出一个batch,基于keras.utils.Sequence Base object for fitting to a sequence of data, suc ...

  6. github 入门教程之 github 访问速度太慢怎么办

    github 是全世界最流行的开源项目托管平台,其代表的开源文化从根本上改变了软件开发的方式. 基本上所有的需求都能从 github 上或多或少找到现成的实现方案,再也不用重头开始造轮子而是自定义轮子 ...

  7. Winform重写键盘按键事件

    /// <summary> /// 重写键盘处理事件,处理退出和回车按钮 /// </summary> protected override bool ProcessCmdKe ...

  8. 微信小程序云开发-从0打造云音乐全栈小程序

    第1章 首门小程序“云开发”课程,你值得学习本章主要介绍什么是小程序云开发以及学习云开发的重要性,并介绍项目的整体架构,真机演示项目功能,详细介绍整体课程安排.课程适用人群以及需要掌握的前置知识.通过 ...

  9. 物联网架构成长之路(42)-直播流媒体入门(RTMP篇)

    1. 安装RTMP流媒体服务器 以前其实我是利用Nginx-RTMP-module搭建过RTMP流媒体服务器,并实现了鉴权功能.参考https://www.cnblogs.com/wunaozai/p ...

  10. 第八周论文学习03 An Efficient Tree-based Power Saving Scheme for Wireless Sensor Networks with Mobile Sink

    来源:IEEE Sensors Journal Year: 2016, Volume: 16, Issue: 20 Pages: 7545 - 7557, DOI: 10.1109/JSEN.2016 ...