001/*-
002 * #%L
003 * HAPI FHIR JPA Server
004 * %%
005 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.jpa.search.builder.predicate;
021
022import ca.uhn.fhir.i18n.Msg;
023import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
024import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
025import ca.uhn.fhir.jpa.util.QueryParameterUtils;
026import ca.uhn.fhir.model.api.IQueryParameterType;
027import ca.uhn.fhir.rest.param.UriParam;
028import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
029import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
030import ca.uhn.fhir.util.StringUtil;
031import ca.uhn.fhir.util.UrlUtil;
032import com.healthmarketscience.sqlbuilder.BinaryCondition;
033import com.healthmarketscience.sqlbuilder.Condition;
034import com.healthmarketscience.sqlbuilder.FunctionCall;
035import com.healthmarketscience.sqlbuilder.UnaryCondition;
036import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040import java.util.List;
041
042import static ca.uhn.fhir.jpa.search.builder.predicate.StringPredicateBuilder.createLeftAndRightMatchLikeExpression;
043import static ca.uhn.fhir.jpa.search.builder.predicate.StringPredicateBuilder.createLeftMatchLikeExpression;
044
045public class SourcePredicateBuilder extends BaseJoiningPredicateBuilder {
046
047        private static final Logger ourLog = LoggerFactory.getLogger(SourcePredicateBuilder.class);
048        private final DbColumn myColumnSourceUri;
049        private final DbColumn myColumnRequestId;
050        private final DbColumn myResourceIdColumn;
051
052        /**
053         * Constructor
054         */
055        public SourcePredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
056                super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_RES_VER_PROV"));
057
058                myResourceIdColumn = getTable().addColumn("RES_PID");
059                myColumnSourceUri = getTable().addColumn("SOURCE_URI");
060                myColumnRequestId = getTable().addColumn("REQUEST_ID");
061        }
062
063        @Override
064        public DbColumn getResourceIdColumn() {
065                return myResourceIdColumn;
066        }
067
068        public Condition createPredicateSourceUri(String theSourceUri) {
069                return BinaryCondition.equalTo(myColumnSourceUri, generatePlaceholder(theSourceUri));
070        }
071
072        public Condition createPredicateMissingSourceUri() {
073                return UnaryCondition.isNull(myColumnSourceUri);
074        }
075
076        public Condition createPredicateSourceUriWithModifiers(
077                        IQueryParameterType theQueryParameter, JpaStorageSettings theStorageSetting, String theSourceUri) {
078                if (theQueryParameter.getMissing() != null && !theQueryParameter.getMissing()) {
079                        return UnaryCondition.isNotNull(myColumnSourceUri);
080                } else if (theQueryParameter instanceof UriParam && theQueryParameter.getQueryParameterQualifier() != null) {
081                        UriParam uriParam = (UriParam) theQueryParameter;
082                        switch (uriParam.getQualifier()) {
083                                case ABOVE:
084                                        return createPredicateSourceAbove(theSourceUri);
085                                case BELOW:
086                                        return createPredicateSourceBelow(theSourceUri);
087                                case CONTAINS:
088                                        return createPredicateSourceContains(theStorageSetting, theSourceUri);
089                                default:
090                                        throw new InvalidRequestException(Msg.code(2418)
091                                                        + String.format(
092                                                                        "Unsupported qualifier specified, qualifier=%s",
093                                                                        theQueryParameter.getQueryParameterQualifier()));
094                        }
095                } else {
096                        return createPredicateSourceUri(theSourceUri);
097                }
098        }
099
100        private Condition createPredicateSourceAbove(String theSourceUri) {
101                List<String> aboveUriCandidates = UrlUtil.getAboveUriCandidates(theSourceUri);
102                List<String> aboveUriPlaceholders = generatePlaceholders(aboveUriCandidates);
103                return QueryParameterUtils.toEqualToOrInPredicate(myColumnSourceUri, aboveUriPlaceholders);
104        }
105
106        private Condition createPredicateSourceBelow(String theSourceUri) {
107                String belowLikeExpression = createLeftMatchLikeExpression(theSourceUri);
108                return BinaryCondition.like(myColumnSourceUri, generatePlaceholder(belowLikeExpression));
109        }
110
111        private Condition createPredicateSourceContains(JpaStorageSettings theStorageSetting, String theSourceUri) {
112                if (theStorageSetting.isAllowContainsSearches()) {
113                        FunctionCall upperFunction = new FunctionCall("UPPER");
114                        upperFunction.addCustomParams(myColumnSourceUri);
115                        String normalizedString = StringUtil.normalizeStringForSearchIndexing(theSourceUri);
116                        String containsLikeExpression = createLeftAndRightMatchLikeExpression(normalizedString);
117                        return BinaryCondition.like(upperFunction, generatePlaceholder(containsLikeExpression));
118                } else {
119                        throw new MethodNotAllowedException(Msg.code(2417) + ":contains modifier is disabled on this server");
120                }
121        }
122
123        public Condition createPredicateRequestId(String theRequestId) {
124                return BinaryCondition.equalTo(myColumnRequestId, generatePlaceholder(theRequestId));
125        }
126}