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.jpa.model.config.PartitionSettings; 023import ca.uhn.fhir.jpa.model.listener.IndexStorageOptimizationListener; 024import ca.uhn.fhir.model.api.IQueryParameterType; 025import ca.uhn.fhir.rest.param.QuantityParam; 026import jakarta.persistence.Column; 027import jakarta.persistence.Embeddable; 028import jakarta.persistence.Entity; 029import jakarta.persistence.EntityListeners; 030import jakarta.persistence.FetchType; 031import jakarta.persistence.ForeignKey; 032import jakarta.persistence.GeneratedValue; 033import jakarta.persistence.GenerationType; 034import jakarta.persistence.Id; 035import jakarta.persistence.Index; 036import jakarta.persistence.JoinColumn; 037import jakarta.persistence.ManyToOne; 038import jakarta.persistence.Table; 039import org.apache.commons.lang3.builder.EqualsBuilder; 040import org.apache.commons.lang3.builder.HashCodeBuilder; 041import org.apache.commons.lang3.builder.ToStringBuilder; 042import org.apache.commons.lang3.builder.ToStringStyle; 043import org.hibernate.annotations.GenericGenerator; 044import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumberField; 045 046import java.math.BigDecimal; 047import java.util.Objects; 048 049import static org.apache.commons.lang3.StringUtils.defaultString; 050import static org.apache.commons.lang3.StringUtils.isBlank; 051 052// @formatter:off 053@Embeddable 054@EntityListeners(IndexStorageOptimizationListener.class) 055@Entity 056@Table( 057 name = "HFJ_SPIDX_QUANTITY", 058 indexes = { 059 // We used to have an index named IDX_SP_QUANTITY - Dont reuse 060 @Index(name = "IDX_SP_QUANTITY_HASH_V2", columnList = "HASH_IDENTITY,SP_VALUE,RES_ID,PARTITION_ID"), 061 @Index( 062 name = "IDX_SP_QUANTITY_HASH_UN_V2", 063 columnList = "HASH_IDENTITY_AND_UNITS,SP_VALUE,RES_ID,PARTITION_ID"), 064 @Index( 065 name = "IDX_SP_QUANTITY_HASH_SYSUN_V2", 066 columnList = "HASH_IDENTITY_SYS_UNITS,SP_VALUE,RES_ID,PARTITION_ID"), 067 @Index( 068 name = "IDX_SP_QUANTITY_RESID_V2", 069 columnList = 070 "RES_ID,HASH_IDENTITY,HASH_IDENTITY_SYS_UNITS,HASH_IDENTITY_AND_UNITS,SP_VALUE,PARTITION_ID") 071 }) 072public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearchParamQuantity { 073 074 private static final long serialVersionUID = 1L; 075 076 @Id 077 @GenericGenerator( 078 name = "SEQ_SPIDX_QUANTITY", 079 type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) 080 @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_QUANTITY") 081 @Column(name = "SP_ID") 082 private Long myId; 083 084 @Column(name = "SP_VALUE", nullable = true) 085 @ScaledNumberField 086 public Double myValue; 087 088 @ManyToOne( 089 optional = false, 090 fetch = FetchType.LAZY, 091 cascade = {}) 092 @JoinColumn( 093 foreignKey = @ForeignKey(name = "FK_SP_QUANTITY_RES"), 094 name = "RES_ID", 095 referencedColumnName = "RES_ID", 096 nullable = false) 097 private ResourceTable myResource; 098 099 public ResourceIndexedSearchParamQuantity() { 100 super(); 101 } 102 103 public ResourceIndexedSearchParamQuantity( 104 PartitionSettings thePartitionSettings, 105 String theResourceType, 106 String theParamName, 107 BigDecimal theValue, 108 String theSystem, 109 String theUnits) { 110 this(); 111 setPartitionSettings(thePartitionSettings); 112 setResourceType(theResourceType); 113 setParamName(theParamName); 114 setSystem(theSystem); 115 setValue(theValue); 116 setUnits(theUnits); 117 calculateHashes(); 118 } 119 120 @Override 121 public <T extends BaseResourceIndex> void copyMutableValuesFrom(T theSource) { 122 super.copyMutableValuesFrom(theSource); 123 ResourceIndexedSearchParamQuantity source = (ResourceIndexedSearchParamQuantity) theSource; 124 mySystem = source.mySystem; 125 myUnits = source.myUnits; 126 myValue = source.myValue; 127 setHashIdentity(source.getHashIdentity()); 128 setHashIdentityAndUnits(source.getHashIdentityAndUnits()); 129 setHashIdentitySystemAndUnits(source.getHashIdentitySystemAndUnits()); 130 } 131 132 public BigDecimal getValue() { 133 return myValue != null ? new BigDecimal(myValue) : null; 134 } 135 136 public ResourceIndexedSearchParamQuantity setValue(BigDecimal theValue) { 137 myValue = theValue != null ? theValue.doubleValue() : null; 138 return this; 139 } 140 141 @Override 142 public Long getId() { 143 return myId; 144 } 145 146 @Override 147 public void setId(Long theId) { 148 myId = theId; 149 } 150 151 @Override 152 public IQueryParameterType toQueryParameterType() { 153 return new QuantityParam(null, getValue(), getSystem(), getUnits()); 154 } 155 156 @Override 157 public String toString() { 158 ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); 159 b.append("paramName", getParamName()); 160 b.append("resourceId", getResourcePid()); 161 b.append("system", getSystem()); 162 b.append("units", getUnits()); 163 b.append("value", getValue()); 164 b.append("missing", isMissing()); 165 b.append("hashIdentitySystemAndUnits", getHashIdentitySystemAndUnits()); 166 return b.build(); 167 } 168 169 @Override 170 public boolean equals(Object theObj) { 171 if (this == theObj) { 172 return true; 173 } 174 if (theObj == null) { 175 return false; 176 } 177 if (!(theObj instanceof ResourceIndexedSearchParamQuantity)) { 178 return false; 179 } 180 ResourceIndexedSearchParamQuantity obj = (ResourceIndexedSearchParamQuantity) theObj; 181 EqualsBuilder b = new EqualsBuilder(); 182 b.append(getHashIdentity(), obj.getHashIdentity()); 183 b.append(getHashIdentityAndUnits(), obj.getHashIdentityAndUnits()); 184 b.append(getHashIdentitySystemAndUnits(), obj.getHashIdentitySystemAndUnits()); 185 b.append(isMissing(), obj.isMissing()); 186 b.append(getValue(), obj.getValue()); 187 return b.isEquals(); 188 } 189 190 @Override 191 public int hashCode() { 192 HashCodeBuilder b = new HashCodeBuilder(); 193 b.append(getHashIdentity()); 194 b.append(getHashIdentityAndUnits()); 195 b.append(getHashIdentitySystemAndUnits()); 196 b.append(isMissing()); 197 b.append(getValue()); 198 return b.toHashCode(); 199 } 200 201 @Override 202 public boolean matches(IQueryParameterType theParam) { 203 204 if (!(theParam instanceof QuantityParam)) { 205 return false; 206 } 207 QuantityParam quantity = (QuantityParam) theParam; 208 boolean retval = false; 209 210 // Only match on system if it wasn't specified 211 String quantityUnitsString = defaultString(quantity.getUnits()); 212 if (quantity.getSystem() == null && isBlank(quantityUnitsString)) { 213 if (Objects.equals(getValue(), quantity.getValue())) { 214 retval = true; 215 } 216 } else { 217 String unitsString = defaultString(getUnits()); 218 if (quantity.getSystem() == null) { 219 if (unitsString.equalsIgnoreCase(quantityUnitsString) 220 && Objects.equals(getValue(), quantity.getValue())) { 221 retval = true; 222 } 223 } else if (isBlank(quantityUnitsString)) { 224 if (getSystem().equalsIgnoreCase(quantity.getSystem()) 225 && Objects.equals(getValue(), quantity.getValue())) { 226 retval = true; 227 } 228 } else { 229 if (getSystem().equalsIgnoreCase(quantity.getSystem()) 230 && unitsString.equalsIgnoreCase(quantityUnitsString) 231 && Objects.equals(getValue(), quantity.getValue())) { 232 retval = true; 233 } 234 } 235 } 236 237 return retval; 238 } 239 240 @Override 241 public ResourceTable getResource() { 242 return myResource; 243 } 244 245 @Override 246 public BaseResourceIndexedSearchParam setResource(ResourceTable theResource) { 247 myResource = theResource; 248 setResourceType(theResource.getResourceType()); 249 return this; 250 } 251}