ConceptMap multi-version supported

This commit is contained in:
Frank Tao 2020-09-06 11:33:55 -04:00
parent 190f0ccb22
commit ed2801dbaf
6 changed files with 216 additions and 28 deletions

View File

@ -1,12 +1,15 @@
package ca.uhn.fhir.jpa.dao.data;
import ca.uhn.fhir.jpa.entity.TermConceptMap;
import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.Optional;
import ca.uhn.fhir.jpa.entity.TermConceptMap;
/*
* #%L
@ -36,6 +39,14 @@ public interface ITermConceptMapDao extends JpaRepository<TermConceptMap, Long>
@Query("SELECT cm FROM TermConceptMap cm WHERE cm.myResourcePid = :resource_pid")
Optional<TermConceptMap> findTermConceptMapByResourcePid(@Param("resource_pid") Long theResourcePid);
//-- Replaced with next method. Should it be removed?
@Deprecated
@Query("SELECT cm FROM TermConceptMap cm WHERE cm.myUrl = :url")
Optional<TermConceptMap> findTermConceptMapByUrl(@Param("url") String theUrl);
@Query(value="SELECT cm FROM TermConceptMap cm INNER JOIN ResourceTable r ON r.myId = cm.myResourcePid WHERE cm.myUrl = :url ORDER BY r.myUpdated DESC")
List<TermConceptMap> findTermConceptMapByUrl(Pageable thePage, @Param("url") String theUrl);
@Query("SELECT cm FROM TermConceptMap cm WHERE cm.myUrl = :url AND cm.myVersion = :version")
Optional<TermConceptMap> findTermConceptMapByUrlAndVersion(@Param("url") String theUrl, @Param("version") String theVersion);
}

View File

@ -35,12 +35,13 @@ import static org.apache.commons.lang3.StringUtils.length;
@Entity
@Table(name = "TRM_CONCEPT_MAP", uniqueConstraints = {
@UniqueConstraint(name = "IDX_CONCEPT_MAP_URL", columnNames = {"URL"})
@UniqueConstraint(name = "IDX_CONCEPT_MAP_URL", columnNames = {"URL", "VER"})
})
public class TermConceptMap implements Serializable {
private static final long serialVersionUID = 1L;
static final int MAX_URL_LENGTH = 200;
static final int MAX_VER_LENGTH = 200;
@Id()
@SequenceGenerator(name = "SEQ_CONCEPT_MAP_PID", sequenceName = "SEQ_CONCEPT_MAP_PID")
@ -64,6 +65,9 @@ public class TermConceptMap implements Serializable {
@Column(name = "URL", nullable = false, length = MAX_URL_LENGTH)
private String myUrl;
@Column(name = "VER", nullable = true, length = MAX_VER_LENGTH)
private String myVersion;
@OneToMany(mappedBy = "myConceptMap")
private List<TermConceptMapGroup> myConceptMapGroups;
@ -131,6 +135,17 @@ public class TermConceptMap implements Serializable {
return this;
}
public String getVersion() {
return myVersion;
}
public TermConceptMap setVersion(String theVersion) {
ValidateUtil.isNotTooLongOrThrowIllegalArgument(theVersion, MAX_VER_LENGTH,
"URL exceeds maximum length (" + MAX_URL_LENGTH + "): " + length(theVersion));
myVersion = theVersion;
return this;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
@ -140,6 +155,7 @@ public class TermConceptMap implements Serializable {
.append("mySource", mySource)
.append("myTarget", myTarget)
.append("myUrl", myUrl)
.append("myVersion", myVersion)
.append(myConceptMapGroups != null ? ("myConceptMapGroups - size=" + myConceptMapGroups.size()) : ("myConceptMapGroups=(null)"))
.toString();
}

View File

@ -131,6 +131,7 @@ import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
@ -1514,6 +1515,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
TermConceptMap termConceptMap = new TermConceptMap();
termConceptMap.setResource(theResourceTable);
termConceptMap.setUrl(theConceptMap.getUrl());
termConceptMap.setVersion(theConceptMap.getVersion());
String source = theConceptMap.hasSourceUriType() ? theConceptMap.getSourceUriType().getValueAsString() : null;
String target = theConceptMap.hasTargetUriType() ? theConceptMap.getTargetUriType().getValueAsString() : null;
@ -1547,7 +1549,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
* Do the upload.
*/
String conceptMapUrl = termConceptMap.getUrl();
Optional<TermConceptMap> optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrl(conceptMapUrl);
String conceptMapVersion = termConceptMap.getVersion();
Optional<TermConceptMap> optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrlAndVersion(conceptMapUrl, conceptMapVersion);
if (!optionalExistingTermConceptMapByUrl.isPresent()) {
try {
if (isNotBlank(source)) {
@ -1630,8 +1633,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
String msg = myContext.getLocalizer().getMessage(
BaseTermReadSvcImpl.class,
"cannotCreateDuplicateConceptMapUrl",
conceptMapUrl,
"cannotCreateDuplicateConceptMapUrlAndVersion",
conceptMapUrl, conceptMapVersion,
existingTermConceptMap.getResource().getIdDt().toUnqualifiedVersionless().getValue());
throw new UnprocessableEntityException(msg);

View File

@ -1,23 +1,32 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import ca.uhn.fhir.jpa.api.model.TranslationMatch;
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
import ca.uhn.fhir.jpa.api.model.TranslationResult;
import ca.uhn.fhir.util.TestUtil;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.Optional;
import org.hl7.fhir.dstu3.model.ConceptMap;
import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.UriType;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import static org.junit.jupiter.api.Assertions.*;
import ca.uhn.fhir.jpa.api.model.TranslationMatch;
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
import ca.uhn.fhir.jpa.api.model.TranslationResult;
import ca.uhn.fhir.jpa.entity.TermConceptMap;
public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3ConceptMapTest.class);
@ -98,4 +107,74 @@ public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test {
});
}
@Test
public void testConcaptMapFindTermConceptMapByUrl() {
Pageable page = PageRequest.of(0, 1);
List<TermConceptMap> theExpConceptMapList = myTermConceptMapDao.findTermConceptMapByUrl(page, CM_URL);
assertEquals(1, theExpConceptMapList.size());
assertEquals(CM_URL, theExpConceptMapList.get(0).getUrl());
}
@Test
public void testConcaptMapTwoConceptMapWithSameUrlDifferentVersion() {
String theUrl = "http://loinc.org/property/analyte-suffix";
ConceptMap theConceptMap1 = new ConceptMap();
ConceptMap theConceptMap2 = new ConceptMap();
theConceptMap1.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name1").setVersion("v1");
theConceptMap2.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name2").setVersion("v2");
myConceptMapDao.create(theConceptMap1);
myConceptMapDao.create(theConceptMap2);
Optional<TermConceptMap> theExpConceptMapV1 = myTermConceptMapDao.findTermConceptMapByUrlAndVersion(theUrl, "v1");
Optional<TermConceptMap> theExpConceptMapV2 = myTermConceptMapDao.findTermConceptMapByUrlAndVersion(theUrl, "v2");
assertTrue(theExpConceptMapV1.isPresent());
assertEquals(theUrl, theExpConceptMapV1.get().getUrl());
assertEquals("v1", theExpConceptMapV1.get().getVersion());
assertTrue(theExpConceptMapV2.isPresent());
assertEquals(theUrl, theExpConceptMapV2.get().getUrl());
assertEquals("v2", theExpConceptMapV2.get().getVersion());
// should return the latest one which is v2
Pageable page = PageRequest.of(0, 1);
List<TermConceptMap> theExpSecondOne = myTermConceptMapDao.findTermConceptMapByUrl(page, theUrl);
assertEquals(1, theExpSecondOne.size());
assertEquals(theUrl, theExpSecondOne.get(0).getUrl());
assertEquals("v2", theExpSecondOne.get(0).getVersion());
}
@Test
public void testConcaptMapTwoConceptMapWithSameUrlOneWithoutVersion() {
String theUrl = "http://loinc.org/property/analyte-suffix";
ConceptMap theConceptMap1 = new ConceptMap();
ConceptMap theConceptMap2 = new ConceptMap();
theConceptMap1.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name1").setVersion("v1");
theConceptMap2.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name2");
myConceptMapDao.create(theConceptMap1);
myConceptMapDao.create(theConceptMap2);
Optional<TermConceptMap> theExpConceptMapV1 = myTermConceptMapDao.findTermConceptMapByUrlAndVersion(theUrl, "v1");
assertTrue(theExpConceptMapV1.isPresent());
assertEquals(theUrl, theExpConceptMapV1.get().getUrl());
assertEquals("v1", theExpConceptMapV1.get().getVersion());
// should return the latest one which is v2
Pageable page = PageRequest.of(0, 1);
List<TermConceptMap> theExpSecondOne = myTermConceptMapDao.findTermConceptMapByUrl(page, theUrl);
assertEquals(1, theExpSecondOne.size());
assertEquals(theUrl, theExpSecondOne.get(0).getUrl());
assertNull(theExpSecondOne.get(0).getVersion());
}
}

View File

@ -1,31 +1,37 @@
package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.api.model.TranslationMatch;
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
import ca.uhn.fhir.jpa.api.model.TranslationResult;
import ca.uhn.fhir.util.TestUtil;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.CanonicalType;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r4.model.UriType;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import ca.uhn.fhir.jpa.api.model.TranslationMatch;
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
import ca.uhn.fhir.jpa.api.model.TranslationResult;
import ca.uhn.fhir.jpa.entity.TermConceptMap;
public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4ConceptMapTest.class);
@ -1148,5 +1154,74 @@ public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test {
assertEquals("S52.209A", outcome.getMatches().get(0).getConcept().getCode());
}
@Test
public void testConcaptMapFindTermConceptMapByUrl() {
Pageable page = PageRequest.of(0, 1);
List<TermConceptMap> theExpConceptMapList = myTermConceptMapDao.findTermConceptMapByUrl(page, CM_URL);
assertEquals(1, theExpConceptMapList.size());
assertEquals(CM_URL, theExpConceptMapList.get(0).getUrl());
}
@Test
public void testConcaptMapTwoConceptMapWithSameUrlDifferentVersion() {
String theUrl = "http://loinc.org/property/analyte-suffix";
ConceptMap theConceptMap1 = new ConceptMap();
ConceptMap theConceptMap2 = new ConceptMap();
theConceptMap1.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name1").setVersion("v1");
theConceptMap2.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name2").setVersion("v2");
myConceptMapDao.create(theConceptMap1);
myConceptMapDao.create(theConceptMap2);
Optional<TermConceptMap> theExpConceptMapV1 = myTermConceptMapDao.findTermConceptMapByUrlAndVersion(theUrl, "v1");
Optional<TermConceptMap> theExpConceptMapV2 = myTermConceptMapDao.findTermConceptMapByUrlAndVersion(theUrl, "v2");
assertTrue(theExpConceptMapV1.isPresent());
assertEquals(theUrl, theExpConceptMapV1.get().getUrl());
assertEquals("v1", theExpConceptMapV1.get().getVersion());
assertTrue(theExpConceptMapV2.isPresent());
assertEquals(theUrl, theExpConceptMapV2.get().getUrl());
assertEquals("v2", theExpConceptMapV2.get().getVersion());
// should return the latest one which is v2
Pageable page = PageRequest.of(0, 1);
List<TermConceptMap> theExpSecondOne = myTermConceptMapDao.findTermConceptMapByUrl(page, theUrl);
assertEquals(1, theExpSecondOne.size());
assertEquals(theUrl, theExpSecondOne.get(0).getUrl());
assertEquals("v2", theExpSecondOne.get(0).getVersion());
}
@Test
public void testConcaptMapTwoConceptMapWithSameUrlOneWithoutVersion() {
String theUrl = "http://loinc.org/property/analyte-suffix";
ConceptMap theConceptMap1 = new ConceptMap();
ConceptMap theConceptMap2 = new ConceptMap();
theConceptMap1.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name1").setVersion("v1");
theConceptMap2.setUrl(theUrl).setStatus(PublicationStatus.ACTIVE).setName("name2");
myConceptMapDao.create(theConceptMap1);
myConceptMapDao.create(theConceptMap2);
Optional<TermConceptMap> theExpConceptMapV1 = myTermConceptMapDao.findTermConceptMapByUrlAndVersion(theUrl, "v1");
assertTrue(theExpConceptMapV1.isPresent());
assertEquals(theUrl, theExpConceptMapV1.get().getUrl());
assertEquals("v1", theExpConceptMapV1.get().getVersion());
// should return the latest one which is v2
Pageable page = PageRequest.of(0, 1);
List<TermConceptMap> theExpSecondOne = myTermConceptMapDao.findTermConceptMapByUrl(page, theUrl);
assertEquals(1, theExpSecondOne.size());
assertEquals(theUrl, theExpSecondOne.get(0).getUrl());
assertNull(theExpSecondOne.get(0).getVersion());
}
}

View File

@ -25,6 +25,8 @@ import org.hl7.fhir.utilities.validation.ValidationOptions;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
@ -292,10 +294,11 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test {
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) {
Optional<TermConceptMap> optionalConceptMap = myTermConceptMapDao.findTermConceptMapByUrl(CM_URL);
assertTrue(optionalConceptMap.isPresent());
Pageable page = PageRequest.of(0, 1);
List<TermConceptMap> optionalConceptMap = myTermConceptMapDao.findTermConceptMapByUrl(page, CM_URL);
assertEquals(1, optionalConceptMap.size());
TermConceptMap conceptMap = optionalConceptMap.get();
TermConceptMap conceptMap = optionalConceptMap.get(0);
ourLog.info("ConceptMap:\n" + conceptMap.toString());
@ -470,10 +473,11 @@ public class TerminologySvcImplR4Test extends BaseTermR4Test {
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) {
Optional<TermConceptMap> optionalConceptMap = myTermConceptMapDao.findTermConceptMapByUrl(CM_URL);
assertTrue(optionalConceptMap.isPresent());
Pageable page = PageRequest.of(0, 1);
List<TermConceptMap> optionalConceptMap = myTermConceptMapDao.findTermConceptMapByUrl(page, CM_URL);
assertEquals(1, optionalConceptMap.size());
TermConceptMap conceptMap = optionalConceptMap.get();
TermConceptMap conceptMap = optionalConceptMap.get(0);
ourLog.info("ConceptMap:\n" + conceptMap.toString());