
001/*- 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2025 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.interceptor.model.RequestPartitionId; 023import ca.uhn.fhir.jpa.cache.ISearchParamIdentityCacheSvc; 024import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam; 025import ca.uhn.fhir.jpa.search.builder.models.MissingQueryParameterPredicateParams; 026import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder; 027import ca.uhn.fhir.jpa.util.QueryParameterUtils; 028import com.google.common.annotations.VisibleForTesting; 029import com.healthmarketscience.sqlbuilder.BinaryCondition; 030import com.healthmarketscience.sqlbuilder.ComboCondition; 031import com.healthmarketscience.sqlbuilder.Condition; 032import com.healthmarketscience.sqlbuilder.NotCondition; 033import com.healthmarketscience.sqlbuilder.SelectQuery; 034import com.healthmarketscience.sqlbuilder.UnaryCondition; 035import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; 036import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable; 037import jakarta.annotation.Nonnull; 038import org.springframework.beans.factory.annotation.Autowired; 039 040import java.util.ArrayList; 041import java.util.List; 042 043public abstract class BaseSearchParamPredicateBuilder extends BaseJoiningPredicateBuilder 044 implements ICanMakeMissingParamPredicate { 045 046 private final DbColumn myColumnMissing; 047 private final DbColumn myColumnResType; 048 private final DbColumn myColumnParamName; 049 private final DbColumn myColumnResId; 050 private final DbColumn myColumnHashIdentity; 051 052 @Autowired 053 private ISearchParamIdentityCacheSvc mySearchParamIdentityCacheSvc; 054 055 @VisibleForTesting 056 public void setSearchParamIdentityCacheSvcForUnitTest(ISearchParamIdentityCacheSvc theSearchParamIdentityCacheSvc) { 057 mySearchParamIdentityCacheSvc = theSearchParamIdentityCacheSvc; 058 } 059 060 public BaseSearchParamPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder, DbTable theTable) { 061 super(theSearchSqlBuilder, theTable); 062 063 myColumnResId = getTable().addColumn("RES_ID"); 064 myColumnMissing = theTable.addColumn("SP_MISSING"); 065 myColumnResType = theTable.addColumn("RES_TYPE"); 066 myColumnParamName = theTable.addColumn("SP_NAME"); 067 myColumnHashIdentity = theTable.addColumn("HASH_IDENTITY"); 068 } 069 070 public DbColumn getColumnHashIdentity() { 071 return myColumnHashIdentity; 072 } 073 074 public DbColumn getResourceTypeColumn() { 075 return myColumnResType; 076 } 077 078 public DbColumn getColumnParamName() { 079 return myColumnParamName; 080 } 081 082 public DbColumn getMissingColumn() { 083 return myColumnMissing; 084 } 085 086 @Override 087 public DbColumn getResourceIdColumn() { 088 return myColumnResId; 089 } 090 091 public Condition combineWithHashIdentityPredicate( 092 String theResourceName, String theParamName, Condition thePredicate) { 093 List<Condition> andPredicates = new ArrayList<>(); 094 095 Condition hashIdentityPredicate = createHashIdentityPredicate(theResourceName, theParamName); 096 andPredicates.add(hashIdentityPredicate); 097 andPredicates.add(thePredicate); 098 099 return QueryParameterUtils.toAndPredicate(andPredicates); 100 } 101 102 @Nonnull 103 public Condition createHashIdentityPredicate(String theResourceType, String theParamName) { 104 return createHashIdentityPredicate(getRequestPartitionId(), theResourceType, theParamName); 105 } 106 107 public Condition createHashIdentityPredicate( 108 RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName) { 109 long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity( 110 getPartitionSettings(), theRequestPartitionId, theResourceType, theParamName); 111 mySearchParamIdentityCacheSvc.findOrCreateSearchParamIdentity(hashIdentity, theResourceType, theParamName); 112 return BinaryCondition.equalTo(getColumnHashIdentity(), generatePlaceholder(hashIdentity)); 113 } 114 115 public Condition createPredicateParamMissingForNonReference( 116 String theResourceName, String theParamName, Boolean theMissing, RequestPartitionId theRequestPartitionId) { 117 118 List<Condition> conditions = new ArrayList<>(); 119 if (getStorageSettings().isIndexStorageOptimized()) { 120 conditions.add(createHashIdentityPredicate(theResourceName, theParamName)); 121 } else { 122 conditions.add(BinaryCondition.equalTo(getResourceTypeColumn(), generatePlaceholder(theResourceName))); 123 conditions.add(BinaryCondition.equalTo(getColumnParamName(), generatePlaceholder(theParamName))); 124 } 125 conditions.add(BinaryCondition.equalTo(getMissingColumn(), generatePlaceholder(theMissing))); 126 127 ComboCondition condition = ComboCondition.and(conditions.toArray()); 128 return combineWithRequestPartitionIdPredicate(theRequestPartitionId, condition); 129 } 130 131 @Override 132 public Condition createPredicateParamMissingValue(MissingQueryParameterPredicateParams theParams) { 133 SelectQuery subquery = new SelectQuery(); 134 subquery.addCustomColumns(1); 135 subquery.addFromTable(getTable()); 136 137 Condition hashIdentityPredicate = createHashIdentityPredicate( 138 theParams.getRequestPartitionId(), 139 theParams.getResourceTablePredicateBuilder().getResourceType(), 140 theParams.getParamName()); 141 142 Condition subQueryCondition = ComboCondition.and( 143 BinaryCondition.equalTo( 144 getResourceIdColumn(), 145 theParams.getResourceTablePredicateBuilder().getResourceIdColumn()), 146 hashIdentityPredicate); 147 148 subquery.addCondition(subQueryCondition); 149 150 Condition unaryCondition = UnaryCondition.exists(subquery); 151 if (theParams.isMissing()) { 152 unaryCondition = new NotCondition(unaryCondition); 153 } 154 155 return combineWithRequestPartitionIdPredicate(theParams.getRequestPartitionId(), unaryCondition); 156 } 157}