[ElasticSearch]Java API 之 滚动搜索(Scroll API)

简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SunnyYoona/article/details/52810397 一般搜索请求都是返回一"页"数据,无论数据量多大都一起返回给用户,Scroll API可以允许我们检索大量数据(甚至全部数据)。
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SunnyYoona/article/details/52810397

一般搜索请求都是返回一"页"数据,无论数据量多大都一起返回给用户,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

5. 说明

本代码基于ElasticSearch 2.4.1 



相关实践学习
以电商场景为例搭建AI语义搜索应用
本实验旨在通过阿里云Elasticsearch结合阿里云搜索开发工作台AI模型服务,构建一个高效、精准的语义搜索系统,模拟电商场景,深入理解AI搜索技术原理并掌握其实现过程。
ElasticSearch 最新快速入门教程
本课程由千锋教育提供。全文搜索的需求非常大。而开源的解决办法Elasricsearch(Elastic)就是一个非常好的工具。目前是全文搜索引擎的首选。本系列教程由浅入深讲解了在CentOS7系统下如何搭建ElasticSearch,如何使用Kibana实现各种方式的搜索并详细分析了搜索的原理,最后讲解了在Java应用中如何集成ElasticSearch并实现搜索。 &nbsp;
目录
相关文章
|
10月前
|
Java API 数据处理
Java新特性:使用Stream API重构你的数据处理
Java新特性:使用Stream API重构你的数据处理
|
9月前
|
缓存 监控 前端开发
顺企网 API 开发实战:搜索 / 详情接口从 0 到 1 落地(附 Elasticsearch 优化 + 错误速查)
企业API开发常陷参数、缓存、错误处理三大坑?本指南拆解顺企网双接口全流程,涵盖搜索优化、签名验证、限流应对,附可复用代码与错误速查表,助你2小时高效搞定开发,提升响应速度与稳定性。
|
10月前
|
机器学习/深度学习 JSON 监控
淘宝拍立淘按图搜索与商品详情API的JSON数据返回详解
通过调用taobao.item.get接口,获取商品标题、价格、销量、SKU、图片、属性、促销信息等全量数据。
|
10月前
|
Java 大数据 API
Java Stream API:现代集合处理与函数式编程
Java Stream API:现代集合处理与函数式编程
420 100
|
10月前
|
Java API 数据处理
Java Stream API:现代集合处理新方式
Java Stream API:现代集合处理新方式
397 101
|
10月前
|
并行计算 Java 大数据
Java Stream API:现代数据处理之道
Java Stream API:现代数据处理之道
502 101
|
10月前
|
安全 Java API
使用 Java 构建强大的 REST API 的四个基本技巧
本文结合探险领域案例,分享Java构建REST API的四大核心策略:统一资源命名、版本控制与自动化文档、安全防护及标准化异常处理,助力开发者打造易用、可维护、安全可靠的稳健API服务。
562 116
|
11月前
|
存储 Java API
Java Stream API:现代数据处理之道
Java Stream API:现代数据处理之道
478 188
|
9月前
|
人工智能 自然语言处理 测试技术
Apipost智能搜索:只需用业务语言描述需求,就能精准定位目标接口,API 搜索的下一代形态!
在大型项目中,API 数量庞大、命名不一,导致“找接口”耗时费力。传统工具依赖关键词搜索,难以应对语义模糊或命名不规范的场景。Apipost AI 智能搜索功能,支持自然语言查询,如“和用户登录有关的接口”,系统可理解语义并精准匹配目标接口。无论是新人上手、模糊查找还是批量定位,都能大幅提升检索效率,降低协作成本。从关键词到语义理解,智能搜索让开发者少花时间找接口,多专注核心开发,真正实现高效协作。
|
9月前
|
存储 API 数据库
按图搜索1688商品的API接口
本文介绍如何利用阿里云ImageSearch服务实现1688商品的按图搜索功能。通过提取图像特征向量并计算相似度,结合Flask搭建API接口,可快速构建基于图片的商品检索系统,提升电商用户体验。