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