java - mostrar - devolver todos los registros en una consulta en búsqueda elástica
filtrar datos en jtable por jcombobox java (7)
Tengo una base de datos en búsqueda elástica y quiero obtener todos los registros en la página de mi sitio web. Escribí un bean, que se conecta al nodo de búsqueda elástica, busca registros y devuelve alguna respuesta. Mi código Java simple, que hace la búsqueda, es:
SearchResponse response = getClient().prepareSearch(indexName).setTypes(typeName) .setQuery(queryString("*:*")).setExplain(true).execute().actionGet();
Pero elasticsearch establece el tamaño predeterminado en 10 y tengo 10 hits en respuesta. Hay más de 10 registros en mi base de datos. Si configuro el tamaño en Integer.MAX_VALUE
mi búsqueda se vuelve muy lenta y esto no es lo que quiero.
¿Cómo puedo obtener todos los registros en una acción en un tiempo aceptable sin establecer el tamaño de la respuesta?
Tendrá que intercambiar el número de resultados devueltos con el tiempo que desea que espere el usuario y la cantidad de memoria disponible del servidor. Si ha indexado 1,000,000 de documentos, no hay una forma realista de recuperar todos esos resultados en una sola solicitud. Supongo que tus resultados son para un usuario. Tendrá que considerar cómo funcionará el sistema bajo carga.
Para consultar todo, debe construir un CountRequestBuilder para obtener el número total de registros (por CountResponse) y luego establecer el número de nuevo al tamaño de su solicitud de búsqueda.
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;
}
1. establecer el tamaño máximo, por ejemplo: MAX_INT_VALUE;
private static final int MAXSIZE = 1000000;
@Override public List getAllSaleCityByCity (int cityId) throws Exception {
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. cuenta el ES antes de buscar
CountResponse countResponse = client.prepareCount(getIndex(EsSaleCity.class)).setTypes(getType(EsSaleCity.class)).setQuery(queryBuilder).execute().actionGet();
int size = (int) countResponse.getCount (); // este es el tamaño que desea;
entonces tú puedes
SearchResponse response= client.prepareSearch(getIndex(EsSaleCity.class)).setTypes(getType(EsSaleCity.class)).setSize(size);
Si su objetivo principal es exportar todos los registros, es posible que desee obtener una solución que no requiera ningún tipo de clasificación, ya que la clasificación es una operación costosa. Puede usar el enfoque de escaneo y desplazamiento con ElasticsearchCRUD como se describe aquí .
La respuesta actual mejor clasificada funciona, pero requiere cargar toda la lista de resultados en la memoria, lo que puede causar problemas de memoria para grandes conjuntos de resultados y, en cualquier caso, es innecesaria.
SearchHit
una clase de Java que implementa un buen Iterator
sobre SearchHit
, que permite recorrer todos los resultados. Internamente, maneja la paginación emitiendo consultas que incluyen el campo from:
y solo mantiene en la memoria una página de resultados .
Uso:
// 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
}
Tenga en cuenta que, al crear su SearchRequestBuilder
, no necesita llamar a setFrom(int)
, ya que esto será realizado por SearchHitIterator
. Si desea especificar el tamaño de una página (es decir, el número de visitas de búsqueda por página), puede llamar a setSize(int)
, de lo contrario, se utiliza el valor predeterminado de 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];
}
}
De hecho, al darme cuenta de lo conveniente que es tener una clase de este tipo, me pregunto por qué el cliente Java de ElasticSearch no ofrece algo similar.
Podría usar la API de desplazamiento. La otra sugerencia de usar un iterador searchhit también funcionaría muy bien, pero solo cuando no desee actualizar esos hits.
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.