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