001/*-
002 * #%L
003 * HAPI FHIR JPA Server
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.entity;
021
022import ca.uhn.fhir.jpa.model.dao.JpaPid;
023import ca.uhn.fhir.jpa.model.entity.AuditableBasePartitionable;
024import ca.uhn.fhir.jpa.model.entity.ResourceTable;
025import ca.uhn.fhir.mdm.api.IMdmLink;
026import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
027import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
028import com.fasterxml.jackson.annotation.JsonIgnore;
029import jakarta.persistence.Column;
030import jakarta.persistence.Entity;
031import jakarta.persistence.EnumType;
032import jakarta.persistence.Enumerated;
033import jakarta.persistence.FetchType;
034import jakarta.persistence.ForeignKey;
035import jakarta.persistence.GeneratedValue;
036import jakarta.persistence.GenerationType;
037import jakarta.persistence.Id;
038import jakarta.persistence.Index;
039import jakarta.persistence.JoinColumn;
040import jakarta.persistence.ManyToOne;
041import jakarta.persistence.SequenceGenerator;
042import jakarta.persistence.Table;
043import jakarta.persistence.Temporal;
044import jakarta.persistence.TemporalType;
045import jakarta.persistence.UniqueConstraint;
046import org.apache.commons.lang3.builder.ToStringBuilder;
047import org.hibernate.annotations.JdbcTypeCode;
048import org.hibernate.envers.AuditTable;
049import org.hibernate.envers.Audited;
050import org.hibernate.envers.NotAudited;
051import org.hibernate.type.SqlTypes;
052
053import java.util.Date;
054
055@Entity
056@Table(
057                name = "MPI_LINK",
058                uniqueConstraints = {
059                        // TODO GGG DROP this index, and instead use the below one
060                        @UniqueConstraint(
061                                        name = "IDX_EMPI_PERSON_TGT",
062                                        columnNames = {"PERSON_PID", "TARGET_PID"}),
063                        // TODO GGG Should i make individual indices for PERSON/TARGET?
064                },
065                indexes = {
066                        @Index(name = "IDX_EMPI_MATCH_TGT_VER", columnList = "MATCH_RESULT, TARGET_PID, VERSION"),
067                        // v---- this one
068                        @Index(name = "IDX_EMPI_GR_TGT", columnList = "GOLDEN_RESOURCE_PID, TARGET_PID"),
069                        @Index(name = "FK_EMPI_LINK_TARGET", columnList = "TARGET_PID"),
070                        // indexes for metrics
071                        @Index(name = "IDX_EMPI_TGT_MR_LS", columnList = "TARGET_TYPE, MATCH_RESULT, LINK_SOURCE"),
072                        @Index(name = "IDX_EMPI_TGT_MR_SCORE", columnList = "TARGET_TYPE, MATCH_RESULT, SCORE")
073                })
074@Audited
075// This is the table name generated by default by envers, but we set it explicitly for clarity
076@AuditTable("MPI_LINK_AUD")
077public class MdmLink extends AuditableBasePartitionable implements IMdmLink<JpaPid> {
078        public static final int VERSION_LENGTH = 16;
079        private static final int MATCH_RESULT_LENGTH = 16;
080        private static final int LINK_SOURCE_LENGTH = 16;
081        public static final int SOURCE_TYPE_LENGTH = 40;
082
083        @SequenceGenerator(name = "SEQ_EMPI_LINK_ID", sequenceName = "SEQ_EMPI_LINK_ID")
084        @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_EMPI_LINK_ID")
085        @Id
086        @Column(name = "PID")
087        private Long myId;
088
089        @ManyToOne(
090                        optional = false,
091                        fetch = FetchType.LAZY,
092                        cascade = {})
093        @JoinColumn(
094                        name = "GOLDEN_RESOURCE_PID",
095                        referencedColumnName = "RES_ID",
096                        foreignKey = @ForeignKey(name = "FK_EMPI_LINK_GOLDEN_RESOURCE"),
097                        insertable = false,
098                        updatable = false,
099                        nullable = false)
100        @NotAudited
101        private ResourceTable myGoldenResource;
102
103        @Column(name = "GOLDEN_RESOURCE_PID", nullable = false)
104        private Long myGoldenResourcePid;
105
106        @Deprecated
107        @ManyToOne(
108                        optional = false,
109                        fetch = FetchType.LAZY,
110                        cascade = {})
111        @JoinColumn(
112                        name = "PERSON_PID",
113                        referencedColumnName = "RES_ID",
114                        foreignKey = @ForeignKey(name = "FK_EMPI_LINK_PERSON"),
115                        insertable = false,
116                        updatable = false,
117                        nullable = false)
118        @NotAudited
119        private ResourceTable myPerson;
120
121        @Deprecated
122        @Column(name = "PERSON_PID", nullable = false)
123        private Long myPersonPid;
124
125        @ManyToOne(
126                        optional = false,
127                        fetch = FetchType.LAZY,
128                        cascade = {})
129        @JoinColumn(
130                        name = "TARGET_PID",
131                        referencedColumnName = "RES_ID",
132                        foreignKey = @ForeignKey(name = "FK_EMPI_LINK_TARGET"),
133                        insertable = false,
134                        updatable = false,
135                        nullable = false)
136        @NotAudited
137        private ResourceTable mySource;
138
139        @Column(name = "TARGET_PID", updatable = false, nullable = false)
140        private Long mySourcePid;
141
142        @Column(name = "MATCH_RESULT", nullable = false)
143        @Enumerated(EnumType.ORDINAL)
144        @JdbcTypeCode(SqlTypes.INTEGER)
145        private MdmMatchResultEnum myMatchResult;
146
147        @Column(name = "LINK_SOURCE", nullable = false)
148        @Enumerated(EnumType.ORDINAL)
149        @JdbcTypeCode(SqlTypes.INTEGER)
150        private MdmLinkSourceEnum myLinkSource;
151
152        @Temporal(TemporalType.TIMESTAMP)
153        @Column(name = "CREATED", nullable = false)
154        private Date myCreated;
155
156        @Temporal(TemporalType.TIMESTAMP)
157        @Column(name = "UPDATED", nullable = false)
158        private Date myUpdated;
159
160        @Column(name = "VERSION", nullable = false, length = VERSION_LENGTH)
161        private String myVersion;
162
163        /** This link was created as a result of an eid match **/
164        @Column(name = "EID_MATCH")
165        private Boolean myEidMatch;
166
167        /** This link created a new person **/
168        @Column(name = "NEW_PERSON")
169        private Boolean myHadToCreateNewGoldenResource;
170
171        @Column(name = "VECTOR")
172        @JsonIgnore
173        private Long myVector;
174
175        @Column(name = "SCORE")
176        private Double myScore;
177
178        // TODO GGG GL-1340
179        @Column(name = "RULE_COUNT")
180        private Long myRuleCount;
181
182        public MdmLink() {}
183
184        public MdmLink(String theVersion) {
185                myVersion = theVersion;
186        }
187
188        @Column(name = "TARGET_TYPE", nullable = true, length = SOURCE_TYPE_LENGTH)
189        private String myMdmSourceType;
190
191        @Override
192        public JpaPid getId() {
193                return JpaPid.fromId(myId);
194        }
195
196        @Override
197        public MdmLink setId(JpaPid theId) {
198                myId = theId.getId();
199                return this;
200        }
201
202        @Override
203        public JpaPid getGoldenResourcePersistenceId() {
204                return JpaPid.fromId(myGoldenResourcePid);
205        }
206
207        @Override
208        public IMdmLink setGoldenResourcePersistenceId(JpaPid theGoldenResourcePid) {
209                Long longPid = theGoldenResourcePid.getId();
210                setPersonPid(longPid);
211
212                myGoldenResourcePid = longPid;
213                return this;
214        }
215
216        @Override
217        public JpaPid getSourcePersistenceId() {
218                return JpaPid.fromId(mySourcePid);
219        }
220
221        @Override
222        public IMdmLink setSourcePersistenceId(JpaPid theSourcePid) {
223                mySourcePid = theSourcePid.getId();
224                return this;
225        }
226
227        public ResourceTable getGoldenResource() {
228                return myGoldenResource;
229        }
230
231        public MdmLink setGoldenResource(ResourceTable theGoldenResource) {
232                myGoldenResource = theGoldenResource;
233                myGoldenResourcePid = theGoldenResource.getId();
234
235                myPerson = theGoldenResource;
236                myPersonPid = theGoldenResource.getId();
237
238                return this;
239        }
240
241        @Deprecated
242        public Long getGoldenResourcePid() {
243                return myGoldenResourcePid;
244        }
245
246        /**
247         * @deprecated  Use {@link #setGoldenResourcePid(Long)} instead
248         */
249        @Deprecated
250        public MdmLink setPersonPid(Long thePersonPid) {
251                myPersonPid = thePersonPid;
252                return this;
253        }
254
255        /**
256         * @deprecated  Use {@link #setGoldenResourcePersistenceId(JpaPid)} instead
257         */
258        @Deprecated
259        public MdmLink setGoldenResourcePid(Long theGoldenResourcePid) {
260                setPersonPid(theGoldenResourcePid);
261
262                myGoldenResourcePid = theGoldenResourcePid;
263                return this;
264        }
265
266        public ResourceTable getSource() {
267                return mySource;
268        }
269
270        public MdmLink setSource(ResourceTable theSource) {
271                mySource = theSource;
272                mySourcePid = theSource.getId();
273                return this;
274        }
275
276        @Deprecated
277        public Long getSourcePid() {
278                return mySourcePid;
279        }
280
281        /**
282         * @deprecated  Use {@link #setSourcePersistenceId(JpaPid)} instead
283         */
284        @Deprecated
285        public MdmLink setSourcePid(Long theSourcePid) {
286                mySourcePid = theSourcePid;
287                return this;
288        }
289
290        @Override
291        public MdmMatchResultEnum getMatchResult() {
292                return myMatchResult;
293        }
294
295        @Override
296        public MdmLink setMatchResult(MdmMatchResultEnum theMatchResult) {
297                myMatchResult = theMatchResult;
298                return this;
299        }
300
301        @Override
302        public MdmLinkSourceEnum getLinkSource() {
303                return myLinkSource;
304        }
305
306        @Override
307        public MdmLink setLinkSource(MdmLinkSourceEnum theLinkSource) {
308                myLinkSource = theLinkSource;
309                return this;
310        }
311
312        @Override
313        public Date getCreated() {
314                return myCreated;
315        }
316
317        @Override
318        public MdmLink setCreated(Date theCreated) {
319                myCreated = theCreated;
320                return this;
321        }
322
323        @Override
324        public Date getUpdated() {
325                return myUpdated;
326        }
327
328        @Override
329        public MdmLink setUpdated(Date theUpdated) {
330                myUpdated = theUpdated;
331                return this;
332        }
333
334        @Override
335        public String getVersion() {
336                return myVersion;
337        }
338
339        @Override
340        public MdmLink setVersion(String theVersion) {
341                myVersion = theVersion;
342                return this;
343        }
344
345        @Override
346        public Long getVector() {
347                return myVector;
348        }
349
350        @Override
351        public MdmLink setVector(Long theVector) {
352                myVector = theVector;
353                return this;
354        }
355
356        @Override
357        public Double getScore() {
358                return myScore;
359        }
360
361        @Override
362        public MdmLink setScore(Double theScore) {
363                myScore = theScore;
364                return this;
365        }
366
367        public Boolean getEidMatch() {
368                return myEidMatch;
369        }
370
371        /**
372         * Note that this method can not be called <code>getEidMatch</code> or
373         * <code>isEidMatch</code> because Hibernate Search complains about having
374         * 2 accessors for this property
375         */
376        @Override
377        public Boolean isEidMatchPresent() {
378                return myEidMatch != null && myEidMatch;
379        }
380
381        @Override
382        public MdmLink setEidMatch(Boolean theEidMatch) {
383                myEidMatch = theEidMatch;
384                return this;
385        }
386
387        @Override
388        public Boolean getHadToCreateNewGoldenResource() {
389                return myHadToCreateNewGoldenResource != null && myHadToCreateNewGoldenResource;
390        }
391
392        @Override
393        public MdmLink setHadToCreateNewGoldenResource(Boolean theHadToCreateNewResource) {
394                myHadToCreateNewGoldenResource = theHadToCreateNewResource;
395                return this;
396        }
397
398        public MdmLink setMdmSourceType(String mdmSourceType) {
399                myMdmSourceType = mdmSourceType;
400                return this;
401        }
402
403        @Override
404        public String toString() {
405                return new ToStringBuilder(this)
406                                .append("myId", myId)
407                                .append("myGoldenResource", myGoldenResourcePid)
408                                .append("mySourcePid", mySourcePid)
409                                .append("myMdmSourceType", myMdmSourceType)
410                                .append("myMatchResult", myMatchResult)
411                                .append("myLinkSource", myLinkSource)
412                                .append("myEidMatch", myEidMatch)
413                                .append("myHadToCreateNewResource", myHadToCreateNewGoldenResource)
414                                .append("myScore", myScore)
415                                .append("myRuleCount", myRuleCount)
416                                .append("myPartitionId", getPartitionId())
417                                .toString();
418        }
419
420        public String getMdmSourceType() {
421                return myMdmSourceType;
422        }
423
424        public Long getRuleCount() {
425                return myRuleCount;
426        }
427
428        public MdmLink setRuleCount(Long theRuleCount) {
429                myRuleCount = theRuleCount;
430                return this;
431        }
432}