package org.alien4cloud.tosca.catalog.index;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import javax.annotation.Resource;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.metrics.tophits.TopHits;
import org.elasticsearch.search.aggregations.metrics.tophits.TopHitsBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import alien4cloud.common.AlienConstants;
import alien4cloud.dao.FilterUtil;
import alien4cloud.dao.IAggregationQueryManager;
import alien4cloud.dao.IGenericSearchDAO;
import alien4cloud.dao.model.FacetedSearchResult;
import alien4cloud.dao.model.FetchContext;
import lombok.SneakyThrows;
/**
* This abstract class allows to search tosca indexed elements (Csar, AbstractToscaType, Topology) as they all follow the same search query logic.
*/
public abstract class AbstractToscaIndexSearchService<T> {
@Resource(name = "alien-es-dao")
protected IGenericSearchDAO alienDAO;
public FacetedSearchResult search(Class<? extends T> clazz, String query, Integer size, Map<String, String[]> filters) {
TopHitsBuilder topHitAggregation = AggregationBuilders.topHits("highest_version").setSize(1)
.addSort(new FieldSortBuilder("nestedVersion.majorVersion").order(SortOrder.DESC))
.addSort(new FieldSortBuilder("nestedVersion.minorVersion").order(SortOrder.DESC))
.addSort(new FieldSortBuilder("nestedVersion.incrementalVersion").order(SortOrder.DESC))
.addSort(new FieldSortBuilder("nestedVersion.qualifier").order(SortOrder.DESC).missing("_first"));
AggregationBuilder aggregation = AggregationBuilders.terms("query_aggregation").field(getAggregationField()).size(size)
.subAggregation(topHitAggregation);
FacetedSearchResult<? extends T> searchResult = alienDAO.buildSearchQuery(clazz, query)
.setFilters(FilterUtil.singleKeyFilter(filters, "workspace", AlienConstants.GLOBAL_WORKSPACE_ID)).prepareSearch()
.setFetchContext(FetchContext.SUMMARY, topHitAggregation).facetedSearch(new IAggregationQueryManager() {
@Override
public AggregationBuilder getQueryAggregation() {
return aggregation;
}
@Override
@SneakyThrows({ IOException.class })
public void setData(ObjectMapper objectMapper, Function getClassFromType, FacetedSearchResult result, Aggregation aggregation) {
List<Object> resultData = Lists.newArrayList();
List<String> resultTypes = Lists.newArrayList();
if (aggregation == null) {
result.setData(getArray(0));
result.setTypes(new String[0]);
}
for (Terms.Bucket bucket : ((Terms) aggregation).getBuckets()) {
TopHits topHits = bucket.getAggregations().get("highest_version");
for (SearchHit hit : topHits.getHits()) {
resultTypes.add(hit.getType());
resultData.add(
objectMapper.readValue(hit.getSourceAsString(), ((Function<String, Class>) getClassFromType).apply(hit.getType())));
}
}
result.setData(resultData.toArray(getArray(resultData.size())));
result.setTypes(resultTypes.toArray(new String[resultTypes.size()]));
result.setFrom(0);
result.setTo(resultData.size());
if (size == Integer.MAX_VALUE || resultData.size() < size) {
result.setTotalResults(resultData.size());
} else {
// just to show that there is more results to fetch but iteration is not possible through aggregations.
result.setTotalResults(resultData.size() + ((Terms) aggregation).getSumOfOtherDocCounts());
}
}
});
return searchResult;
}
protected abstract String getAggregationField();
protected abstract T[] getArray(int size);
}