
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}