13

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

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

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

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

4

10 回答 10

20
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;
}
于 2014-04-21T10:07:00.253 回答
13

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

我创建了一个 Java 类,它实现了一个 nice Iteratorover SearchHits,它允许遍历所有结果。在内部,它通过发出包含该from:字段的查询来处理分页,并且它只在内存中保存一页 results

用法:

// 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
}

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

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];
    }

}

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

于 2016-03-01T17:31:39.427 回答
4

您可以使用滚动 API。使用 searchhit 迭代器的另一个建议也很有效,但前提是您不想更新这些命中。

import static org.elasticsearch.index.query.QueryBuilders.*;

QueryBuilder qb = termQuery("multi", "test");

SearchResponse scrollResp = client.prepareSearch(test)
        .addSort(FieldSortBuilder.DOC_FIELD_NAME, SortOrder.ASC)
        .setScroll(new TimeValue(60000))
        .setQuery(qb)
        .setSize(100).execute().actionGet(); //max of 100 hits will be returned for each scroll
//Scroll until no hits are returned
do {
    for (SearchHit hit : scrollResp.getHits().getHits()) {
        //Handle the hit...
    }

    scrollResp = client.prepareSearchScroll(scrollResp.getScrollId()).setScroll(new TimeValue(60000)).execute().actionGet();
} while(scrollResp.getHits().getHits().length != 0); // Zero hits mark the end of the scroll and the while loop.
于 2016-11-15T17:22:04.570 回答
3

您已经很久没有提出这个问题了,我想将我的答案发布给未来的读者。

正如上面提到的答案,最好加载具有大小的文档并在索引中有数千个文档时开始。在我的项目中,搜索加载 50 个结果作为默认大小并从零索引开始,如果用户想要加载更多数据,则将加载接下来的 50 个结果。这是我在代码中所做的:

public List<CourseDto> searchAllCourses(int startDocument) {

    final int searchSize = 50;
    final SearchRequest searchRequest = new SearchRequest("course_index");
    final SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    searchSourceBuilder.query(QueryBuilders.matchAllQuery());

    if (startDocument != 0) {
        startDocument += searchSize;
    }

    searchSourceBuilder.from(startDocument);
    searchSourceBuilder.size(searchSize);

    // sort the document
    searchSourceBuilder.sort(new FieldSortBuilder("publishedDate").order(SortOrder.ASC));
    searchRequest.source(searchSourceBuilder);

    List<CourseDto> courseList = new ArrayList<>();

    try {
        final SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        final SearchHits hits = searchResponse.getHits();

        // Do you want to know how many documents (results) are returned? here is you get:
        TotalHits totalHits = hits.getTotalHits();
        long numHits = totalHits.value;

        final SearchHit[] searchHits = hits.getHits();

        final ObjectMapper mapper = new ObjectMapper();

        for (SearchHit hit : searchHits) {
            // convert json object to CourseDto
            courseList.add(mapper.readValue(hit.getSourceAsString(), CourseDto.class));
        }
    } catch (IOException e) {
        logger.error("Cannot search by all mach. " + e);
    }
    return courseList;
}

信息: - Elasticsearch 版本 7.5.0 - Java 高级 REST 客户端用作客户端。

我希望,这对某人有用。

于 2020-05-30T21:51:05.717 回答
0

要查询全部,您应该构建一个 CountRequestBuilder 以获取记录总数(通过 CountResponse),然后将数字设置回您的搜索请求的大小。

于 2013-11-07T07:13:03.887 回答
0

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

于 2013-07-06T15:55:54.970 回答
0

如果您的主要关注点是导出所有记录,您可能希望寻求不需要任何排序的解决方案,因为排序是一项昂贵的操作。您可以将扫描和滚动方法与 ElasticsearchCRUD 一起使用,如此处所述

于 2016-01-07T16:51:43.780 回答
0
SearchResponse response = restHighLevelClient.search(new SearchRequest("Index_Name"), RequestOptions.DEFAULT);
SearchHit[] hits = response.getHits().getHits();
于 2022-01-24T11:33:53.143 回答
0

对于版本 6.3.2,以下工作:

public List<Map<String, Object>> getAllDocs(String indexName, String searchType) throws FileNotFoundException, UnsupportedEncodingException{

    int scrollSize = 1000;
    List<Map<String,Object>> esData = new ArrayList<>();
    SearchResponse response = null;
    int i=0;

    response = client.prepareSearch(indexName)
        .setScroll(new TimeValue(60000))
        .setTypes(searchType)  // The document types to execute the search against. Defaults to be executed against all types.
        .setQuery(QueryBuilders.matchAllQuery())
        .setSize(scrollSize).get(); //max of 100 hits will be returned for each scroll
    //Scroll until no hits are returned
    do {
        for (SearchHit hit : response.getHits().getHits()) {
            ++i;
            System.out.println (i + " " + hit.getId());
            writer.println(i + " " + hit.getId());
        }
        System.out.println(i);

        response = client.prepareSearchScroll(response.getScrollId()).setScroll(new TimeValue(60000)).execute().actionGet();
    } while(response.getHits().getHits().length != 0); // Zero hits mark the end of the scroll and the while loop.
    return esData;
}
于 2020-02-26T10:13:42.870 回答
-2

1.设置最大尺寸,例如:MAX_INT_VALUE;

私有静态最终 int MAXSIZE=1000000;

@Override public List getAllSaleCityByCity(int cityId) 抛出异常 {

    List<EsSaleCity> list=new ArrayList<EsSaleCity>();

    Client client=EsFactory.getClient();
    SearchResponse response= client.prepareSearch(getIndex(EsSaleCity.class)).setTypes(getType(EsSaleCity.class)).setSize(MAXSIZE)
            .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.boolFilter()
                    .must(FilterBuilders.termFilter("cityId", cityId)))).execute().actionGet();

    SearchHits searchHits=response.getHits();

    SearchHit[] hits=searchHits.getHits();
    for(SearchHit hit:hits){
        Map<String, Object> resultMap=hit.getSource();
        EsSaleCity saleCity=setEntity(resultMap, EsSaleCity.class);
        list.add(saleCity);
    }

    return list;

}

2.在搜索之前先计算ES

CountResponse countResponse = client.prepareCount(getIndex(EsSaleCity.class)).setTypes(getType(EsSaleCity.class)).setQuery(queryBuilder).execute().actionGet();

int size = (int)countResponse.getCount();//这是你想要的大小;

那么你也能

SearchResponse response= client.prepareSearch(getIndex(EsSaleCity.class)).setTypes(getType(EsSaleCity.class)).setSize(size);
于 2016-01-06T17:12:35.707 回答