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.util.UcumServiceUtil; 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.FetchType; 030import jakarta.persistence.ForeignKey; 031import jakarta.persistence.GeneratedValue; 032import jakarta.persistence.GenerationType; 033import jakarta.persistence.Id; 034import jakarta.persistence.Index; 035import jakarta.persistence.JoinColumn; 036import jakarta.persistence.ManyToOne; 037import jakarta.persistence.SequenceGenerator; 038import jakarta.persistence.Table; 039import org.apache.commons.lang3.builder.EqualsBuilder; 040import org.apache.commons.lang3.builder.ToStringBuilder; 041import org.apache.commons.lang3.builder.ToStringStyle; 042import org.fhir.ucum.Pair; 043import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumberField; 044 045import java.math.BigDecimal; 046import java.util.Objects; 047 048import static org.apache.commons.lang3.StringUtils.defaultString; 049import static org.apache.commons.lang3.StringUtils.isBlank; 050 051// @formatter:off 052@Embeddable 053@Entity 054@Table( 055 name = "HFJ_SPIDX_QUANTITY_NRML", 056 indexes = { 057 @Index(name = "IDX_SP_QNTY_NRML_HASH_V2", columnList = "HASH_IDENTITY,SP_VALUE,RES_ID,PARTITION_ID"), 058 @Index( 059 name = "IDX_SP_QNTY_NRML_HASH_UN_V2", 060 columnList = "HASH_IDENTITY_AND_UNITS,SP_VALUE,RES_ID,PARTITION_ID"), 061 @Index( 062 name = "IDX_SP_QNTY_NRML_HASH_SYSUN_V2", 063 columnList = "HASH_IDENTITY_SYS_UNITS,SP_VALUE,RES_ID,PARTITION_ID"), 064 @Index( 065 name = "IDX_SP_QNTY_NRML_RESID_V2", 066 columnList = 067 "RES_ID,HASH_IDENTITY,HASH_IDENTITY_SYS_UNITS,HASH_IDENTITY_AND_UNITS,SP_VALUE,PARTITION_ID") 068 }) 069/** 070 * Support UCUM service 071 * @since 5.3.0 072 * 073 */ 074public class ResourceIndexedSearchParamQuantityNormalized extends BaseResourceIndexedSearchParamQuantity { 075 076 private static final long serialVersionUID = 1L; 077 078 @Id 079 @SequenceGenerator(name = "SEQ_SPIDX_QUANTITY_NRML", sequenceName = "SEQ_SPIDX_QUANTITY_NRML") 080 @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_QUANTITY_NRML") 081 @Column(name = "SP_ID") 082 private Long myId; 083 084 // Changed to double here for storing the value after converted to the CanonicalForm due to BigDecimal maps 085 // NUMBER(19,2) 086 // The precision may lost even to store 1.2cm which is 0.012m in the CanonicalForm 087 @Column(name = "SP_VALUE", nullable = true) 088 @ScaledNumberField 089 public Double myValue; 090 091 @ManyToOne( 092 optional = false, 093 fetch = FetchType.LAZY, 094 cascade = {}) 095 @JoinColumn( 096 foreignKey = @ForeignKey(name = "FK_SP_QUANTITYNM_RES"), 097 name = "RES_ID", 098 referencedColumnName = "RES_ID", 099 nullable = false) 100 private ResourceTable myResource; 101 102 public ResourceIndexedSearchParamQuantityNormalized() { 103 super(); 104 } 105 106 public ResourceIndexedSearchParamQuantityNormalized( 107 PartitionSettings thePartitionSettings, 108 String theResourceType, 109 String theParamName, 110 double theValue, 111 String theSystem, 112 String theUnits) { 113 this(); 114 setPartitionSettings(thePartitionSettings); 115 setResourceType(theResourceType); 116 setParamName(theParamName); 117 setSystem(theSystem); 118 setValue(theValue); 119 setUnits(theUnits); 120 calculateHashes(); 121 } 122 123 @Override 124 public <T extends BaseResourceIndex> void copyMutableValuesFrom(T theSource) { 125 super.copyMutableValuesFrom(theSource); 126 ResourceIndexedSearchParamQuantityNormalized source = (ResourceIndexedSearchParamQuantityNormalized) theSource; 127 mySystem = source.mySystem; 128 myUnits = source.myUnits; 129 myValue = source.myValue; 130 setHashIdentity(source.getHashIdentity()); 131 setHashIdentityAndUnits(source.getHashIdentityAndUnits()); 132 setHashIdentitySystemAndUnits(source.getHashIdentitySystemAndUnits()); 133 } 134 135 // - myValue 136 public Double getValue() { 137 return myValue; 138 } 139 140 public ResourceIndexedSearchParamQuantityNormalized setValue(Double theValue) { 141 myValue = theValue; 142 return this; 143 } 144 145 public ResourceIndexedSearchParamQuantityNormalized setValue(double theValue) { 146 myValue = theValue; 147 return this; 148 } 149 150 // -- myId 151 @Override 152 public Long getId() { 153 return myId; 154 } 155 156 @Override 157 public void setId(Long theId) { 158 myId = theId; 159 } 160 161 @Override 162 public IQueryParameterType toQueryParameterType() { 163 return new QuantityParam(null, getValue(), getSystem(), getUnits()); 164 } 165 166 @Override 167 public String toString() { 168 ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); 169 b.append("paramName", getParamName()); 170 b.append("resourceId", getResourcePid()); 171 b.append("system", getSystem()); 172 b.append("units", getUnits()); 173 b.append("value", getValue()); 174 b.append("missing", isMissing()); 175 b.append("hashIdentitySystemAndUnits", getHashIdentitySystemAndUnits()); 176 return b.build(); 177 } 178 179 @Override 180 public boolean equals(Object theObj) { 181 if (this == theObj) { 182 return true; 183 } 184 if (theObj == null) { 185 return false; 186 } 187 if (!(theObj instanceof ResourceIndexedSearchParamQuantityNormalized)) { 188 return false; 189 } 190 ResourceIndexedSearchParamQuantityNormalized obj = (ResourceIndexedSearchParamQuantityNormalized) theObj; 191 EqualsBuilder b = new EqualsBuilder(); 192 b.append(getResourceType(), obj.getResourceType()); 193 b.append(getParamName(), obj.getParamName()); 194 b.append(getHashIdentity(), obj.getHashIdentity()); 195 b.append(getHashIdentityAndUnits(), obj.getHashIdentityAndUnits()); 196 b.append(getHashIdentitySystemAndUnits(), obj.getHashIdentitySystemAndUnits()); 197 b.append(isMissing(), obj.isMissing()); 198 b.append(getValue(), obj.getValue()); 199 return b.isEquals(); 200 } 201 202 @Override 203 public boolean matches(IQueryParameterType theParam) { 204 205 if (!(theParam instanceof QuantityParam)) { 206 return false; 207 } 208 QuantityParam quantity = (QuantityParam) theParam; 209 boolean retval = false; 210 211 String quantitySystem = quantity.getSystem(); 212 BigDecimal quantityValue = quantity.getValue(); 213 Double quantityDoubleValue = null; 214 if (quantityValue != null) quantityDoubleValue = quantityValue.doubleValue(); 215 String quantityUnits = defaultString(quantity.getUnits()); 216 217 // -- convert the value/unit to the canonical form if any, otherwise store the original value/units pair 218 Pair canonicalForm = UcumServiceUtil.getCanonicalForm(quantitySystem, quantityValue, quantityUnits); 219 if (canonicalForm != null) { 220 quantityDoubleValue = Double.parseDouble(canonicalForm.getValue().asDecimal()); 221 quantityUnits = canonicalForm.getCode(); 222 } 223 224 // Only match on system if it wasn't specified 225 if (quantitySystem == null && isBlank(quantityUnits)) { 226 if (Objects.equals(getValue(), quantityDoubleValue)) { 227 retval = true; 228 } 229 } else { 230 String unitsString = defaultString(getUnits()); 231 if (quantitySystem == null) { 232 if (unitsString.equalsIgnoreCase(quantityUnits) && Objects.equals(getValue(), quantityDoubleValue)) { 233 retval = true; 234 } 235 } else if (isBlank(quantityUnits)) { 236 if (getSystem().equalsIgnoreCase(quantitySystem) && Objects.equals(getValue(), quantityDoubleValue)) { 237 retval = true; 238 } 239 } else { 240 if (getSystem().equalsIgnoreCase(quantitySystem) 241 && unitsString.equalsIgnoreCase(quantityUnits) 242 && Objects.equals(getValue(), quantityDoubleValue)) { 243 retval = true; 244 } 245 } 246 } 247 248 return retval; 249 } 250 251 @Override 252 public ResourceTable getResource() { 253 return myResource; 254 } 255 256 @Override 257 public BaseResourceIndexedSearchParam setResource(ResourceTable theResource) { 258 myResource = theResource; 259 setResourceType(theResource.getResourceType()); 260 return this; 261 } 262}