Resolvi parcialmente com um workaround, basicamente a paginação não existe para as agregações (https://github.com/elastic/elasticsearch/issues/4915) porém resolvi com uma ideia baseada na ideia de dividir para conquistar:
- A consulta que tinha 3 agregações foi fragmentada em 2 consultas até o ES.
- A primeira fazia o filtro dos produtos no mês em questão, a jogada aqui foi colocar o size para delimitar o retorno em apenas X itens.
- Uma vez retornado os itens, a agregação do top_hits é 1 para 1, então o size feito na primeira consulta já serviria de delimitador.
O problema que não foi solucionado é que para paginação, no cenário onde o usuário quer a partir do 20° item, os próximos 10, tenho que fazer um size de 30 e então 'picotar' do 20 até 30 e assim por diante, ou seja, tenho um processamento perdido..
public assinaturaQualquer (Parametros necessários) {
List<Produts> resultList = new ArrayList<>();
JsonNode distinctProdutsInPeriod;
SearchRequestBuilder searchRequestBuilderGenerics = /*Método que monta query com o periodo de data e demais filters necessários*/;
searchRequestBuilderGenerics.addAggregation(AggregationBuilders.terms(CALLS).field(fieldToAggregate).size(pageSize)); /* <- Pulo do gato */
searchRequestBuilderGenerics.execute().get();
distinctProdutsInPeriod = mapper.readTree(resultQuery.toString()).get(ES_AGGREGATIONS).get(CALLS).get(ES_BUCKETS);
List<String> productsListFilter = new ArrayList<>();
distinctProdutsInPeriod.forEach(each -> {
productsListFilter.add(each.get(ES_KEY).asText());
});
if (productsListFilter.isEmpty()) {
return resultList;
}
SearchResponse resultQuery = /*Método que monta query com o periodo de data e demais filters necessários E FAZ O 'IN' DA LISTA DE productsListFilter USANDO constantScoreQuery*/;
resultQuery.addAggregation(AggregationBuilders.terms(CALLS).field(fieldToAggregate)
.subAggregation(AggregationBuilders.topHits(LATEST_CALL).addSort(RECEIVED_ON, SortOrder.DESC).setSize(1)
.setFetchSource(new String[] { /*Fields to return*/ }, null)));
resultQuery.execute().get();
JsonNode jsonNode = mapper.readTree(resultQuery.toString()).get(ES_AGGREGATIONS).get(CALLS).get(ES_BUCKETS);
jsonNode.forEach(lastCall -> {
/* Aqui só alegria */
resultList.add(lastCall);
});
/* No caso meu front ficou responsável por picar a informação de acordo com que for usar. */
return resultList;
}