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 ResourceHistoryPredicateBuilder extends BaseJoiningPredicateBuilder implements ISourcePredicateBuilder {
044
045        private final DbColumn myColumnSourceUri;
046        private final DbColumn myColumnRequestId;
047        private final DbColumn myResourceIdColumn;
048
049        /**
050         * Constructor
051         */
052        public ResourceHistoryPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
053                super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_RES_VER"));
054
055                myResourceIdColumn = getTable().addColumn("RES_ID");
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        @Override
066        public Condition createPredicateSourceUri(String theSourceUri) {
067                return BinaryCondition.equalTo(myColumnSourceUri, generatePlaceholder(theSourceUri));
068        }
069
070        @Override
071        public Condition createPredicateMissingSourceUri() {
072                return UnaryCondition.isNull(myColumnSourceUri);
073        }
074
075        @Override
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        @Override
124        public Condition createPredicateRequestId(String theRequestId) {
125                return BinaryCondition.equalTo(myColumnRequestId, generatePlaceholder(theRequestId));
126        }
127}