
001/* 002 * #%L 003 * HAPI FHIR JPA Model 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.model.entity; 021 022import ca.uhn.fhir.i18n.Msg; 023import ca.uhn.fhir.interceptor.model.RequestPartitionId; 024import ca.uhn.fhir.jpa.model.config.PartitionSettings; 025import ca.uhn.fhir.jpa.model.util.SearchParamHash; 026import ca.uhn.fhir.model.api.IQueryParameterType; 027import ca.uhn.fhir.rest.api.Constants; 028import jakarta.persistence.Column; 029import jakarta.persistence.MappedSuperclass; 030import jakarta.persistence.Temporal; 031import jakarta.persistence.TemporalType; 032import jakarta.persistence.Transient; 033import org.apache.commons.lang3.StringUtils; 034import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; 035import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; 036 037import java.util.Date; 038import java.util.List; 039 040import static org.apache.commons.lang3.StringUtils.isNotBlank; 041 042@MappedSuperclass 043public abstract class BaseResourceIndexedSearchParam extends BaseResourceIndex { 044 static final int MAX_SP_NAME = 100; 045 private static final long serialVersionUID = 1L; 046 047 @GenericField 048 @Column(name = "SP_MISSING", nullable = false) 049 private boolean myMissing = false; 050 051 @FullTextField 052 @Column(name = "SP_NAME", length = MAX_SP_NAME) 053 private String myParamName; 054 055 /** 056 * This is just a place to stash the {@link #myParamName} value if 057 * {@link #optimizeIndexStorage()} is called. 058 */ 059 @Transient 060 private transient String myParamNameCached; 061 062 @Column(name = "RES_ID", insertable = false, updatable = false, nullable = false) 063 private Long myResourcePid; 064 065 @FullTextField 066 @Column(name = "RES_TYPE", length = Constants.MAX_RESOURCE_NAME_LENGTH) 067 private String myResourceType; 068 069 /** 070 * Composite of resourceType, paramName, and partition info if configured. 071 * Combined with the various date fields for a query. 072 * Nullable to allow optimized storage. 073 */ 074 @Column(name = "HASH_IDENTITY", nullable = true) 075 protected Long myHashIdentity; 076 077 @GenericField 078 @Column(name = "SP_UPDATED", nullable = true) 079 @Temporal(TemporalType.TIMESTAMP) 080 private Date myUpdated; 081 082 @Transient 083 private transient PartitionSettings myPartitionSettings; 084 085 @Transient 086 private transient StorageSettings myStorageSettings; 087 088 @Override 089 public abstract Long getId(); 090 091 public String getParamName() { 092 if (myParamNameCached != null) { 093 return myParamNameCached; 094 } 095 return myParamName; 096 } 097 098 public void setParamName(String theName) { 099 if (!StringUtils.equals(myParamName, theName)) { 100 myParamName = theName; 101 clearHashes(); 102 } 103 } 104 105 /** 106 * Restore SP_NAME without clearing hashes 107 */ 108 public void restoreParamName(String theParamName) { 109 if (myParamName == null) { 110 myParamName = theParamName; 111 } 112 } 113 114 /** 115 * Set SP_NAME, RES_TYPE to null without clearing hashes 116 */ 117 public void optimizeIndexStorage() { 118 if (isNotBlank(myParamName)) { 119 myParamNameCached = myParamName; 120 myParamName = null; 121 } 122 myResourceType = null; 123 } 124 125 public boolean isIndexStorageOptimized() { 126 return myParamName == null || myResourceType == null; 127 } 128 129 // MB pushed these down to the individual SP classes so we could name the FK in the join annotation 130 /** 131 * Get the Resource this SP indexes 132 */ 133 public abstract ResourceTable getResource(); 134 135 public abstract BaseResourceIndexedSearchParam setResource(ResourceTable theResource); 136 137 @Override 138 public <T extends BaseResourceIndex> void copyMutableValuesFrom(T theSource) { 139 BaseResourceIndexedSearchParam source = (BaseResourceIndexedSearchParam) theSource; 140 myMissing = source.myMissing; 141 myParamName = source.myParamName; 142 myResourceType = source.myResourceType; 143 myUpdated = source.myUpdated; 144 myStorageSettings = source.myStorageSettings; 145 myPartitionSettings = source.myPartitionSettings; 146 setPartitionId(source.getPartitionId()); 147 } 148 149 public Long getResourcePid() { 150 return myResourcePid; 151 } 152 153 public String getResourceType() { 154 return myResourceType; 155 } 156 157 public void setResourceType(String theResourceType) { 158 myResourceType = theResourceType; 159 } 160 161 public void setHashIdentity(Long theHashIdentity) { 162 myHashIdentity = theHashIdentity; 163 } 164 165 public Long getHashIdentity() { 166 return myHashIdentity; 167 } 168 169 public Date getUpdated() { 170 return myUpdated; 171 } 172 173 public void setUpdated(Date theUpdated) { 174 myUpdated = theUpdated; 175 } 176 177 public boolean isMissing() { 178 return myMissing; 179 } 180 181 public BaseResourceIndexedSearchParam setMissing(boolean theMissing) { 182 myMissing = theMissing; 183 return this; 184 } 185 186 public abstract IQueryParameterType toQueryParameterType(); 187 188 public boolean matches(IQueryParameterType theParam) { 189 throw new UnsupportedOperationException(Msg.code(1526) + "No parameter matcher for " + theParam); 190 } 191 192 public PartitionSettings getPartitionSettings() { 193 return myPartitionSettings; 194 } 195 196 public BaseResourceIndexedSearchParam setPartitionSettings(PartitionSettings thePartitionSettings) { 197 myPartitionSettings = thePartitionSettings; 198 return this; 199 } 200 201 public StorageSettings getStorageSettings() { 202 return myStorageSettings; 203 } 204 205 public BaseResourceIndexedSearchParam setStorageSettings(StorageSettings theStorageSettings) { 206 myStorageSettings = theStorageSettings; 207 return this; 208 } 209 210 public static long calculateHashIdentity( 211 PartitionSettings thePartitionSettings, 212 PartitionablePartitionId theRequestPartitionId, 213 String theResourceType, 214 String theParamName) { 215 RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(theRequestPartitionId); 216 return calculateHashIdentity(thePartitionSettings, requestPartitionId, theResourceType, theParamName); 217 } 218 219 public static long calculateHashIdentity( 220 PartitionSettings thePartitionSettings, 221 RequestPartitionId theRequestPartitionId, 222 String theResourceType, 223 String theParamName) { 224 return SearchParamHash.hashSearchParam( 225 thePartitionSettings, theRequestPartitionId, theResourceType, theParamName); 226 } 227 228 public static long calculateHashIdentity( 229 PartitionSettings thePartitionSettings, 230 RequestPartitionId theRequestPartitionId, 231 String theResourceType, 232 String theParamName, 233 List<String> theAdditionalValues) { 234 String[] values = new String[theAdditionalValues.size() + 2]; 235 values[0] = theResourceType; 236 values[1] = theParamName; 237 for (int i = 0; i < theAdditionalValues.size(); i++) { 238 values[i + 2] = theAdditionalValues.get(i); 239 } 240 241 return SearchParamHash.hashSearchParam(thePartitionSettings, theRequestPartitionId, values); 242 } 243}