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;
037
038import java.util.List;
039
040import static ca.uhn.fhir.jpa.search.builder.predicate.StringPredicateBuilder.createLeftAndRightMatchLikeExpression;
041import static ca.uhn.fhir.jpa.search.builder.predicate.StringPredicateBuilder.createLeftMatchLikeExpression;
042
043public class SourcePredicateBuilder extends BaseJoiningPredicateBuilder {
044
045        private final DbColumn myColumnSourceUri;
046        private final DbColumn myColumnRequestId;
047        private final DbColumn myResourceIdColumn;
048
049        /**
050         * Constructor
051         */
052        public SourcePredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
053                super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_RES_VER_PROV"));
054
055                myResourceIdColumn = getTable().addColumn("RES_PID");
056                myColumnSourceUri = getTable().addColumn("SOURCE_URI");
057                myColumnRequestId = getTable().addColumn("REQUEST_ID");
058        }
059
060        @Override
061        public DbColumn getResourceIdColumn() {
062                return myResourceIdColumn;
063        }
064
065        public Condition createPredicateSourceUri(String theSourceUri) {
066                return BinaryCondition.equalTo(myColumnSourceUri, generatePlaceholder(theSourceUri));
067        }
068
069        public Condition createPredicateMissingSourceUri() {
070                return UnaryCondition.isNull(myColumnSourceUri);
071        }
072
073        public Condition createPredicateSourceUriWithModifiers(
074                        IQueryParameterType theQueryParameter, JpaStorageSettings theStorageSetting, String theSourceUri) {
075                if (theQueryParameter.getMissing() != null && !theQueryParameter.getMissing()) {
076                        return UnaryCondition.isNotNull(myColumnSourceUri);
077                } else if (theQueryParameter instanceof UriParam && theQueryParameter.getQueryParameterQualifier() != null) {
078                        UriParam uriParam = (UriParam) theQueryParameter;
079                        switch (uriParam.getQualifier()) {
080                                case ABOVE:
081                                        return createPredicateSourceAbove(theSourceUri);
082                                case BELOW:
083                                        return createPredicateSourceBelow(theSourceUri);
084                                case CONTAINS:
085                                        return createPredicateSourceContains(theStorageSetting, theSourceUri);
086                                default:
087                                        throw new InvalidRequestException(Msg.code(2418)
088                                                        + String.format(
089                                                                        "Unsupported qualifier specified, qualifier=%s",
090                                                                        theQueryParameter.getQueryParameterQualifier()));
091                        }
092                } else {
093                        return createPredicateSourceUri(theSourceUri);
094                }
095        }
096
097        private Condition createPredicateSourceAbove(String theSourceUri) {
098                List<String> aboveUriCandidates = UrlUtil.getAboveUriCandidates(theSourceUri);
099                List<String> aboveUriPlaceholders = generatePlaceholders(aboveUriCandidates);
100                return QueryParameterUtils.toEqualToOrInPredicate(myColumnSourceUri, aboveUriPlaceholders);
101        }
102
103        private Condition createPredicateSourceBelow(String theSourceUri) {
104                String belowLikeExpression = createLeftMatchLikeExpression(theSourceUri);
105                return BinaryCondition.like(myColumnSourceUri, generatePlaceholder(belowLikeExpression));
106        }
107
108        private Condition createPredicateSourceContains(JpaStorageSettings theStorageSetting, String theSourceUri) {
109                if (theStorageSetting.isAllowContainsSearches()) {
110                        FunctionCall upperFunction = new FunctionCall("UPPER");
111                        upperFunction.addCustomParams(myColumnSourceUri);
112                        String normalizedString = StringUtil.normalizeStringForSearchIndexing(theSourceUri);
113                        String containsLikeExpression = createLeftAndRightMatchLikeExpression(normalizedString);
114                        return BinaryCondition.like(upperFunction, generatePlaceholder(containsLikeExpression));
115                } else {
116                        throw new MethodNotAllowedException(Msg.code(2417) + ":contains modifier is disabled on this server");
117                }
118        }
119
120        public Condition createPredicateRequestId(String theRequestId) {
121                return BinaryCondition.equalTo(myColumnRequestId, generatePlaceholder(theRequestId));
122        }
123}