diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java index 8a730a842e8..207b072cc96 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java @@ -75,6 +75,7 @@ public enum VersionEnum { V5_4_1, V5_4_2, V5_5_0, + ; public static VersionEnum latestVersion() { diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2871-enhance-mdm-expansion.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2871-enhance-mdm-expansion.yaml new file mode 100644 index 00000000000..f0f427f2b01 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2871-enhance-mdm-expansion.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 2871 +title: "Modified the behaviour of the `:mdm` param qualifier. Previously, it used to only resolve IDs if the resource ID was a source resource. +Now, MDM expansion will work if you pass it the ID of a golden resource instead." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java index 16834facbe2..969ea3926ca 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java @@ -67,4 +67,7 @@ public interface IMdmLinkDao extends JpaRepository { "AND ml.myMatchResult=:matchResult") List expandPidsBySourcePidAndMatchResult(@Param("sourcePid") Long theSourcePid, @Param("matchResult") MdmMatchResultEnum theMdmMatchResultEnum); + @Query("SELECT ml.myGoldenResourcePid as goldenPid, ml.mySourcePid as sourcePid FROM MdmLink ml WHERE ml.myGoldenResourcePid = :goldenPid and ml.myMatchResult = :matchResult") + List expandPidsByGoldenResourcePidAndMatchResult(@Param("goldenPid") Long theSourcePid, @Param("matchResult") MdmMatchResultEnum theMdmMatchResultEnum); + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java index 66af7f5a706..4a28be40c5a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java @@ -25,15 +25,13 @@ import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.model.primitive.IdDt; -import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.ArrayList; +import javax.annotation.Nonnull; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -82,14 +80,50 @@ public class MdmLinkExpandSvc { public Set expandMdmBySourceResourcePid(Long theSourceResourcePid) { ourLog.debug("About to expand source resource with PID {}", theSourceResourcePid); List goldenPidSourcePidTuples = myMdmLinkDao.expandPidsBySourcePidAndMatchResult(theSourceResourcePid, MdmMatchResultEnum.MATCH); + return flattenPidTuplesToSet(theSourceResourcePid, goldenPidSourcePidTuples); + } + + /** + * Given a PID of a golden resource, perform MDM expansion and return all the resource IDs of all resources that are + * MDM-Matched to this golden resource. + * + * @param theGoldenResourcePid The PID of the golden resource to MDM-Expand. + * @return A set of strings representing the FHIR ids of the expanded resources. + */ + public Set expandMdmByGoldenResourceId(Long theGoldenResourcePid) { + ourLog.debug("About to expand golden resource with PID {}", theGoldenResourcePid); + List goldenPidSourcePidTuples = myMdmLinkDao.expandPidsByGoldenResourcePidAndMatchResult(theGoldenResourcePid, MdmMatchResultEnum.MATCH); + return flattenPidTuplesToSet(theGoldenResourcePid, goldenPidSourcePidTuples); + } + + + /** + * Given a resource ID of a golden resource, perform MDM expansion and return all the resource IDs of all resources that are + * MDM-Matched to this golden resource. + * + * @param theGoldenResourcePid The resource ID of the golden resource to MDM-Expand. + * @return A set of strings representing the FHIR ids of the expanded resources. + */ + public Set expandMdmByGoldenResourcePid(Long theGoldenResourcePid) { + ourLog.debug("About to expand golden resource with PID {}", theGoldenResourcePid); + List goldenPidSourcePidTuples = myMdmLinkDao.expandPidsByGoldenResourcePidAndMatchResult(theGoldenResourcePid, MdmMatchResultEnum.MATCH); + return flattenPidTuplesToSet(theGoldenResourcePid, goldenPidSourcePidTuples); + } + public Set expandMdmByGoldenResourceId(IdDt theId) { + ourLog.debug("About to expand golden resource with golden resource id {}", theId); + Long pidOrThrowException = myIdHelperService.getPidOrThrowException(theId); + return expandMdmByGoldenResourcePid(pidOrThrowException); + } + + @Nonnull + private Set flattenPidTuplesToSet(Long initialPid, List goldenPidSourcePidTuples) { Set flattenedPids = new HashSet<>(); goldenPidSourcePidTuples.forEach(tuple -> { flattenedPids.add(tuple.getSourcePid()); flattenedPids.add(tuple.getGoldenPid()); }); Set resourceIds = myIdHelperService.translatePidsToFhirResourceIds(flattenedPids); - ourLog.debug("Pid {} has been expanded to [{}]", theSourceResourcePid, String.join(",", resourceIds)); + ourLog.debug("Pid {} has been expanded to [{}]", initialPid, String.join(",", resourceIds)); return resourceIds; } - } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/MdmSearchExpandingInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/MdmSearchExpandingInterceptor.java index 4516e342a14..1ec7b3593c9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/MdmSearchExpandingInterceptor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/MdmSearchExpandingInterceptor.java @@ -76,8 +76,16 @@ public class MdmSearchExpandingInterceptor { if (iQueryParameterType instanceof ReferenceParam) { ReferenceParam refParam = (ReferenceParam) iQueryParameterType; if (refParam.isMdmExpand()) { - ourLog.debug("Found a reference parameter to expand: {}", refParam.toString()); + ourLog.debug("Found a reference parameter to expand: {}", refParam); + //First, attempt to expand as a source resource. Set expandedResourceIds = myMdmLinkExpandSvc.expandMdmBySourceResourceId(new IdDt(refParam.getValue())); + + // If we failed, attempt to expand as a golden resource + if (expandedResourceIds.isEmpty()) { + expandedResourceIds = myMdmLinkExpandSvc.expandMdmByGoldenResourceId(new IdDt(refParam.getValue())); + } + + //Rebuild the search param list. if (!expandedResourceIds.isEmpty()) { ourLog.debug("Parameter has been expanded to: {}", String.join(", ", expandedResourceIds)); toRemove.add(refParam); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java index 58d3abf4afa..2c1d2a40bad 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java @@ -2,14 +2,18 @@ package ca.uhn.fhir.jpa.mdm.interceptor; import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; +import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.mdm.api.MdmConstants; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.param.ReferenceOrListParam; import ca.uhn.fhir.rest.param.ReferenceParam; +import org.elasticsearch.common.collect.Set; +import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Patient; @@ -21,6 +25,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; +import java.util.List; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -73,6 +79,19 @@ public class MdmSearchExpandingInterceptorIT extends BaseMdmR4Test { myDaoConfig.setAllowMdmExpansion(true); search = myObservationDao.search(searchParameterMap); assertThat(search.size(), is(equalTo(4))); + List all = myMdmLinkDao.findAll(); + Long goldenPid = all.get(0).getGoldenResourcePid(); + IIdType goldenId = myIdHelperService.translatePidIdToForcedId(myFhirContext, "Patient", new ResourcePersistentId(goldenPid)); + //Verify that expansion by the golden resource id resolves down to everything its links have. + + SearchParameterMap goldenSpMap = new SearchParameterMap(); + goldenSpMap.setLoadSynchronous(true); + ReferenceOrListParam goldenReferenceOrListParam = new ReferenceOrListParam(); + goldenReferenceOrListParam.addOr(new ReferenceParam(goldenId).setMdmExpand(true)); + goldenSpMap.add(Observation.SP_SUBJECT, goldenReferenceOrListParam); + + search = myObservationDao.search(goldenSpMap); + assertThat(search.size(), is(equalTo(4))); } @Test