
com.sap.cds.util.CdsSearchUtils Maven / Gradle / Ivy
/************************************************************************
* © 2020-2023 SAP SE or an SAP affiliate company. All rights reserved. *
************************************************************************/
package com.sap.cds.util;
import static com.sap.cds.impl.builder.model.Conjunction.and;
import static com.sap.cds.impl.parser.token.CqnBoolLiteral.FALSE;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.sap.cds.impl.builder.model.Connective;
import com.sap.cds.impl.builder.model.CqnNull;
import com.sap.cds.impl.builder.model.Disjunction;
import com.sap.cds.impl.builder.model.ElementRefImpl;
import com.sap.cds.impl.builder.model.LiteralImpl;
import com.sap.cds.impl.builder.model.Negation;
import com.sap.cds.impl.util.Stack;
import com.sap.cds.ql.CQL;
import com.sap.cds.ql.cqn.CqnComparisonPredicate;
import com.sap.cds.ql.cqn.CqnConnectivePredicate;
import com.sap.cds.ql.cqn.CqnElementRef;
import com.sap.cds.ql.cqn.CqnNegation;
import com.sap.cds.ql.cqn.CqnPredicate;
import com.sap.cds.ql.cqn.CqnSearchPredicate;
import com.sap.cds.ql.cqn.CqnSelect;
import com.sap.cds.ql.cqn.CqnVisitor;
import com.sap.cds.ql.impl.SelectBuilder;
import com.sap.cds.reflect.CdsAnnotatable;
import com.sap.cds.reflect.CdsAnnotation;
import com.sap.cds.reflect.CdsAssociationType;
import com.sap.cds.reflect.CdsBaseType;
import com.sap.cds.reflect.CdsElement;
import com.sap.cds.reflect.CdsStructuredType;
public class CdsSearchUtils {
private static final String DEFAULT_SEARCH_ELEMENT = "Search.defaultSearchElement";
private static final String CDS_SEARCH = "cds.search";
@Deprecated
private static final String SEARCH_CASCADE = "Search.cascade";
private CdsSearchUtils() {
}
public static Collection searchableElementRefs(CdsStructuredType targetType) {
return searchableRefs(targetType);
}
public static void moveSearchToWhere(CqnSelect select, CqnPredicate filter) {
SelectBuilder> selectBuilder = (SelectBuilder>) select;
selectBuilder.where(select.where().map(w -> and(w, filter)).orElse(filter));
selectBuilder.search((CqnPredicate) null);
}
public static CqnPredicate searchToLikeExpression(Collection elements, CqnPredicate expression) {
if (elements.isEmpty()) {
return FALSE;
}
Stack stack = new Stack<>();
CqnVisitor visitor = new CqnVisitor() {
@Override
public void visit(CqnSearchPredicate search) {
stack.push(anyElementContains(search.searchTerm()));
}
@Override
public void visit(CqnConnectivePredicate connective) {
int n = connective.predicates().size();
stack.push(Connective.create(connective.operator(), stack.pop(n)));
}
@Override
public void visit(CqnNegation cqnNegation) {
stack.push(Negation.not(stack.pop()));
}
private CqnPredicate anyElementContains(String searchTerm) {
return elements.stream().map(e -> containsCaseInsensitive(e, searchTerm)).collect(Disjunction.or());
}
};
expression.accept(visitor);
return stack.pop();
}
public static Collection getSearchableElements(CqnSelect select, CdsStructuredType targetType) {
Collection searchableElements = ((SelectBuilder>) select).searchableElements();
if (!searchableElements.isEmpty()) {
// are searchable elements set manually?
return searchableElements.stream().map(ElementRefImpl::parse).collect(Collectors.toList());
}
return searchableElementRefs(targetType);
}
private static Collection searchableRefs(CdsStructuredType targetType) {
Set searchablePaths = searchablePaths(targetType);
return searchablePaths.stream().map(ElementRefImpl::parse).collect(Collectors.toList());
}
private static Set searchablePaths(CdsStructuredType targetType) {
Set searchablePaths = new HashSet<>();
// @cds.search
onCdsSearch(targetType, (element, path) -> {
if (!element.getType().isAssociation() && !element.isVirtual()) {
searchablePaths.add(path);
}
});
// Old style search
targetType.elements().filter(annotatedWith(DEFAULT_SEARCH_ELEMENT)).filter(e -> !e.isVirtual())
.map(CdsElement::getName)
.forEach(searchablePaths::add);
// TODO - remove with 2.0 - https://github.wdf.sap.corp/cds-java/home/issues/574
targetType.associations().filter(annotatedWith(SEARCH_CASCADE))
.forEach(element -> onAssociation(searchablePaths, element));
// Default elements not explicitly disabled with @cds.search: false
if (searchablePaths.isEmpty()) {
defaultSearchElements(targetType).map(CdsElement::getName)
.filter(n -> targetType.getAnnotationValue(CDS_SEARCH + "." + n, true))
.forEach(searchablePaths::add);
}
onCdsSearch(targetType, ((element, path) -> onAssociation(searchablePaths, element)));
return searchablePaths;
}
private static void onCdsSearch(CdsStructuredType targetType, BiConsumer action) {
targetType.annotations()
.filter(annotation -> annotation.getName().startsWith(CDS_SEARCH)
&& Boolean.TRUE.equals(annotation.getValue()))
.map(CdsSearchUtils::path)
.forEach(path -> targetType.findElement(path).ifPresent(element -> action.accept(element, path)));
}
private static void onAssociation(Set searchablePaths, CdsElement element) {
if (element.getType().isAssociation()) {
String prefix = element.getName();
searchablePaths(element.getType().as(CdsAssociationType.class).getTarget()).stream()
.map(suffix -> prefix + "." + suffix).forEach(searchablePaths::add);
}
}
private static String path(CdsAnnotation> a) {
String name = a.getName();
return name.substring(CDS_SEARCH.length() + 1);
}
private static Stream defaultSearchElements(CdsStructuredType type) {
return type.elements().filter(e -> !e.isVirtual() && e.getType().isSimpleType(CdsBaseType.STRING));
}
private static Predicate super CdsAnnotatable> annotatedWith(String annotationName) {
return t -> t.getAnnotationValue(annotationName, false);
}
private static CqnPredicate containsCaseInsensitive(CqnElementRef element, String searchTerm) {
return CQL.and(CQL.comparison(element, CqnComparisonPredicate.Operator.IS_NOT, CqnNull.NULL),
CQL.contains(element, LiteralImpl.val(searchTerm), true));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy