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