2013-02-27 4 views
8

У меня есть база данных в эластичном поиске и вы хотите получить все записи на моей странице веб-сайта. Я написал bean-компонент, который соединяется с узлом упругого поиска, ищет записи и возвращает некоторый ответ. Мой простой код Java, который делает поиск, является:верните все записи в одном запросе в эластичном поиске

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

Но elasticsearch набор по умолчанию размера до 10, и у меня есть 10 ударов в ответ. В моей базе данных более 10 записей. Если я установил размер Integer.MAX_VALUE, мой поиск будет очень медленным, и это не то, что я хочу.

Как я могу получить все записи за одно действие за приемлемое время без установки размера ответа?

+0

У меня также такая же проблема. Thans для вашего вопроса –

+0

У меня есть 100 документов. Я устанавливаю Integer.MAX_VALUE как size.Got OutOfMemoryError [Java heap space] также в My Jboss. Если я даю 1000, то работал нормально. –

ответ

0

Вам придется торговать с количеством возвращаемых результатов и временем ожидания пользователя и количеством доступной памяти сервера. Если вы проиндексировали 1 000 000 документов, нет реалистичного способа получить все эти результаты в одном запросе. Я предполагаю, что ваши результаты для одного пользователя. Вам нужно будет подумать о том, как система будет работать под нагрузкой.

0

Чтобы запросить все, вы должны построить CountRequestBuilder, чтобы получить общее количество записей (по CountResponse), а затем установить число обратно в размер вашего запроса на поиск.

15
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; 
} 
+0

Это работает, но требует загрузки всего списка результатов в память, что необязательно и даже невозможно для очень больших наборов результатов. Более надежное решение заключается в использовании итераторов: http://stackoverflow.com/a/35729505/2091700 – Alphaaa

-1

1. установить максимальный размер, например: MAX_INT_VALUE;

закрытый статический окончательный int MAXSIZE = 1000000;

@Override общественности Список getAllSaleCityByCity (интермедиат 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.count ЭС, прежде чем искать

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

INT размер = (INT) countResponse.getCount() ; // это вы хотите размер;

, то вы можете

SearchResponse response= client.prepareSearch(getIndex(EsSaleCity.class)).setTypes(getType(EsSaleCity.class)).setSize(size); 
+0

это взорвется на фазе проверки, так как MAX_RESULT_WINDOW устанавливается как 10000. вам нужно установить значение прокрутки в файле подготовки, чтобы избежать сценарий. Возможно, использование метода prepareSearchScroll с идентификатором прокрутки из вашего объекта ответа будет лучшим решением. –

0

Если основной акцент делается на экспорт всех записей, которые вы могли бы хотеть пойти на такое решение, которое не требует какого-либо сортировки, как сортировка является дорогостоящей операцией. Вы можете использовать метод сканирования и прокрутки с помощью ElasticsearchCRUD, как описано here.

5

Текущий ответ с наивысшим рангом работает, но требует загрузки всего списка результатов в памяти, что может вызвать проблемы с памятью для больших наборов результатов и в любом случае не нужно.

Я создал класс Java, который реализует хороший Iterator по сравнению с SearchHit s, что позволяет выполнять итерацию по всем результатам. Внутренне он обрабатывает разбиение на страницы путем выпуска запросов, которые включают поле 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 
} 

Обратите внимание, что при создании вашего SearchRequestBuilder, вам не нужно вызывать setFrom(int), так как это будет сделано interally по 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]; 
    } 

} 

В самом деле, понимая, насколько это удобно иметь такой класс, интересно, почему Java-клиент ElasticSearch не предлагает что-то подобное.

+0

Если кто-то добавляет или удаляет документ, есть ли у нас гарантия, что мы перебираем все документы? –

+1

Я думаю, что использование [scrolls] (https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/java-search-scrolling.html) должно быть более надежным. –

+0

Это зависит от того, как вы создаете свой 'SearchRequestBuilder'. Например, если вы сортируете результаты по возрастанию ID документа и только добавляете документы с более высоким идентификатором документа, вы будете перебирать все результаты. Этот ответ был написан для ES 1.7, когда Scrolls еще не существовал. Они могут быть лучшей альтернативой. – Alphaaa

2

Вы можете использовать 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.