Add basic skeleton of MDM expansion
This commit is contained in:
parent
009f184ab4
commit
6226ae02d5
|
@ -33,11 +33,11 @@ import java.util.List;
|
|||
@Repository
|
||||
public interface IMdmLinkDao extends JpaRepository<MdmLink, Long> {
|
||||
@Modifying
|
||||
@Query("DELETE FROM MdmLink f WHERE myGoldenResourcePid = :pid OR mySourcePid = :pid")
|
||||
@Query("DELETE FROM MdmLink f WHERE f.myGoldenResourcePid = :pid OR f.mySourcePid = :pid")
|
||||
int deleteWithAnyReferenceToPid(@Param("pid") Long thePid);
|
||||
|
||||
@Modifying
|
||||
@Query("DELETE FROM MdmLink f WHERE (myGoldenResourcePid = :pid OR mySourcePid = :pid) AND myMatchResult <> :matchResult")
|
||||
@Query("DELETE FROM MdmLink f WHERE (f.myGoldenResourcePid = :pid OR f.mySourcePid = :pid) AND f.myMatchResult <> :matchResult")
|
||||
int deleteWithAnyReferenceToPidAndMatchResultNot(@Param("pid") Long thePid, @Param("matchResult") MdmMatchResultEnum theMatchResult);
|
||||
|
||||
@Query("SELECT ml2.myGoldenResourcePid, ml2.mySourcePid FROM MdmLink ml2 " +
|
||||
|
@ -51,4 +51,14 @@ public interface IMdmLinkDao extends JpaRepository<MdmLink, Long> {
|
|||
"AND hrl.myTargetResourceType='Patient'" +
|
||||
")")
|
||||
List<List<Long>> expandPidsFromGroupPidGivenMatchResult(@Param("groupPid") Long theGroupPid, @Param("matchResult") MdmMatchResultEnum theMdmMatchResultEnum);
|
||||
|
||||
@Query("SELECT ml.myGoldenResourcePid, ml.mySourcePid " +
|
||||
"FROM MdmLink ml " +
|
||||
"INNER JOIN MdmLink ml2 " +
|
||||
"on ml.myGoldenResourcePid=ml2.myGoldenResourcePid " +
|
||||
"WHERE ml2.mySourcePid=:sourcePid " +
|
||||
"AND ml2.myMatchResult=:matchResult " +
|
||||
"AND ml.myMatchResult=:matchResult")
|
||||
List<List<Long>> expandPidsBySourcePidAndMatchResult(@Param("sourcePid") Long theSourcePid, @Param("matchResult") MdmMatchResultEnum theMdmMatchResultEnum);
|
||||
|
||||
}
|
||||
|
|
|
@ -380,6 +380,29 @@ public class IdHelperService {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Given a set of PIDs, return a set of public FHIR Resource IDs.
|
||||
* This function will resolve a forced ID if it resolves, and if it fails to resolve to a forced it, will just return the pid
|
||||
* Example:
|
||||
* Let's say we have Patient/1(pid == 1), Patient/pat1 (pid == 2), Patient/3 (pid == 3), their pids would resolve as follows:
|
||||
*
|
||||
* [1,2,3] -> ["1","pat1","3"]
|
||||
*
|
||||
* @param thePids The Set of pids you would like to resolve to external FHIR Resource IDs.
|
||||
* @return A Set of strings representing the FHIR IDs of the pids.
|
||||
*/
|
||||
public Set<String> translatePidsToFhirResourceIds(Set<Long> thePids) {
|
||||
Map<Long, Optional<String>> pidToForcedIdMap = translatePidsToForcedIds(thePids);
|
||||
|
||||
//If the result of the translation is an empty optional, it means there is no forced id, and we can use the PID as the resource ID.
|
||||
Set<String> resolvedResourceIds = pidToForcedIdMap.entrySet().stream()
|
||||
.map(entry -> entry.getValue().isPresent() ? entry.getValue().get() : entry.getKey().toString())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
return resolvedResourceIds;
|
||||
|
||||
}
|
||||
public Map<Long, Optional<String>> translatePidsToForcedIds(Set<Long> thePids) {
|
||||
Map<Long, Optional<String>> retVal = new HashMap<>(myMemoryCacheService.getAllPresent(MemoryCacheService.CacheEnum.FORCED_ID, thePids));
|
||||
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package ca.uhn.fhir.jpa.dao.mdm;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2021 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%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
|
||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||
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 java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class MdmLinkExpandSvc {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkExpandSvc.class);
|
||||
|
||||
@Autowired
|
||||
private IMdmLinkDao myMdmLinkDao;
|
||||
@Autowired
|
||||
private IdHelperService myIdHelperService;
|
||||
|
||||
/**
|
||||
* Given a source resource, perform MDM expansion and return all the resource IDs of all resources that are
|
||||
* MDM-Matched to this resource.
|
||||
*
|
||||
* @param theResource The resource to MDM-Expand
|
||||
* @return A set of strings representing the FHIR IDs of the expanded resources.
|
||||
*/
|
||||
public Set<String> expandMdmBySourceResource(IBaseResource theResource) {
|
||||
return expandMdmBySourceResourceId(theResource.getIdElement());
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a resource ID of a source resource, perform MDM expansion and return all the resource IDs of all resources that are
|
||||
* MDM-Matched to this resource.
|
||||
*
|
||||
* @param theId The Resource ID of the resource to MDM-Expand
|
||||
* @return A set of strings representing the FHIR ids of the expanded resources.
|
||||
*/
|
||||
public Set<String> expandMdmBySourceResourceId(IIdType theId) {
|
||||
Long pidOrThrowException = myIdHelperService.getPidOrThrowException(theId);
|
||||
return expandMdmBySourceResourcePid(pidOrThrowException);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a PID of a source resource, perform MDM expansion and return all the resource IDs of all resources that are
|
||||
* MDM-Matched to this resource.
|
||||
*
|
||||
* @param theSourceResourcePid The PID of the resource to MDM-Expand
|
||||
* @return A set of strings representing the FHIR ids of the expanded resources.
|
||||
*/
|
||||
public Set<String> expandMdmBySourceResourcePid(Long theSourceResourcePid) {
|
||||
List<List<Long>> goldenPidSourcePidTuples = myMdmLinkDao.expandPidsBySourcePidAndMatchResult(theSourceResourcePid, MdmMatchResultEnum.MATCH);
|
||||
Set<Long> flattenedPids = new HashSet<>();
|
||||
goldenPidSourcePidTuples.forEach(flattenedPids::addAll);
|
||||
|
||||
Set<String> resourceIds = myIdHelperService.translatePidsToFhirResourceIds(flattenedPids);
|
||||
return resourceIds;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package ca.uhn.fhir.jpa.search.helper;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class SearchParamHelper {
|
||||
|
||||
@Autowired
|
||||
private FhirContext myFhirContext;
|
||||
|
||||
|
||||
public Collection<RuntimeSearchParam> getPatientSearchParamsForResourceType(String theResourceType) {
|
||||
RuntimeResourceDefinition runtimeResourceDefinition = myFhirContext.getResourceDefinition(theResourceType);
|
||||
Map<String, RuntimeSearchParam> searchParams = new HashMap<>();
|
||||
|
||||
RuntimeSearchParam patientSearchParam = runtimeResourceDefinition.getSearchParam("patient");
|
||||
if (patientSearchParam != null) {
|
||||
searchParams.put(patientSearchParam.getName(), patientSearchParam);
|
||||
|
||||
}
|
||||
RuntimeSearchParam subjectSearchParam = runtimeResourceDefinition.getSearchParam("subject");
|
||||
if (subjectSearchParam != null) {
|
||||
searchParams.put(subjectSearchParam.getName(), subjectSearchParam);
|
||||
}
|
||||
|
||||
List<RuntimeSearchParam> compartmentSearchParams = getPatientCompartmentRuntimeSearchParams(runtimeResourceDefinition);
|
||||
compartmentSearchParams.forEach(param -> searchParams.put(param.getName(), param));
|
||||
|
||||
return searchParams.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the resource definition for a compartment named 'patient' and return its related Search Parameter.
|
||||
*/
|
||||
public List<RuntimeSearchParam> getPatientCompartmentRuntimeSearchParams(RuntimeResourceDefinition runtimeResourceDefinition) {
|
||||
List<RuntimeSearchParam> patientSearchParam = new ArrayList<>();
|
||||
List<RuntimeSearchParam> searchParams = runtimeResourceDefinition.getSearchParamsForCompartmentName("Patient");
|
||||
return searchParams;
|
||||
// if (searchParams == null || searchParams.size() == 0) {
|
||||
// String errorMessage = String.format("Resource type [%s] is not eligible for this type of export, as it contains no Patient compartment, and no `patient` or `subject` search parameter", myResourceType);
|
||||
// throw new IllegalArgumentException(errorMessage);
|
||||
// } else if (searchParams.size() == 1) {
|
||||
// patientSearchParam = searchParams.get(0);
|
||||
// } else {
|
||||
// String errorMessage = String.format("Resource type [%s] is not eligible for Group Bulk export, as we are unable to disambiguate which patient search parameter we should be searching by.", myResourceType);
|
||||
// throw new IllegalArgumentException(errorMessage);
|
||||
// }
|
||||
// return patientSearchParam;
|
||||
}
|
||||
}
|
|
@ -2635,6 +2635,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
|||
@CoverageIgnore
|
||||
@Override
|
||||
public IValidationSupport.CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
//TODO GGG TRY TO JUST AUTO_PASS HERE AND SEE WHAT HAPPENS.
|
||||
invokeRunnableForUnitTest();
|
||||
|
||||
if (isNotBlank(theValueSetUrl)) {
|
||||
|
|
|
@ -68,6 +68,7 @@ public class JpaValidationSupportChain extends ValidationSupportChain {
|
|||
public void postConstruct() {
|
||||
addValidationSupport(myDefaultProfileValidationSupport);
|
||||
addValidationSupport(myJpaValidationSupport);
|
||||
//TODO MAKE SURE THAT THIS IS BEING CAL
|
||||
addValidationSupport(myTerminologyService);
|
||||
addValidationSupport(new SnapshotGeneratingValidationSupport(myFhirContext));
|
||||
addValidationSupport(new InMemoryTerminologyServerValidationSupport(myFhirContext));
|
||||
|
|
|
@ -112,7 +112,8 @@ public class TestR4Config extends BaseJavaConfigR4 {
|
|||
};
|
||||
|
||||
retVal.setDriver(new org.h2.Driver());
|
||||
retVal.setUrl("jdbc:h2:mem:testdb_r4");
|
||||
retVal.setUrl("jdbc:h2:file:/home/tadgh/smile/hapi-fhir/testdb_r4.db");
|
||||
// retVal.setUrl("jdbc:h2:mem:testdb_r4");
|
||||
retVal.setMaxWaitMillis(30000);
|
||||
retVal.setUsername("");
|
||||
retVal.setPassword("");
|
||||
|
|
|
@ -182,7 +182,6 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
|||
Patient patient = (Patient) outcome.getResource();
|
||||
patient.setId(outcome.getId());
|
||||
return patient;
|
||||
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
|
|
@ -2,15 +2,26 @@ package ca.uhn.fhir.jpa.mdm.dao;
|
|||
|
||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
||||
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
|
||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||
import ca.uhn.fhir.jpa.util.TestUtil;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.in;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.isIn;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -48,4 +59,47 @@ public class MdmLinkDaoSvcTest extends BaseMdmR4Test {
|
|||
assertEquals(rules.getVersion(), newLink.getVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandPidsWorks() {
|
||||
|
||||
Patient golden = createGoldenPatient();
|
||||
|
||||
//Create 10 linked patients.
|
||||
List<MdmLink> mdmLinks = new ArrayList<>();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
mdmLinks.add(createPatientAndLinkTo(golden.getIdElement().getIdPartAsLong(), MdmMatchResultEnum.MATCH));
|
||||
}
|
||||
|
||||
//Now lets connect a few as just POSSIBLE_MATCHes and ensure they aren't returned.
|
||||
for (int i = 0 ; i < 5; i++) {
|
||||
createPatientAndLinkTo(golden.getIdElement().getIdPartAsLong(), MdmMatchResultEnum.POSSIBLE_MATCH);
|
||||
}
|
||||
|
||||
List<Long> expectedExpandedPids = mdmLinks.stream().map(MdmLink::getSourcePid).collect(Collectors.toList());
|
||||
|
||||
//SUT
|
||||
List<List<Long>> lists = myMdmLinkDao.expandPidsBySourcePidAndMatchResult(mdmLinks.get(0).getSourcePid(), MdmMatchResultEnum.MATCH);
|
||||
|
||||
assertThat(lists, hasSize(10));
|
||||
|
||||
lists.stream()
|
||||
.forEach(pair -> {
|
||||
assertThat(pair.get(0), is(equalTo(golden.getIdElement().getIdPartAsLong())));
|
||||
assertThat(pair.get(1), is(in(expectedExpandedPids)));
|
||||
});
|
||||
}
|
||||
|
||||
private MdmLink createPatientAndLinkTo(Long thePatientPid, MdmMatchResultEnum theMdmMatchResultEnum) {
|
||||
Patient patient = createPatient();
|
||||
|
||||
MdmLink mdmLink = myMdmLinkDaoSvc.newMdmLink();
|
||||
mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
|
||||
mdmLink.setMatchResult(theMdmMatchResultEnum);
|
||||
mdmLink.setCreated(new Date());
|
||||
mdmLink.setUpdated(new Date());
|
||||
mdmLink.setGoldenResourcePid(thePatientPid);
|
||||
mdmLink.setSourcePid(myIdHelperService.getPidOrNull(patient));
|
||||
MdmLink saved= myMdmLinkDao.save(mdmLink);
|
||||
return saved;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.core.StringContains.containsString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -49,6 +51,8 @@ public class MdmExpungeTest extends BaseMdmR4Test {
|
|||
saveLink(mdmLink);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testUninterceptedDeleteRemovesMdmReference() {
|
||||
assertEquals(1, myMdmLinkDao.count());
|
||||
|
|
|
@ -68,6 +68,7 @@ public class TestJpaR4Config extends BaseJavaConfigR4 {
|
|||
|
||||
retVal.setDriver(new org.h2.Driver());
|
||||
retVal.setUrl("jdbc:h2:mem:testdb_r4");
|
||||
// retVal.setUrl("jdbc:h2:file:/home/tadgh/smile/hapi-fhir/testdb_r4.db");
|
||||
retVal.setMaxWaitMillis(10000);
|
||||
retVal.setUsername("");
|
||||
retVal.setPassword("");
|
||||
|
|
Loading…
Reference in New Issue