001package ca.uhn.fhir.jpa.dao.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.LegacySearchBuilder;
026import ca.uhn.fhir.model.api.IQueryParameterType;
027import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
028import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
029import org.hl7.fhir.r4.model.IdType;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032import org.springframework.beans.factory.annotation.Autowired;
033import org.springframework.context.annotation.Scope;
034import org.springframework.stereotype.Component;
035
036import javax.annotation.Nullable;
037import javax.persistence.criteria.Predicate;
038import java.util.ArrayList;
039import java.util.HashSet;
040import java.util.List;
041import java.util.Set;
042
043import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
044import static org.apache.commons.lang3.StringUtils.isNotBlank;
045
046@Component
047@Scope("prototype")
048public class PredicateBuilderResourceId extends BasePredicateBuilder {
049        private static final Logger ourLog = LoggerFactory.getLogger(PredicateBuilderResourceId.class);
050
051        @Autowired
052        IIdHelperService myIdHelperService;
053
054        public PredicateBuilderResourceId(LegacySearchBuilder theSearchBuilder) {
055                super(theSearchBuilder);
056        }
057
058        @Nullable
059        Predicate addPredicateResourceId(List<List<IQueryParameterType>> theValues, String theResourceName, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
060
061                Predicate nextPredicate = createPredicate(theResourceName, theValues, theOperation, theRequestPartitionId);
062
063                if (nextPredicate != null) {
064                        myQueryStack.addPredicate(nextPredicate);
065                        return nextPredicate;
066                }
067
068                return null;
069        }
070
071        @Nullable
072        private Predicate createPredicate(String theResourceName, List<List<IQueryParameterType>> theValues, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
073                Predicate nextPredicate = null;
074
075                Set<ResourcePersistentId> allOrPids = null;
076
077                for (List<? extends IQueryParameterType> nextValue : theValues) {
078                        Set<ResourcePersistentId> orPids = new HashSet<>();
079                        boolean haveValue = false;
080                        for (IQueryParameterType next : nextValue) {
081                                String value = next.getValueAsQueryToken(myContext);
082                                if (value != null && value.startsWith("|")) {
083                                        value = value.substring(1);
084                                }
085
086                                IdType valueAsId = new IdType(value);
087                                if (isNotBlank(value)) {
088                                        haveValue = true;
089                                        try {
090                                                ResourcePersistentId pid = myIdHelperService.resolveResourcePersistentIds(theRequestPartitionId, theResourceName, valueAsId.getIdPart());
091                                                orPids.add(pid);
092                                        } catch (ResourceNotFoundException e) {
093                                                // This is not an error in a search, it just results in no matchesFhirResourceDaoR4InterceptorTest
094                                                ourLog.debug("Resource ID {} was requested but does not exist", valueAsId.getIdPart());
095                                        }
096                                }
097                        }
098                        if (haveValue) {
099                                if (allOrPids == null) {
100                                        allOrPids = orPids;
101                                } else {
102                                        allOrPids.retainAll(orPids);
103                                }
104
105                        }
106                }
107
108                if (allOrPids != null && allOrPids.isEmpty()) {
109
110                        // This will never match
111                        nextPredicate = myCriteriaBuilder.equal(myQueryStack.getResourcePidColumn(), -1);
112
113                } else if (allOrPids != null) {
114
115                        SearchFilterParser.CompareOperation operation = defaultIfNull(theOperation, SearchFilterParser.CompareOperation.eq);
116                        assert operation == SearchFilterParser.CompareOperation.eq || operation == SearchFilterParser.CompareOperation.ne;
117                        List<Predicate> codePredicates = new ArrayList<>();
118                        switch (operation) {
119                                default:
120                                case eq:
121                                        codePredicates.add(myQueryStack.getResourcePidColumn().in(ResourcePersistentId.toLongList(allOrPids)));
122                                        nextPredicate = myCriteriaBuilder.and(toArray(codePredicates));
123                                        break;
124                                case ne:
125                                        codePredicates.add(myQueryStack.getResourcePidColumn().in(ResourcePersistentId.toLongList(allOrPids)).not());
126                                        nextPredicate = myCriteriaBuilder.and(toArray(codePredicates));
127                                        break;
128                        }
129
130                }
131
132                return nextPredicate;
133        }
134
135}