001package ca.uhn.fhir.jpa.search.builder.predicate;
002
003/*-
004 * #%L
005 * HAPI FHIR JPA Server
006 * %%
007 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.interceptor.model.RequestPartitionId;
024import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
025import ca.uhn.fhir.jpa.dao.index.IdHelperService;
026import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
027import ca.uhn.fhir.jpa.search.builder.QueryStack;
028import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
029import ca.uhn.fhir.model.api.IQueryParameterType;
030import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
031import ca.uhn.fhir.rest.param.TokenParam;
032import ca.uhn.fhir.rest.param.TokenParamModifier;
033import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
034import com.healthmarketscience.sqlbuilder.Condition;
035import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
036import org.hl7.fhir.r4.model.IdType;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039import org.springframework.beans.factory.annotation.Autowired;
040
041import javax.annotation.Nullable;
042import java.util.HashSet;
043import java.util.List;
044import java.util.Set;
045
046import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
047import static org.apache.commons.lang3.StringUtils.isNotBlank;
048
049public class ResourceIdPredicateBuilder extends BasePredicateBuilder {
050        private static final Logger ourLog = LoggerFactory.getLogger(ResourceIdPredicateBuilder.class);
051
052        @Autowired
053        private IIdHelperService myIdHelperService;
054
055        /**
056         * Constructor
057         */
058        public ResourceIdPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
059                super(theSearchSqlBuilder);
060        }
061
062
063        @Nullable
064        public Condition createPredicateResourceId(@Nullable DbColumn theSourceJoinColumn, String theResourceName, List<List<IQueryParameterType>> theValues, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
065
066                Set<ResourcePersistentId> allOrPids = null;
067                SearchFilterParser.CompareOperation defaultOperation = SearchFilterParser.CompareOperation.eq;
068
069                boolean allIdsAreForcedIds = true;
070                for (List<? extends IQueryParameterType> nextValue : theValues) {
071                        Set<ResourcePersistentId> orPids = new HashSet<>();
072                        boolean haveValue = false;
073                        for (IQueryParameterType next : nextValue) {
074                                String value = next.getValueAsQueryToken(getFhirContext());
075                                if (value != null && value.startsWith("|")) {
076                                        value = value.substring(1);
077                                }
078
079                                IdType valueAsId = new IdType(value);
080                                if (isNotBlank(value)) {
081                                        if (!myIdHelperService.idRequiresForcedId(valueAsId.getIdPart()) && allIdsAreForcedIds) {
082                                                allIdsAreForcedIds = false;
083                                        }
084                                        haveValue = true;
085                                        try {
086                                                ResourcePersistentId pid = myIdHelperService.resolveResourcePersistentIds(theRequestPartitionId, theResourceName, valueAsId.getIdPart());
087                                                orPids.add(pid);
088                                        } catch (ResourceNotFoundException e) {
089                                                // This is not an error in a search, it just results in no matches
090                                                ourLog.debug("Resource ID {} was requested but does not exist", valueAsId.getIdPart());
091                                        }
092                                }
093
094                                if (next instanceof TokenParam) {
095                                        if (((TokenParam) next).getModifier() == TokenParamModifier.NOT) {
096                                                defaultOperation = SearchFilterParser.CompareOperation.ne;
097                                        }
098                                }
099
100                        }
101                        if (haveValue) {
102                                if (allOrPids == null) {
103                                        allOrPids = orPids;
104                                } else {
105                                        allOrPids.retainAll(orPids);
106                                }
107
108                        }
109                }
110
111                if (allOrPids != null && allOrPids.isEmpty()) {
112
113                        setMatchNothing();
114
115                } else if (allOrPids != null) {
116
117                        SearchFilterParser.CompareOperation operation = defaultIfNull(theOperation, defaultOperation);
118                        assert operation == SearchFilterParser.CompareOperation.eq || operation == SearchFilterParser.CompareOperation.ne;
119
120                        List<Long> resourceIds = ResourcePersistentId.toLongList(allOrPids);
121                        if (theSourceJoinColumn == null) {
122                                BaseJoiningPredicateBuilder queryRootTable = super.getOrCreateQueryRootTable(!allIdsAreForcedIds);
123                                Condition predicate;
124                                switch (operation) {
125                                        default:
126                                        case eq:
127                                                predicate = queryRootTable.createPredicateResourceIds(false, resourceIds);
128                                                return queryRootTable.combineWithRequestPartitionIdPredicate(theRequestPartitionId, predicate);
129                                        case ne:
130                                                predicate = queryRootTable.createPredicateResourceIds(true, resourceIds);
131                                                return queryRootTable.combineWithRequestPartitionIdPredicate(theRequestPartitionId, predicate);
132                                }
133                        } else {
134                                return QueryStack.toEqualToOrInPredicate(theSourceJoinColumn, generatePlaceholders(resourceIds), operation == SearchFilterParser.CompareOperation.ne);
135                        }
136
137                }
138
139                return null;
140        }
141
142
143}