example = Example.of(exampleLink);
return myMdmLinkDao.findOne(example);
}
+
/**
* Delete a given {@link IMdmLink}. Note that this does not clear out the Golden resource.
* It is a simple entity delete.
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java
index 092b9cc4d25..b1e15c91a40 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java
@@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc;
import ca.uhn.fhir.mdm.api.IMdmLink;
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
+import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
@@ -81,6 +82,9 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
@Autowired
IInterceptorBroadcaster myInterceptorBroadcaster;
+ @Autowired
+ private IMdmSurvivorshipService myMdmSurvivorshipService;
+
@Override
@Transactional
public IAnyResource mergeGoldenResources(MdmMergeGoldenResourcesParams theParams) {
@@ -105,7 +109,8 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
.getResource();
} else {
myGoldenResourceHelper.mergeIndentifierFields(fromGoldenResource, toGoldenResource, mdmTransactionContext);
- myGoldenResourceHelper.mergeNonIdentiferFields(fromGoldenResource, toGoldenResource, mdmTransactionContext);
+ myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(
+ fromGoldenResource, toGoldenResource, mdmTransactionContext);
// Save changes to the golden resource
myMdmResourceDaoSvc.upsertGoldenResource(toGoldenResource, resourceType);
}
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java
index 36a8f201d70..6ea10e08c9f 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java
@@ -34,10 +34,10 @@ import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
import ca.uhn.fhir.mdm.api.IMdmLinkCreateSvc;
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc;
-import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
-import ca.uhn.fhir.mdm.api.MdmQuerySearchParameters;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
+import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters;
+import ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters;
import ca.uhn.fhir.mdm.batch2.clear.MdmClearAppCtx;
import ca.uhn.fhir.mdm.batch2.clear.MdmClearJobParameters;
import ca.uhn.fhir.mdm.batch2.submit.MdmSubmitAppCtx;
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java
index c442a2e3b1e..95e445012d3 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java
@@ -180,8 +180,8 @@ public class MdmEidUpdateService {
log(
theMdmTransactionContext,
"Duplicate detected based on the fact that both resources have different external EIDs.");
- IAnyResource newGoldenResource =
- myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource(theResource, theMdmTransactionContext);
+ IAnyResource newGoldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource(
+ theResource, theMdmTransactionContext, myMdmSurvivorshipService);
myMdmLinkSvc.updateLink(
newGoldenResource,
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java
index 740cf046356..c638fbb5b35 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java
@@ -22,12 +22,12 @@ package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.mdm.api.IMdmLink;
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
-import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmLinkWithRevision;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
-import ca.uhn.fhir.mdm.api.MdmQuerySearchParameters;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
+import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters;
+import ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson;
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java
index 8c721bf0b6c..96892d37965 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java
@@ -141,16 +141,7 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
// Add partition for the mdm link if it doesn't exist
- RequestPartitionId goldenResourcePartitionId =
- (RequestPartitionId) goldenResource.getUserData(Constants.RESOURCE_PARTITION_ID);
- if (goldenResourcePartitionId != null
- && goldenResourcePartitionId.hasPartitionIds()
- && goldenResourcePartitionId.getFirstPartitionIdOrNull() != null
- && (mdmLink.getPartitionId() == null || mdmLink.getPartitionId().getPartitionId() == null)) {
- mdmLink.setPartitionId(new PartitionablePartitionId(
- goldenResourcePartitionId.getFirstPartitionIdOrNull(),
- goldenResourcePartitionId.getPartitionDate()));
- }
+ addPartitioninfoForLinkIfNecessary(goldenResource, mdmLink);
myMdmLinkDaoSvc.save(mdmLink);
if (matchResult == MdmMatchResultEnum.MATCH) {
@@ -158,15 +149,38 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(sourceResource, goldenResource, mdmContext);
}
+ /**
+ * We use the versionless id
+ * because we call update on the goldenResource in 2 places:
+ * here and below where we rebuild goldenresources if we have set
+ * a link to NO_MATCH.
+ *
+ * This can be a problem when a source resource is deleted.
+ * then {@link MdmStorageInterceptor} will update all links
+ * connecting to any golden resource that was connected to the now deleted
+ * source resource to NO_MATCH before deleting orphaned golden resources.
+ */
+ goldenResource.setId(goldenResource.getIdElement().toVersionless());
myMdmResourceDaoSvc.upsertGoldenResource(goldenResource, mdmContext.getResourceType());
if (matchResult == MdmMatchResultEnum.NO_MATCH) {
- // We need to return no match for when a Golden Resource has already been found elsewhere
- if (myMdmLinkDaoSvc
- .getMdmLinksBySourcePidAndMatchResult(sourceResourceId, MdmMatchResultEnum.MATCH)
- .isEmpty()) {
- // Need to find a new Golden Resource to link this target to
+ /*
+ * link is broken. We need to do 2 things:
+ * * update links for the source resource (if no other golden resources exist, for instance)
+ * * rebuild the golden resource from scratch, using current survivorship rules
+ * and the current set of links
+ */
+ List> links =
+ myMdmLinkDaoSvc.getMdmLinksBySourcePidAndMatchResult(sourceResourceId, MdmMatchResultEnum.MATCH);
+ if (links.isEmpty()) {
+ // No more links to source; Find a new Golden Resource to link this target to
myMdmMatchLinkSvc.updateMdmLinksForMdmSource(sourceResource, mdmContext);
}
+
+ // with the link broken, the golden resource has delta info from a resource
+ // that is no longer matched to it; we need to remove this delta. But it's
+ // easier to just rebuild the resource from scratch using survivorship rules/current links
+ goldenResource =
+ myMdmSurvivorshipService.rebuildGoldenResourceWithSurvivorshipRules(goldenResource, mdmContext);
}
if (myInterceptorBroadcaster.hasHooks(Pointcut.MDM_POST_UPDATE_LINK)) {
@@ -181,6 +195,19 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
return goldenResource;
}
+ private static void addPartitioninfoForLinkIfNecessary(IAnyResource goldenResource, IMdmLink mdmLink) {
+ RequestPartitionId goldenResourcePartitionId =
+ (RequestPartitionId) goldenResource.getUserData(Constants.RESOURCE_PARTITION_ID);
+ if (goldenResourcePartitionId != null
+ && goldenResourcePartitionId.hasPartitionIds()
+ && goldenResourcePartitionId.getFirstPartitionIdOrNull() != null
+ && (mdmLink.getPartitionId() == null || mdmLink.getPartitionId().getPartitionId() == null)) {
+ mdmLink.setPartitionId(new PartitionablePartitionId(
+ goldenResourcePartitionId.getFirstPartitionIdOrNull(),
+ goldenResourcePartitionId.getPartitionDate()));
+ }
+ }
+
/**
* When updating POSSIBLE_MATCH link to a MATCH we need to validate that a MATCH to a different golden resource
* doesn't exist, because a resource mustn't be a MATCH to more than one golden resource
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java
index 8dd8abca747..b7fba3ab877 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java
+++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java
@@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateStrategyEnum;
import ca.uhn.fhir.jpa.mdm.svc.candidate.MatchedGoldenResourceCandidate;
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc;
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
+import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
@@ -69,6 +70,9 @@ public class MdmMatchLinkSvc {
@Autowired
private IBlockRuleEvaluationSvc myBlockRuleEvaluationSvc;
+ @Autowired
+ private IMdmSurvivorshipService myMdmSurvivorshipService;
+
/**
* Given an MDM source (consisting of any supported MDM type), find a suitable Golden Resource candidate for them,
* or create one if one does not exist. Performs matching based on rules defined in mdm-rules.json.
@@ -187,8 +191,8 @@ public class MdmMatchLinkSvc {
String.format(
"There were no matched candidates for MDM, creating a new %s Golden Resource.",
theResource.getIdElement().getResourceType()));
- IAnyResource newGoldenResource =
- myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource(theResource, theMdmTransactionContext);
+ IAnyResource newGoldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource(
+ theResource, theMdmTransactionContext, myMdmSurvivorshipService);
// TODO GGG :)
// 1. Get the right helper
// 2. Create source resource for the MDM source
@@ -214,7 +218,7 @@ public class MdmMatchLinkSvc {
theMdmTransactionContext,
"Duplicate detected based on the fact that both resources have different external EIDs.");
IAnyResource newGoldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource(
- theTargetResource, theMdmTransactionContext);
+ theTargetResource, theMdmTransactionContext, myMdmSurvivorshipService);
myMdmLinkSvc.updateLink(
newGoldenResource,
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImpl.java
deleted file mode 100644
index e1624d7999e..00000000000
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImpl.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*-
- * #%L
- * HAPI FHIR JPA Server - Master Data Management
- * %%
- * Copyright (C) 2014 - 2023 Smile CDR, Inc.
- * %%
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * #L%
- */
-package ca.uhn.fhir.jpa.mdm.svc;
-
-import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService;
-import ca.uhn.fhir.mdm.model.MdmTransactionContext;
-import ca.uhn.fhir.util.TerserUtil;
-import org.hl7.fhir.instance.model.api.IBase;
-import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.springframework.beans.factory.annotation.Autowired;
-
-public class MdmSurvivorshipSvcImpl implements IMdmSurvivorshipService {
-
- @Autowired
- private FhirContext myFhirContext;
-
- /**
- * Merges two golden resources by overwriting all field values on theGoldenResource param for CREATE_RESOURCE,
- * UPDATE_RESOURCE, SUBMIT_RESOURCE_TO_MDM, UPDATE_LINK (when setting to MATCH) and MANUAL_MERGE_GOLDEN_RESOURCES.
- * PID, identifiers and meta values are not affected by this operation.
- *
- * @param theTargetResource Target resource to retrieve fields from
- * @param theGoldenResource Golden resource to merge fields into
- * @param theMdmTransactionContext Current transaction context
- * @param