返回Elasticsearch中一个查询中的所有记录

San*_*n4o 10 java api search elasticsearch

我有一个弹性搜索数据库,想要在我的网站页面上获取所有记录.我写了一个bean,它连接到弹性搜索节点,搜索记录并返回一些响应.我的简单java代码,用于搜索,是

SearchResponse response = getClient().prepareSearch(indexName)
    .setTypes(typeName)              
    .setQuery(queryString("\*:*"))
    .setExplain(true)
    .execute().actionGet();
Run Code Online (Sandbox Code Playgroud)

但是Elasticsearch将默认大小设置为10,并且我有10次点击响应.我的数据库中有超过10条记录.如果我设置大小Integer.MAX_VALUE我的搜索变得非常慢,这不是我想要的.

如何在没有设置响应大小的情况下,在可接受的时间内在一个操作中获取所有记录?

Din*_*air 18

public List<Map<String, Object>> getAllDocs(){
        int scrollSize = 1000;
        List<Map<String,Object>> esData = new ArrayList<Map<String,Object>>();
        SearchResponse response = null;
        int i = 0;
        while( response == null || response.getHits().hits().length != 0){
            response = client.prepareSearch(indexName)
                    .setTypes(typeName)
                       .setQuery(QueryBuilders.matchAllQuery())
                       .setSize(scrollSize)
                       .setFrom(i * scrollSize)
                    .execute()
                    .actionGet();
            for(SearchHit hit : response.getHits()){
                esData.add(hit.getSource());
            }
            i++;
        }
        return esData;
}
Run Code Online (Sandbox Code Playgroud)


Alp*_*aaa 9

当前排名最高的答案有效,但它需要在内存中加载整个结果列表,这可能导致大型结果集的内存问题,并且在任何情况下都是不必要的.

我创建了一个实现了不错的Java类IteratorSearchHitS,允许通过所有的结果进行迭代.在内部,它通过发出包含该from:字段的查询来处理分页,并且它仅在内存中保留一页结果.

用法:

// build your query here -- no need for setFrom(int)
SearchRequestBuilder requestBuilder = client.prepareSearch(indexName)
                                            .setTypes(typeName)
                                            .setQuery(QueryBuilders.matchAllQuery()) 

SearchHitIterator hitIterator = new SearchHitIterator(requestBuilder);
while (hitIterator.hasNext()) {
    SearchHit hit = hitIterator.next();

    // process your hit
}
Run Code Online (Sandbox Code Playgroud)

请注意,在创建时SearchRequestBuilder,您不需要调用setFrom(int),因为这将由内部完成SearchHitIterator.如果要指定页面大小(即每页搜索命中数),可以调用setSize(int),否则使用ElasticSearch的默认值.

SearchHitIterator:

import java.util.Iterator;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;

public class SearchHitIterator implements Iterator<SearchHit> {

    private final SearchRequestBuilder initialRequest;

    private int searchHitCounter;
    private SearchHit[] currentPageResults;
    private int currentResultIndex;

    public SearchHitIterator(SearchRequestBuilder initialRequest) {
        this.initialRequest = initialRequest;
        this.searchHitCounter = 0;
        this.currentResultIndex = -1;
    }

    @Override
    public boolean hasNext() {
        if (currentPageResults == null || currentResultIndex + 1 >= currentPageResults.length) {
            SearchRequestBuilder paginatedRequestBuilder = initialRequest.setFrom(searchHitCounter);
            SearchResponse response = paginatedRequestBuilder.execute().actionGet();
            currentPageResults = response.getHits().getHits();

            if (currentPageResults.length < 1) return false;

            currentResultIndex = -1;
        }

        return true;
    }

    @Override
    public SearchHit next() {
        if (!hasNext()) return null;

        currentResultIndex++;
        searchHitCounter++;
        return currentPageResults[currentResultIndex];
    }

}
Run Code Online (Sandbox Code Playgroud)

事实上,意识到拥有这样一个类是多么方便,我想知道为什么ElasticSearch的Java客户端不提供类似的东西.

  • 我认为使用 [scrolls](https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/java-search-scrolling.html) 应该更健壮。 (3认同)

小智 0

您必须权衡返回结果的数量与您希望用户等待的时间以及可用服务器内存量。如果您已对 1,000,000 个文档建立了索引,则没有一种现实的方法可以在一次请求中检索所有这些结果。我假设您的结果适用于一名用户。您必须考虑系统在负载下的性能。