package org.alien4cloud.tosca.catalog.index;
import static alien4cloud.dao.FilterUtil.fromKeyValueCouples;
import static alien4cloud.dao.FilterUtil.singleKeyFilter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import org.alien4cloud.tosca.model.CSARDependency;
import org.alien4cloud.tosca.model.Csar;
import org.alien4cloud.tosca.model.types.AbstractToscaType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import alien4cloud.dao.IGenericSearchDAO;
import alien4cloud.dao.model.FacetedSearchResult;
import alien4cloud.exception.NotFoundException;
import alien4cloud.utils.VersionUtil;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
@Primary
public class ToscaTypeSearchService extends AbstractToscaIndexSearchService<AbstractToscaType> implements IToscaTypeSearchService {
@Resource(name = "alien-es-dao")
private IGenericSearchDAO searchDAO;
@Override
public Csar getArchive(String archiveName, String archiveVersion) {
return searchDAO.findById(Csar.class, Csar.createId(archiveName, archiveVersion));
}
@Override
public boolean hasTypes(String archiveName, String archiveVersion) {
return searchDAO.buildQuery(AbstractToscaType.class).setFilters(fromKeyValueCouples("archiveName", archiveName, "archiveVersion", archiveVersion))
.count() > 0;
}
@Override
public AbstractToscaType[] getArchiveTypes(String archiveName, String archiveVersion) {
return searchDAO.buildQuery(AbstractToscaType.class).setFilters(fromKeyValueCouples("archiveName", archiveName, "archiveVersion", archiveVersion))
.prepareSearch().search(0, Integer.MAX_VALUE).getData();
}
@Override
public <T extends AbstractToscaType> T find(Class<T> elementType, String elementId, String version) {
return searchDAO.buildQuery(elementType).setFilters(fromKeyValueCouples("rawElementId", elementId, "archiveVersion", version)).prepareSearch().find();
}
@Override
public <T extends AbstractToscaType> T findMostRecent(Class<T> elementType, String elementId) {
return searchDAO.buildQuery(elementType).setFilters(fromKeyValueCouples("rawElementId", elementId)).prepareSearch()
.alterSearchRequestBuilder(
searchRequestBuilder -> searchRequestBuilder.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")))
.find();
}
@Override
public <T extends AbstractToscaType> T[] findAll(Class<T> elementType, String elementId) {
return searchDAO.buildQuery(elementType).setFilters(singleKeyFilter("rawElementId", elementId)).prepareSearch().search(0, Integer.MAX_VALUE).getData();
}
/**
* Build an elasticsearch query to get data tosca elements based on a set of dependencies.
*
* @param dependencies The set of dependencies.
* @param keyValueFilters List of key1, value1, key2, value2 to add term filters to the query for each dependency.
* @return
*/
private BoolQueryBuilder getDependencyQuery(Set<CSARDependency> dependencies, String... keyValueFilters) {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
for (CSARDependency dependency : dependencies) {
BoolQueryBuilder dependencyQuery = QueryBuilders.boolQuery();
dependencyQuery.must(QueryBuilders.termQuery("archiveName", dependency.getName()))
.must(QueryBuilders.termQuery("archiveVersion", dependency.getVersion()));
if (keyValueFilters != null) {
for (int i = 0; i < keyValueFilters.length; i += 2) {
dependencyQuery.must(QueryBuilders.termQuery(keyValueFilters[i], keyValueFilters[i + 1]));
}
}
boolQueryBuilder.should(dependencyQuery);
}
return boolQueryBuilder;
}
@Override
public boolean isElementExistInDependencies(@NonNull Class<? extends AbstractToscaType> elementClass, @NonNull String elementId,
Set<CSARDependency> dependencies) {
if (dependencies == null || dependencies.isEmpty()) {
return false;
}
return searchDAO.count(elementClass, getDependencyQuery(dependencies, "rawElementId", elementId)) > 0;
}
private <T extends AbstractToscaType> T getLatestVersionOfElement(Class<T> elementClass, QueryBuilder queryBuilder) {
List<T> elements = searchDAO.customFindAll(elementClass, queryBuilder);
if (elements != null && !elements.isEmpty()) {
Collections.sort(elements,
(left, right) -> VersionUtil.parseVersion(left.getArchiveVersion()).compareTo(VersionUtil.parseVersion(right.getArchiveVersion())));
return elements.get(elements.size() - 1);
} else {
return null;
}
}
@Override
public <T extends AbstractToscaType> T getElementInDependencies(Class<T> elementClass, Set<CSARDependency> dependencies, String... keyValues) {
if (dependencies == null || dependencies.isEmpty()) {
return null;
}
BoolQueryBuilder boolQueryBuilder = getDependencyQuery(dependencies, keyValues);
return getLatestVersionOfElement(elementClass, boolQueryBuilder);
}
@Override
public <T extends AbstractToscaType> T getElementInDependencies(Class<T> elementClass, String elementId, Set<CSARDependency> dependencies) {
if (dependencies == null || dependencies.isEmpty()) {
return null;
}
BoolQueryBuilder boolQueryBuilder = getDependencyQuery(dependencies, "rawElementId", elementId);
return getLatestVersionOfElement(elementClass, boolQueryBuilder);
}
@Override
public <T extends AbstractToscaType> T getRequiredElementInDependencies(Class<T> elementClass, String elementId, Set<CSARDependency> dependencies)
throws NotFoundException {
T element = getElementInDependencies(elementClass, elementId, dependencies);
if (element == null) {
throw new NotFoundException(
"Element elementId: <" + elementId + "> of type <" + elementClass.getSimpleName() + "> cannot be found in dependencies " + dependencies);
}
return element;
}
// we need to override for aspect purpose
@Override
public FacetedSearchResult search(Class<? extends AbstractToscaType> clazz, String query, Integer size, Map<String, String[]> filters) {
return super.search(clazz, query, size, filters);
}
@Override
protected AbstractToscaType[] getArray(int size) {
return new AbstractToscaType[size];
}
@Override
protected String getAggregationField() {
return "rawElementId";
}
}