
com.sap.cds.impl.SearchResolver Maven / Gradle / Ivy
/*******************************************************************
* © 2020 SAP SE or an SAP affiliate company. All rights reserved. *
*******************************************************************/
package com.sap.cds.impl;
import static com.sap.cds.impl.builder.model.Conjunction.and;
import static com.sap.cds.impl.builder.model.LiteralImpl.literal;
import static com.sap.cds.impl.parser.token.CqnBoolLiteral.FALSE;
import static java.util.Comparator.comparing;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import com.sap.cds.impl.builder.model.ComparisonPredicate;
import com.sap.cds.impl.builder.model.Connective;
import com.sap.cds.impl.builder.model.Disjunction;
import com.sap.cds.impl.builder.model.ElementRefImpl;
import com.sap.cds.impl.builder.model.ExistsSubQuery;
import com.sap.cds.impl.builder.model.Negation;
import com.sap.cds.impl.builder.model.SubQuery;
import com.sap.cds.impl.util.Stack;
import com.sap.cds.ql.ElementRef;
import com.sap.cds.ql.RefSegment;
import com.sap.cds.ql.Select;
import com.sap.cds.ql.cqn.CqnConnectivePredicate;
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.CdsElement;
import com.sap.cds.reflect.CdsEntity;
import com.sap.cds.reflect.CdsModel;
import com.sap.cds.reflect.CdsStructuredType;
import com.sap.cds.util.CdsSearchUtils;
import com.sap.cds.util.CqnStatementUtils;
public class SearchResolver {
private final CdsModel model;
public SearchResolver(CdsModel model) {
this.model = model;
}
public CqnSelect resolve(CqnSelect select) {
select.search().ifPresent(expression -> {
CdsStructuredType targetType = CqnStatementUtils.targetType(model, select);
Collection> searchableRefs = getSearchableElements(select, targetType);
CqnPredicate filter = searchToContains(searchableRefs, expression);
if (anyRefViaCollectionAssociation(targetType, searchableRefs)) {
if (!(targetType instanceof CdsEntity)) {
throw new UnsupportedOperationException(
"A path expression used in search must originate from an entity");
}
CdsEntity targetEntity = (CdsEntity) targetType;
filter = wrapIntoExistsSubquery(targetEntity, filter);
}
moveSearchToWhere(select, filter);
});
return select;
}
public CqnSelect resolveWithPath(CqnSelect select) {
select.search().ifPresent(expression -> {
CdsStructuredType targetType = CqnStatementUtils.targetType(model, select);
Collection> searchableRefs = getSearchableElements(select, targetType);
CqnPredicate filter = searchToContains(searchableRefs, expression);
moveSearchToWhere(select, filter);
});
return select;
}
private static boolean anyRefViaCollectionAssociation(CdsStructuredType root, Collection> refs) {
for (ElementRef> ref : refs) {
List segments = ref.segments();
if (segments.size() > 1) {
List prefix = segments.subList(0, segments.size() - 1);
if (!CqnStatementUtils.isToOnePath(root, prefix)) {
return true;
}
}
}
return false;
}
private 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);
}
private CqnPredicate wrapIntoExistsSubquery(CdsEntity target, CqnPredicate search) {
CqnPredicate toOuter = target.concreteNonAssociationElements().filter(e -> e.isKey())
.sorted(comparing(CdsElement::getName)).map(e -> outerToInner(e.getName())).collect(and());
CqnSelect subquery = Select.from(target).where(and(toOuter, search));
return new ExistsSubQuery(subquery);
}
private static CqnPredicate outerToInner(String name) {
ElementRef
© 2015 - 2025 Weber Informatics LLC | Privacy Policy