001package ca.uhn.fhir.jpa.model.entity; 002 003/* 004 * #%L 005 * HAPI FHIR JPA Model 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.model.config.PartitionSettings; 025import ca.uhn.fhir.model.api.IQueryParameterType; 026import ca.uhn.fhir.rest.api.Constants; 027import ca.uhn.fhir.rest.param.TokenParam; 028import org.apache.commons.lang3.StringUtils; 029import org.apache.commons.lang3.builder.EqualsBuilder; 030import org.apache.commons.lang3.builder.HashCodeBuilder; 031import org.apache.commons.lang3.builder.ToStringBuilder; 032import org.apache.commons.lang3.builder.ToStringStyle; 033import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; 034 035import javax.persistence.Column; 036import javax.persistence.Embeddable; 037import javax.persistence.Entity; 038import javax.persistence.GeneratedValue; 039import javax.persistence.GenerationType; 040import javax.persistence.Id; 041import javax.persistence.Index; 042import javax.persistence.SequenceGenerator; 043import javax.persistence.Table; 044 045import static org.apache.commons.lang3.StringUtils.defaultString; 046import static org.apache.commons.lang3.StringUtils.trim; 047 048@Embeddable 049@Entity 050@Table(name = "HFJ_SPIDX_TOKEN", indexes = { 051 /* 052 * Note: We previously had indexes with the following names, 053 * do not reuse these names: 054 * IDX_SP_TOKEN 055 * IDX_SP_TOKEN_UNQUAL 056 */ 057 058 // TODO PERF Recommend to drop this index (added by JA - I don't actually think we even need the identity hash for this type, we could potentially drop the column too): 059 @Index(name = "IDX_SP_TOKEN_HASH", columnList = "HASH_IDENTITY"), 060 @Index(name = "IDX_SP_TOKEN_HASH_S", columnList = "HASH_SYS"), 061 @Index(name = "IDX_SP_TOKEN_HASH_SV", columnList = "HASH_SYS_AND_VALUE"), 062 // TODO PERF change this to: 063 // @Index(name = "IDX_SP_TOKEN_HASH_V", columnList = "HASH_VALUE,RES_ID"), 064 @Index(name = "IDX_SP_TOKEN_HASH_V", columnList = "HASH_VALUE"), 065 066 @Index(name = "IDX_SP_TOKEN_UPDATED", columnList = "SP_UPDATED"), 067 @Index(name = "IDX_SP_TOKEN_RESID", columnList = "RES_ID") 068}) 069public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchParam { 070 071 public static final int MAX_LENGTH = 200; 072 073 private static final long serialVersionUID = 1L; 074 075 @FullTextField 076 @Column(name = "SP_SYSTEM", nullable = true, length = MAX_LENGTH) 077 public String mySystem; 078 079 @FullTextField 080 @Column(name = "SP_VALUE", nullable = true, length = MAX_LENGTH) 081 private String myValue; 082 083 @SuppressWarnings("unused") 084 @Id 085 @SequenceGenerator(name = "SEQ_SPIDX_TOKEN", sequenceName = "SEQ_SPIDX_TOKEN") 086 @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_TOKEN") 087 @Column(name = "SP_ID") 088 private Long myId; 089 /** 090 * @since 3.4.0 - At some point this should be made not-null 091 */ 092 @Column(name = "HASH_IDENTITY", nullable = true) 093 private Long myHashIdentity; 094 /** 095 * @since 3.4.0 - At some point this should be made not-null 096 */ 097 @Column(name = "HASH_SYS", nullable = true) 098 private Long myHashSystem; 099 /** 100 * @since 3.4.0 - At some point this should be made not-null 101 */ 102 @Column(name = "HASH_SYS_AND_VALUE", nullable = true) 103 private Long myHashSystemAndValue; 104 /** 105 * @since 3.4.0 - At some point this should be made not-null 106 */ 107 @Column(name = "HASH_VALUE", nullable = true) 108 private Long myHashValue; 109 110 111 /** 112 * Constructor 113 */ 114 public ResourceIndexedSearchParamToken() { 115 super(); 116 } 117 118 /** 119 * Constructor 120 */ 121 public ResourceIndexedSearchParamToken(PartitionSettings thePartitionSettings, String theResourceType, String theParamName, String theSystem, String theValue) { 122 super(); 123 setPartitionSettings(thePartitionSettings); 124 setResourceType(theResourceType); 125 setParamName(theParamName); 126 setSystem(theSystem); 127 setValue(theValue); 128 calculateHashes(); 129 } 130 131 @Override 132 public <T extends BaseResourceIndex> void copyMutableValuesFrom(T theSource) { 133 super.copyMutableValuesFrom(theSource); 134 ResourceIndexedSearchParamToken source = (ResourceIndexedSearchParamToken) theSource; 135 136 mySystem = source.mySystem; 137 myValue = source.myValue; 138 myHashSystem = source.myHashSystem; 139 myHashSystemAndValue = source.getHashSystemAndValue(); 140 myHashValue = source.myHashValue; 141 myHashIdentity = source.myHashIdentity; 142 } 143 144 @Override 145 public void clearHashes() { 146 myHashIdentity = null; 147 myHashSystem = null; 148 myHashSystemAndValue = null; 149 myHashValue = null; 150 } 151 152 153 @Override 154 public void calculateHashes() { 155 if (myHashIdentity != null || myHashSystem != null || myHashValue != null || myHashSystemAndValue != null) { 156 return; 157 } 158 159 String resourceType = getResourceType(); 160 String paramName = getParamName(); 161 String system = getSystem(); 162 String value = getValue(); 163 setHashIdentity(calculateHashIdentity(getPartitionSettings(), getPartitionId(), resourceType, paramName)); 164 setHashSystemAndValue(calculateHashSystemAndValue(getPartitionSettings(), getPartitionId(), resourceType, paramName, system, value)); 165 166 // Searches using the :of-type modifier can never be partial (system-only or value-only) so don't 167 // bother saving these 168 boolean calculatePartialHashes = !StringUtils.endsWith(paramName, Constants.PARAMQUALIFIER_TOKEN_OF_TYPE); 169 if (calculatePartialHashes) { 170 setHashSystem(calculateHashSystem(getPartitionSettings(), getPartitionId(), resourceType, paramName, system)); 171 setHashValue(calculateHashValue(getPartitionSettings(), getPartitionId(), resourceType, paramName, value)); 172 } 173 } 174 175 @Override 176 public boolean equals(Object theObj) { 177 if (this == theObj) { 178 return true; 179 } 180 if (theObj == null) { 181 return false; 182 } 183 if (!(theObj instanceof ResourceIndexedSearchParamToken)) { 184 return false; 185 } 186 ResourceIndexedSearchParamToken obj = (ResourceIndexedSearchParamToken) theObj; 187 EqualsBuilder b = new EqualsBuilder(); 188 b.append(getHashSystem(), obj.getHashSystem()); 189 b.append(getHashValue(), obj.getHashValue()); 190 b.append(getHashSystemAndValue(), obj.getHashSystemAndValue()); 191 return b.isEquals(); 192 } 193 194 public Long getHashSystem() { 195 return myHashSystem; 196 } 197 198 private void setHashSystem(Long theHashSystem) { 199 myHashSystem = theHashSystem; 200 } 201 202 private void setHashIdentity(Long theHashIdentity) { 203 myHashIdentity = theHashIdentity; 204 } 205 206 public Long getHashSystemAndValue() { 207 return myHashSystemAndValue; 208 } 209 210 private void setHashSystemAndValue(Long theHashSystemAndValue) { 211 myHashSystemAndValue = theHashSystemAndValue; 212 } 213 214 public Long getHashValue() { 215 return myHashValue; 216 } 217 218 private void setHashValue(Long theHashValue) { 219 myHashValue = theHashValue; 220 } 221 222 @Override 223 public Long getId() { 224 return myId; 225 } 226 227 @Override 228 public void setId(Long theId) { 229 myId = theId; 230 } 231 232 public String getSystem() { 233 return mySystem; 234 } 235 236 public void setSystem(String theSystem) { 237 mySystem = StringUtils.defaultIfBlank(theSystem, null); 238 myHashSystemAndValue = null; 239 } 240 241 public String getValue() { 242 return myValue; 243 } 244 245 public ResourceIndexedSearchParamToken setValue(String theValue) { 246 myValue = StringUtils.defaultIfBlank(theValue, null); 247 myHashSystemAndValue = null; 248 return this; 249 } 250 251 @Override 252 public int hashCode() { 253 HashCodeBuilder b = new HashCodeBuilder(); 254 b.append(getResourceType()); 255 b.append(getHashValue()); 256 b.append(getHashSystem()); 257 b.append(getHashSystemAndValue()); 258 259 return b.toHashCode(); 260 } 261 262 @Override 263 public IQueryParameterType toQueryParameterType() { 264 return new TokenParam(getSystem(), getValue()); 265 } 266 267 @Override 268 public String toString() { 269 ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); 270 if (getPartitionId() != null) { 271 b.append("partitionId", getPartitionId().getPartitionId()); 272 } 273 b.append("resourceType", getResourceType()); 274 b.append("paramName", getParamName()); 275 if (isMissing()) { 276 b.append("missing", true); 277 } else { 278 b.append("system", getSystem()); 279 b.append("value", getValue()); 280 } 281 b.append("hashIdentity", myHashIdentity); 282 b.append("hashSystem", myHashSystem); 283 b.append("hashValue", myHashValue); 284 b.append("hashSysAndValue", myHashSystemAndValue); 285 b.append("partition", getPartitionId()); 286 return b.build(); 287 } 288 289 @Override 290 public boolean matches(IQueryParameterType theParam) { 291 if (!(theParam instanceof TokenParam)) { 292 return false; 293 } 294 TokenParam token = (TokenParam) theParam; 295 boolean retVal = false; 296 String valueString = defaultString(getValue()); 297 String tokenValueString = defaultString(token.getValue()); 298 299 // Only match on system if it wasn't specified 300 if (token.getSystem() == null || token.getSystem().isEmpty()) { 301 if (valueString.equalsIgnoreCase(tokenValueString)) { 302 retVal = true; 303 } 304 } else if (tokenValueString == null || tokenValueString.isEmpty()) { 305 if (token.getSystem().equalsIgnoreCase(getSystem())) { 306 retVal = true; 307 } 308 } else { 309 if (token.getSystem().equalsIgnoreCase(getSystem()) && 310 valueString.equalsIgnoreCase(tokenValueString)) { 311 retVal = true; 312 } 313 } 314 return retVal; 315 } 316 317 318 public static long calculateHashSystem(PartitionSettings thePartitionSettings, PartitionablePartitionId theRequestPartitionId, String theResourceType, String theParamName, String theSystem) { 319 RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(theRequestPartitionId); 320 return calculateHashSystem(thePartitionSettings, requestPartitionId, theResourceType, theParamName, theSystem); 321 } 322 323 public static long calculateHashSystem(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theSystem) { 324 return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, trim(theSystem)); 325 } 326 327 public static long calculateHashSystemAndValue(PartitionSettings thePartitionSettings, PartitionablePartitionId theRequestPartitionId, String theResourceType, String theParamName, String theSystem, String theValue) { 328 RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(theRequestPartitionId); 329 return calculateHashSystemAndValue(thePartitionSettings, requestPartitionId, theResourceType, theParamName, theSystem, theValue); 330 } 331 332 public static long calculateHashSystemAndValue(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theSystem, String theValue) { 333 return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, defaultString(trim(theSystem)), trim(theValue)); 334 } 335 336 public static long calculateHashValue(PartitionSettings thePartitionSettings, PartitionablePartitionId theRequestPartitionId, String theResourceType, String theParamName, String theValue) { 337 RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(theRequestPartitionId); 338 return calculateHashValue(thePartitionSettings, requestPartitionId, theResourceType, theParamName, theValue); 339 } 340 341 public static long calculateHashValue(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theValue) { 342 String value = trim(theValue); 343 return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, value); 344 } 345 346 347}