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.interceptor.model.RequestPartitionId;
023import ca.uhn.fhir.jpa.model.config.PartitionSettings;
024import ca.uhn.fhir.jpa.model.util.SearchParamHash;
025import jakarta.persistence.Column;
026import jakarta.persistence.Entity;
027import jakarta.persistence.FetchType;
028import jakarta.persistence.ForeignKey;
029import jakarta.persistence.GeneratedValue;
030import jakarta.persistence.GenerationType;
031import jakarta.persistence.Id;
032import jakarta.persistence.IdClass;
033import jakarta.persistence.Index;
034import jakarta.persistence.JoinColumn;
035import jakarta.persistence.JoinColumns;
036import jakarta.persistence.ManyToOne;
037import jakarta.persistence.Table;
038import jakarta.persistence.Transient;
039import org.apache.commons.lang3.builder.CompareToBuilder;
040import org.apache.commons.lang3.builder.EqualsBuilder;
041import org.apache.commons.lang3.builder.HashCodeBuilder;
042import org.apache.commons.lang3.builder.ToStringBuilder;
043import org.hibernate.annotations.GenericGenerator;
044
045@Entity
046@Table(
047                name = ResourceIndexedComboTokenNonUnique.HFJ_IDX_CMB_TOK_NU,
048                indexes = {
049                        // TODO: The hash index was added in 7.4.0 - In 7.6.0 we should drop the string index
050                        @Index(name = "IDX_IDXCMBTOKNU_STR", columnList = "IDX_STRING", unique = false),
051                        @Index(name = "IDX_IDXCMBTOKNU_HASHC", columnList = "HASH_COMPLETE,RES_ID,PARTITION_ID", unique = false),
052                        @Index(name = "IDX_IDXCMBTOKNU_RES", columnList = "RES_ID", unique = false)
053                })
054@IdClass(IdAndPartitionId.class)
055public class ResourceIndexedComboTokenNonUnique extends BaseResourceIndexedCombo
056                implements Comparable<ResourceIndexedComboTokenNonUnique>, IResourceIndexComboSearchParameter {
057
058        public static final String HFJ_IDX_CMB_TOK_NU = "HFJ_IDX_CMB_TOK_NU";
059
060        @GenericGenerator(
061                        name = "SEQ_IDXCMBTOKNU_ID",
062                        type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class)
063        @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_IDXCMBTOKNU_ID")
064        @Id
065        @Column(name = "PID")
066        private Long myId;
067
068        @ManyToOne(
069                        optional = false,
070                        fetch = FetchType.LAZY,
071                        cascade = {})
072        @JoinColumns(
073                        value = {
074                                @JoinColumn(
075                                                name = "RES_ID",
076                                                referencedColumnName = "RES_ID",
077                                                insertable = false,
078                                                updatable = false,
079                                                nullable = true),
080                                @JoinColumn(
081                                                name = "PARTITION_ID",
082                                                referencedColumnName = "PARTITION_ID",
083                                                insertable = false,
084                                                updatable = false,
085                                                nullable = true)
086                        },
087                        foreignKey = @ForeignKey(name = "FK_IDXCMBTOKNU_RES_ID"))
088        private ResourceTable myResource;
089
090        @Column(name = "RES_ID", updatable = false, nullable = true)
091        private Long myResourceId;
092
093        @Column(name = "HASH_COMPLETE", nullable = false)
094        private Long myHashComplete;
095
096        @Column(name = "IDX_STRING", nullable = false, length = ResourceIndexedComboStringUnique.MAX_STRING_LENGTH)
097        private String myIndexString;
098
099        @Transient
100        private transient PartitionSettings myPartitionSettings;
101
102        /**
103         * Constructor
104         */
105        public ResourceIndexedComboTokenNonUnique() {
106                super();
107        }
108
109        public ResourceIndexedComboTokenNonUnique(
110                        PartitionSettings thePartitionSettings, ResourceTable theEntity, String theQueryString) {
111                myPartitionSettings = thePartitionSettings;
112                myResource = theEntity;
113                myIndexString = theQueryString;
114                calculateHashes();
115        }
116
117        @Override
118        public String getIndexString() {
119                return myIndexString;
120        }
121
122        public void setIndexString(String theIndexString) {
123                myIndexString = theIndexString;
124        }
125
126        @Override
127        public boolean equals(Object theO) {
128                calculateHashes();
129
130                if (this == theO) {
131                        return true;
132                }
133
134                if (theO == null || getClass() != theO.getClass()) {
135                        return false;
136                }
137
138                ResourceIndexedComboTokenNonUnique that = (ResourceIndexedComboTokenNonUnique) theO;
139
140                EqualsBuilder b = new EqualsBuilder();
141                b.append(getHashComplete(), that.getHashComplete());
142                return b.isEquals();
143        }
144
145        @Override
146        public <T extends BaseResourceIndex> void copyMutableValuesFrom(T theSource) {
147                ResourceIndexedComboTokenNonUnique source = (ResourceIndexedComboTokenNonUnique) theSource;
148                myPartitionSettings = source.myPartitionSettings;
149                myHashComplete = source.myHashComplete;
150                myIndexString = source.myIndexString;
151        }
152
153        @Override
154        public void setResourceId(Long theResourceId) {
155                myResourceId = theResourceId;
156        }
157
158        @Override
159        public Long getId() {
160                return myId;
161        }
162
163        @Override
164        public void setId(Long theId) {
165                myId = theId;
166        }
167
168        @Override
169        public void clearHashes() {
170                myHashComplete = null;
171        }
172
173        @Override
174        public void calculateHashes() {
175                if (myHashComplete != null) {
176                        return;
177                }
178
179                PartitionSettings partitionSettings = getPartitionSettings();
180                PartitionablePartitionId partitionId = getPartitionId();
181                String queryString = myIndexString;
182                setHashComplete(calculateHashComplete(partitionSettings, partitionId, queryString));
183        }
184
185        @Override
186        public int hashCode() {
187                calculateHashes();
188
189                HashCodeBuilder builder = new HashCodeBuilder(17, 37);
190                builder.append(getHashComplete());
191                return builder.toHashCode();
192        }
193
194        public PartitionSettings getPartitionSettings() {
195                return myPartitionSettings;
196        }
197
198        public void setPartitionSettings(PartitionSettings thePartitionSettings) {
199                myPartitionSettings = thePartitionSettings;
200        }
201
202        @Override
203        public ResourceTable getResource() {
204                return myResource;
205        }
206
207        @Override
208        public void setResource(ResourceTable theResource) {
209                myResource = theResource;
210        }
211
212        public Long getHashComplete() {
213                return myHashComplete;
214        }
215
216        public void setHashComplete(Long theHashComplete) {
217                myHashComplete = theHashComplete;
218        }
219
220        @Override
221        public int compareTo(ResourceIndexedComboTokenNonUnique theO) {
222                CompareToBuilder b = new CompareToBuilder();
223                b.append(myHashComplete, theO.getHashComplete());
224                return b.toComparison();
225        }
226
227        @Override
228        public String toString() {
229                return new ToStringBuilder(this)
230                                .append("id", myId)
231                                .append("resourceId", myResourceId)
232                                .append("hashComplete", myHashComplete)
233                                .append("indexString", myIndexString)
234                                .toString();
235        }
236
237        public static long calculateHashComplete(
238                        PartitionSettings partitionSettings, PartitionablePartitionId thePartitionId, String queryString) {
239                RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(thePartitionId);
240                return SearchParamHash.hashSearchParam(partitionSettings, requestPartitionId, queryString);
241        }
242}