Fix teerm sevice indexing

This commit is contained in:
jamesagnew 2016-06-26 23:10:02 -04:00
parent fae4344c36
commit e9cb518012
26 changed files with 1123 additions and 570 deletions

View File

@ -101,6 +101,7 @@ public class BaseDstu3Config extends BaseConfig {
@Bean(name = "mySystemProviderDstu3") @Bean(name = "mySystemProviderDstu3")
public ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3 systemProviderDstu3() { public ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3 systemProviderDstu3() {
ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3 retVal = new ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3(); ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3 retVal = new ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3();
retVal.setContext(defaultFhirContext());
retVal.setDao(systemDaoDstu3()); retVal.setDao(systemDaoDstu3());
return retVal; return retVal;
} }

View File

@ -1174,7 +1174,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
coordsParams = extractSearchParamCoords(theEntity, theResource); coordsParams = extractSearchParamCoords(theEntity, theResource);
// ourLog.info("Indexing resource: {}", entity.getId()); // ourLog.info("Indexing resource: {}", entity.getId());
ourLog.info("Storing date indexes: {}", dateParams); ourLog.trace("Storing date indexes: {}", dateParams);
tokenParams = new HashSet<ResourceIndexedSearchParamToken>(); tokenParams = new HashSet<ResourceIndexedSearchParamToken>();
for (BaseResourceIndexedSearchParam next : extractSearchParamTokens(theEntity, theResource)) { for (BaseResourceIndexedSearchParam next : extractSearchParamTokens(theEntity, theResource)) {

View File

@ -37,8 +37,6 @@ import javax.persistence.criteria.Root;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
@ -50,7 +48,6 @@ import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptDao;
import ca.uhn.fhir.jpa.entity.ForcedId; import ca.uhn.fhir.jpa.entity.ForcedId;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.util.ReindexFailureException; import ca.uhn.fhir.jpa.util.ReindexFailureException;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
@ -89,7 +86,6 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager); TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED); txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
int retVal = doPerformReindexingPassForResources(theCount, txTemplate); int retVal = doPerformReindexingPassForResources(theCount, txTemplate);
retVal += doPerformReindexingPassForConcepts(txTemplate);
return retVal; return retVal;
} }
@ -152,37 +148,6 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
}); });
} }
private int doPerformReindexingPassForConcepts(TransactionTemplate txTemplate) {
return txTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus theStatus) {
int maxResult = 10000;
Page<TermConcept> resources = myTermConceptDao.findResourcesRequiringReindexing(new PageRequest(0, maxResult));
if (resources.hasContent() == false) {
return 0;
}
ourLog.info("Indexing {} / {} concepts", resources.getContent().size(), resources.getTotalElements());
int count = 0;
long start = System.currentTimeMillis();
for (TermConcept resourceTable : resources) {
resourceTable.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED);
myTermConceptDao.save(resourceTable);
count++;
}
long delay = System.currentTimeMillis() - start;
long avg = (delay / resources.getContent().size());
ourLog.info("Indexed {} / {} concepts in {}ms - Avg {}ms / resource", new Object[] { count, resources.getContent().size(), delay, avg });
return resources.getContent().size();
}
});
}
@Override @Override
public TagList getAllTags(RequestDetails theRequestDetails) { public TagList getAllTags(RequestDetails theRequestDetails) {
// Notify interceptors // Notify interceptors

View File

@ -78,7 +78,7 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
} }
@Override @Override
public ValueSet expand(IIdType theId, String theFilter) { public ValueSet expand(IIdType theId, String theFilter, RequestDetails theRequestDetails) {
ValueSet source = loadValueSetForExpansion(theId); ValueSet source = loadValueSetForExpansion(theId);
return expand(source, theFilter); return expand(source, theFilter);
@ -179,7 +179,7 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
} }
@Override @Override
public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, CodingDt theCoding, CodeableConceptDt theCodeableConcept) { public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, CodingDt theCoding, CodeableConceptDt theCodeableConcept, RequestDetails theRequestDetails) {
List<IIdType> valueSetIds; List<IIdType> valueSetIds;
boolean haveCodeableConcept = theCodeableConcept != null && theCodeableConcept.getCoding().size() > 0; boolean haveCodeableConcept = theCodeableConcept != null && theCodeableConcept.getCoding().size() > 0;
@ -212,7 +212,7 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
} }
for (IIdType nextId : valueSetIds) { for (IIdType nextId : valueSetIds) {
ValueSet expansion = expand(nextId, null); ValueSet expansion = expand(nextId, null, theRequestDetails);
List<ExpansionContains> contains = expansion.getExpansion().getContains(); List<ExpansionContains> contains = expansion.getExpansion().getContains();
ValidateCodeResult result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept); ValidateCodeResult result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept);
if (result != null) { if (result != null) {
@ -314,7 +314,7 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
List<IIdType> valueSetIds = findCodeSystemIdsContainingSystemAndCode(code, system); List<IIdType> valueSetIds = findCodeSystemIdsContainingSystemAndCode(code, system);
for (IIdType nextId : valueSetIds) { for (IIdType nextId : valueSetIds) {
ValueSet expansion = expand(nextId, null); ValueSet expansion = expand(nextId, null, theRequestDetails);
List<ExpansionContains> contains = expansion.getExpansion().getContains(); List<ExpansionContains> contains = expansion.getExpansion().getContains();
LookupCodeResult result = lookup(contains, system, code); LookupCodeResult result = lookup(contains, system, code);
if (result != null) { if (result != null) {

View File

@ -24,9 +24,11 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.rest.method.RequestDetails;
public interface IFhirResourceDaoValueSet<T extends IBaseResource, CD, CC> extends IFhirResourceDao<T> { public interface IFhirResourceDaoValueSet<T extends IBaseResource, CD, CC> extends IFhirResourceDao<T> {
T expand(IIdType theId, String theFilter); T expand(IIdType theId, String theFilter, RequestDetails theRequestDetails);
T expand(T theSource, String theFilter); T expand(T theSource, String theFilter);
@ -34,7 +36,7 @@ public interface IFhirResourceDaoValueSet<T extends IBaseResource, CD, CC> exten
void purgeCaches(); void purgeCaches();
ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, CD theCoding, CC theCodeableConcept); ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, CD theCoding, CC theCodeableConcept, RequestDetails theRequestDetails);
public class ValidateCodeResult { public class ValidateCodeResult {
private String myDisplay; private String myDisplay;

View File

@ -26,6 +26,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.nio.file.FileVisitOption; import java.nio.file.FileVisitOption;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -56,6 +57,7 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.util.LogicUtil; import ca.uhn.fhir.jpa.util.LogicUtil;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.ElementUtil;
@ -70,8 +72,8 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
private IFhirResourceDaoCodeSystem<CodeSystem, CodeableConcept, Coding> myCodeSystemDao; private IFhirResourceDaoCodeSystem<CodeSystem, CodeableConcept, Coding> myCodeSystemDao;
@Override @Override
public ValueSet expand(IIdType theId, String theFilter) { public ValueSet expand(IIdType theId, String theFilter, RequestDetails theRequestDetails) {
ValueSet source = myValidationSupport.fetchResource(getContext(), ValueSet.class, theId.getValue()); ValueSet source = read(theId, theRequestDetails);
return expand(source, theFilter); return expand(source, theFilter);
} }
@ -86,6 +88,7 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
ValueSetExpansionComponent expansion = outcome.getValueset().getExpansion(); ValueSetExpansionComponent expansion = outcome.getValueset().getExpansion();
ValueSet retVal = new ValueSet(); ValueSet retVal = new ValueSet();
retVal.getMeta().setLastUpdated(new Date());
retVal.setExpansion(expansion); retVal.setExpansion(expansion);
return retVal; return retVal;
} }
@ -166,7 +169,7 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
@Override @Override
public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode, public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode,
IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, Coding theCoding, CodeableConcept theCodeableConcept) { IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
List<IIdType> valueSetIds = Collections.emptyList(); List<IIdType> valueSetIds = Collections.emptyList();
List<IIdType> codeSystemIds = Collections.emptyList(); List<IIdType> codeSystemIds = Collections.emptyList();
@ -205,7 +208,7 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
} }
for (IIdType nextId : valueSetIds) { for (IIdType nextId : valueSetIds) {
ValueSet expansion = expand(nextId, null); ValueSet expansion = expand(nextId, null, theRequestDetails);
List<ValueSetExpansionContainsComponent> contains = expansion.getExpansion().getContains(); List<ValueSetExpansionContainsComponent> contains = expansion.getExpansion().getContains();
ValidateCodeResult result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept); ValidateCodeResult result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept);
if (result != null) { if (result != null) {
@ -219,7 +222,7 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
} }
for (IIdType nextId : codeSystemIds) { for (IIdType nextId : codeSystemIds) {
ValueSet expansion = expand(nextId, null); ValueSet expansion = expand(nextId, null, theRequestDetails);
List<ValueSetExpansionContainsComponent> contains = expansion.getExpansion().getContains(); List<ValueSetExpansionContainsComponent> contains = expansion.getExpansion().getContains();
ValidateCodeResult result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept); ValidateCodeResult result = validateCodeIsInContains(contains, toStringOrNull(theSystem), toStringOrNull(theCode), theCoding, theCodeableConcept);
if (result != null) { if (result != null) {

View File

@ -25,6 +25,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -39,6 +40,8 @@ import javax.persistence.Index;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.SequenceGenerator; import javax.persistence.SequenceGenerator;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.Transient; import javax.persistence.Transient;
@ -49,47 +52,56 @@ import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.lucene.analysis.core.WhitespaceTokenizerFactory;
import org.hibernate.search.annotations.Analyze; import org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.Analyzer; import org.hibernate.search.annotations.Analyzer;
import org.hibernate.search.annotations.AnalyzerDef;
import org.hibernate.search.annotations.AnalyzerDefs;
import org.hibernate.search.annotations.Field; import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Fields; import org.hibernate.search.annotations.Fields;
import org.hibernate.search.annotations.Indexed; import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.Store; import org.hibernate.search.annotations.Store;
import org.hibernate.search.annotations.TokenizerDef;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.search.DeferConceptIndexingInterceptor;
//@formatter:off //@formatter:off
@Entity @Entity
@Indexed() @Indexed(interceptor=DeferConceptIndexingInterceptor.class)
@Table(name="TRM_CONCEPT", uniqueConstraints= { @Table(name="TRM_CONCEPT", uniqueConstraints= {
@UniqueConstraint(name="IDX_CONCEPT_CS_CODE", columnNames= {"CODESYSTEM_PID", "CODE"}) @UniqueConstraint(name="IDX_CONCEPT_CS_CODE", columnNames= {"CODESYSTEM_PID", "CODE"})
}, indexes= { }, indexes= {
@Index(name = "IDX_CONCEPT_INDEXSTATUS", columnList="INDEX_STATUS") @Index(name = "IDX_CONCEPT_INDEXSTATUS", columnList="INDEX_STATUS")
}) })
@AnalyzerDefs({
@AnalyzerDef(name = "conceptParentPidsAnalyzer",
tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
filters = {
})
})
//@formatter:on //@formatter:on
public class TermConcept implements Serializable { public class TermConcept implements Serializable {
private static final int MAX_DESC_LENGTH = 400; private static final int MAX_DESC_LENGTH = 400;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TermConcept.class);
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@OneToMany(fetch=FetchType.LAZY, mappedBy="myParent") @OneToMany(fetch = FetchType.LAZY, mappedBy = "myParent", cascade= {})
private Collection<TermConceptParentChildLink> myChildren; private Collection<TermConceptParentChildLink> myChildren;
@Column(name="CODE", length=100, nullable=false) @Column(name = "CODE", length = 100, nullable = false)
@Fields({ @Fields({ @Field(name = "myCode", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "exactAnalyzer")), })
@Field(name = "myCode", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "exactAnalyzer")),
})
private String myCode; private String myCode;
@ManyToOne() @ManyToOne()
@JoinColumn(name="CODESYSTEM_PID", referencedColumnName="PID", foreignKey=@ForeignKey(name="FK_CONCEPT_PID_CS_PID")) @JoinColumn(name = "CODESYSTEM_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPT_PID_CS_PID"))
private TermCodeSystemVersion myCodeSystem; private TermCodeSystemVersion myCodeSystem;
@Column(name="CODESYSTEM_PID", insertable=false, updatable=false) @Column(name = "CODESYSTEM_PID", insertable = false, updatable = false)
@Fields({ @Fields({ @Field(name = "myCodeSystemVersionPid") })
@Field(name="myCodeSystemVersionPid")
})
private long myCodeSystemVersionPid; private long myCodeSystemVersionPid;
//@formatter:off //@formatter:off
@Column(name="DISPLAY", length=MAX_DESC_LENGTH, nullable=true) @Column(name="DISPLAY", length=MAX_DESC_LENGTH, nullable=true)
@Fields({ @Fields({
@ -100,23 +112,21 @@ public class TermConcept implements Serializable {
}) })
private String myDisplay; private String myDisplay;
//@formatter:on //@formatter:on
@Id() @Id()
@SequenceGenerator(name="SEQ_CONCEPT_PID", sequenceName="SEQ_CONCEPT_PID") @SequenceGenerator(name = "SEQ_CONCEPT_PID", sequenceName = "SEQ_CONCEPT_PID")
@GeneratedValue(strategy=GenerationType.AUTO, generator="SEQ_CONCEPT_PID") @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_PID")
@Column(name="PID") @Column(name = "PID")
private Long myId; private Long myId;
@Column(name = "INDEX_STATUS", nullable = true) @Column(name = "INDEX_STATUS", nullable = true)
private Long myIndexStatus; private Long myIndexStatus;
@Transient @Transient
@Fields({ @Field(name = "myParentPids", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "conceptParentPidsAnalyzer"))
@Field(name = "myParentPids", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "standardAnalyzer")),
})
private String myParentPids; private String myParentPids;
@OneToMany(cascade= {}, fetch=FetchType.LAZY, mappedBy="myChild") @OneToMany(cascade = {}, fetch = FetchType.LAZY, mappedBy = "myChild")
private Collection<TermConceptParentChildLink> myParents; private Collection<TermConceptParentChildLink> myParents;
public TermConcept() { public TermConcept() {
@ -135,7 +145,7 @@ public class TermConcept implements Serializable {
link.setChild(theChild); link.setChild(theChild);
link.setRelationshipType(theRelationshipType); link.setRelationshipType(theRelationshipType);
getChildren().add(link); getChildren().add(link);
theChild.getParents().add(link); theChild.getParents().add(link);
return this; return this;
} }
@ -154,8 +164,8 @@ public class TermConcept implements Serializable {
if (theObj == this) { if (theObj == this) {
return true; return true;
} }
TermConcept obj = (TermConcept)theObj; TermConcept obj = (TermConcept) theObj;
EqualsBuilder b = new EqualsBuilder(); EqualsBuilder b = new EqualsBuilder();
b.append(myCodeSystem, obj.myCodeSystem); b.append(myCodeSystem, obj.myCodeSystem);
@ -190,13 +200,17 @@ public class TermConcept implements Serializable {
return myIndexStatus; return myIndexStatus;
} }
public String getParentPidsAsString() {
return myParentPids;
}
public Collection<TermConceptParentChildLink> getParents() { public Collection<TermConceptParentChildLink> getParents() {
if (myParents == null) { if (myParents == null) {
myParents = new ArrayList<TermConceptParentChildLink>(); myParents = new ArrayList<TermConceptParentChildLink>();
} }
return myParents; return myParents;
} }
@Override @Override
public int hashCode() { public int hashCode() {
HashCodeBuilder b = new HashCodeBuilder(); HashCodeBuilder b = new HashCodeBuilder();
@ -204,7 +218,29 @@ public class TermConcept implements Serializable {
b.append(myCode); b.append(myCode);
return b.toHashCode(); return b.toHashCode();
} }
private void parentPids(TermConcept theNextConcept, Set<Long> theParentPids) {
for (TermConceptParentChildLink nextParentLink : theNextConcept.getParents()) {
TermConcept parent = nextParentLink.getParent();
Long parentConceptId = parent.getId();
Validate.notNull(parentConceptId);
if (parent != null && theParentPids.add(parentConceptId)) {
parentPids(parent, theParentPids);
}
}
}
@PreUpdate
@PrePersist
public void prePersist() {
Set<Long> parentPids = new HashSet<Long>();
TermConcept entity = this;
parentPids(entity, parentPids);
entity.setParentPids(parentPids);
ourLog.trace("Code {}/{} has parents {}", entity.getId(), entity.getCode(), entity.getParentPidsAsString());
}
public void setCode(String theCode) { public void setCode(String theCode) {
myCode = theCode; myCode = theCode;
} }
@ -236,7 +272,11 @@ public class TermConcept implements Serializable {
} }
b.append(next); b.append(next);
} }
if (b.length() == 0) {
b.append("NONE");
}
myParentPids = b.toString(); myParentPids = b.toString();
} }
@ -244,5 +284,4 @@ public class TermConcept implements Serializable {
public String toString() { public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("code", myCode).append("display", myDisplay).build(); return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("code", myCode).append("display", myDisplay).build();
} }
} }

View File

@ -96,7 +96,55 @@ public class TermConceptParentChildLink implements Serializable {
} }
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((myChild == null) ? 0 : myChild.hashCode());
result = prime * result + ((myCodeSystem == null) ? 0 : myCodeSystem.hashCode());
result = prime * result + ((myParent == null) ? 0 : myParent.hashCode());
result = prime * result + ((myRelationshipType == null) ? 0 : myRelationshipType.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TermConceptParentChildLink other = (TermConceptParentChildLink) obj;
if (myChild == null) {
if (other.myChild != null)
return false;
} else if (!myChild.equals(other.myChild))
return false;
if (myCodeSystem == null) {
if (other.myCodeSystem != null)
return false;
} else if (!myCodeSystem.equals(other.myCodeSystem))
return false;
if (myParent == null) {
if (other.myParent != null)
return false;
} else if (!myParent.equals(other.myParent))
return false;
if (myRelationshipType != other.myRelationshipType)
return false;
return true;
}
public enum RelationshipTypeEnum{ public enum RelationshipTypeEnum{
// ********************************************
// IF YOU ADD HERE MAKE SURE ORDER IS PRESERVED
ISA ISA
} }
public Long getId() {
return myPid;
}
} }

View File

@ -53,7 +53,8 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst
@IdParam(optional=true) IdDt theId, @IdParam(optional=true) IdDt theId,
@OperationParam(name="valueSet", min=0, max=1) ValueSet theValueSet, @OperationParam(name="valueSet", min=0, max=1) ValueSet theValueSet,
@OperationParam(name="identifier", min=0, max=1) UriDt theIdentifier, @OperationParam(name="identifier", min=0, max=1) UriDt theIdentifier,
@OperationParam(name = "filter", min=0, max=1) StringDt theFilter) { @OperationParam(name = "filter", min=0, max=1) StringDt theFilter,
RequestDetails theRequestDetails) {
//@formatter:on //@formatter:on
boolean haveId = theId != null && theId.hasIdPart(); boolean haveId = theId != null && theId.hasIdPart();
@ -72,7 +73,7 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst
try { try {
IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt> dao = (IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt>) getDao(); IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt> dao = (IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt>) getDao();
if (haveId) { if (haveId) {
return dao.expand(theId, toFilterString(theFilter)); return dao.expand(theId, toFilterString(theFilter), theRequestDetails);
} else if (haveIdentifier) { } else if (haveIdentifier) {
return dao.expandByIdentifier(theIdentifier.getValue(), toFilterString(theFilter)); return dao.expandByIdentifier(theIdentifier.getValue(), toFilterString(theFilter));
} else { } else {
@ -155,14 +156,15 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst
@OperationParam(name="system", min=0, max=1) UriDt theSystem, @OperationParam(name="system", min=0, max=1) UriDt theSystem,
@OperationParam(name="display", min=0, max=1) StringDt theDisplay, @OperationParam(name="display", min=0, max=1) StringDt theDisplay,
@OperationParam(name="coding", min=0, max=1) CodingDt theCoding, @OperationParam(name="coding", min=0, max=1) CodingDt theCoding,
@OperationParam(name="codeableConcept", min=0, max=1) CodeableConceptDt theCodeableConcept @OperationParam(name="codeableConcept", min=0, max=1) CodeableConceptDt theCodeableConcept,
RequestDetails theRequestDetails
) { ) {
//@formatter:on //@formatter:on
startRequest(theServletRequest); startRequest(theServletRequest);
try { try {
IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt> dao = (IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt>) getDao(); IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt> dao = (IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt>) getDao();
ValidateCodeResult result = dao.validateCode(theValueSetIdentifier, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept); ValidateCodeResult result = dao.validateCode(theValueSetIdentifier, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
Parameters retVal = new Parameters(); Parameters retVal = new Parameters();
retVal.addParameter().setName("result").setValue(new BooleanDt(result.isResult())); retVal.addParameter().setName("result").setValue(new BooleanDt(result.isResult()));
if (isNotBlank(result.getMessage())) { if (isNotBlank(result.getMessage())) {

View File

@ -39,6 +39,7 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDstu3<ValueSet> { public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDstu3<ValueSet> {
@ -50,7 +51,8 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst
@IdParam(optional=true) IdType theId, @IdParam(optional=true) IdType theId,
@OperationParam(name="valueSet", min=0, max=1) ValueSet theValueSet, @OperationParam(name="valueSet", min=0, max=1) ValueSet theValueSet,
@OperationParam(name="identifier", min=0, max=1) UriType theIdentifier, @OperationParam(name="identifier", min=0, max=1) UriType theIdentifier,
@OperationParam(name = "filter", min=0, max=1) StringType theFilter) { @OperationParam(name = "filter", min=0, max=1) StringType theFilter,
RequestDetails theRequestDetails) {
//@formatter:on //@formatter:on
boolean haveId = theId != null && theId.hasIdPart(); boolean haveId = theId != null && theId.hasIdPart();
@ -69,7 +71,7 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst
try { try {
IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> dao = (IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept>) getDao(); IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> dao = (IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept>) getDao();
if (haveId) { if (haveId) {
return dao.expand(theId, toFilterString(theFilter)); return dao.expand(theId, toFilterString(theFilter), theRequestDetails);
} else if (haveIdentifier) { } else if (haveIdentifier) {
return dao.expandByIdentifier(theIdentifier.getValue(), toFilterString(theFilter)); return dao.expandByIdentifier(theIdentifier.getValue(), toFilterString(theFilter));
} else { } else {
@ -118,14 +120,15 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst
@OperationParam(name="system", min=0, max=1) UriType theSystem, @OperationParam(name="system", min=0, max=1) UriType theSystem,
@OperationParam(name="display", min=0, max=1) StringType theDisplay, @OperationParam(name="display", min=0, max=1) StringType theDisplay,
@OperationParam(name="coding", min=0, max=1) Coding theCoding, @OperationParam(name="coding", min=0, max=1) Coding theCoding,
@OperationParam(name="codeableConcept", min=0, max=1) CodeableConcept theCodeableConcept @OperationParam(name="codeableConcept", min=0, max=1) CodeableConcept theCodeableConcept,
RequestDetails theRequestDetails
) { ) {
//@formatter:on //@formatter:on
startRequest(theServletRequest); startRequest(theServletRequest);
try { try {
IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> dao = (IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept>) getDao(); IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> dao = (IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept>) getDao();
ValidateCodeResult result = dao.validateCode(theValueSetIdentifier, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept); ValidateCodeResult result = dao.validateCode(theValueSetIdentifier, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
Parameters retVal = new Parameters(); Parameters retVal = new Parameters();
retVal.addParameter().setName("result").setValue(new BooleanType(result.isResult())); retVal.addParameter().setName("result").setValue(new BooleanType(result.isResult()));
if (isNotBlank(result.getMessage())) { if (isNotBlank(result.getMessage())) {

View File

@ -0,0 +1,35 @@
package ca.uhn.fhir.jpa.search;
import org.hibernate.search.indexes.interceptor.EntityIndexingInterceptor;
import org.hibernate.search.indexes.interceptor.IndexingOverride;
import ca.uhn.fhir.jpa.entity.TermConcept;
public class DeferConceptIndexingInterceptor implements EntityIndexingInterceptor<TermConcept> {
@Override
public IndexingOverride onAdd(TermConcept theEntity) {
if (theEntity.getIndexStatus() == null) {
return IndexingOverride.SKIP;
}
return IndexingOverride.APPLY_DEFAULT;
}
@Override
public IndexingOverride onCollectionUpdate(TermConcept theEntity) {
return IndexingOverride.APPLY_DEFAULT;
}
@Override
public IndexingOverride onDelete(TermConcept theEntity) {
return IndexingOverride.APPLY_DEFAULT;
}
@Override
public IndexingOverride onUpdate(TermConcept theEntity) {
return onAdd(theEntity);
}
}

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.term;
*/ */
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
@ -32,10 +33,17 @@ import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType; import javax.persistence.PersistenceContextType;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.google.common.base.Stopwatch; import com.google.common.base.Stopwatch;
@ -50,6 +58,8 @@ import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.ObjectUtil;
@ -85,6 +95,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
protected EntityManager myEntityManager; protected EntityManager myEntityManager;
private boolean myProcessDeferred = true; private boolean myProcessDeferred = true;
private long myNextReindexPass;
private boolean addToSet(Set<TermConcept> theSetToPopulate, TermConcept theConcept) { private boolean addToSet(Set<TermConcept> theSetToPopulate, TermConcept theConcept) {
boolean retVal = theSetToPopulate.add(theConcept); boolean retVal = theSetToPopulate.add(theConcept);
@ -209,15 +220,6 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return cs; return cs;
} }
private void parentPids(TermConcept theNextConcept, Set<Long> theParentPids) {
for (TermConceptParentChildLink nextParentLink : theNextConcept.getParents()){
TermConcept parent = nextParentLink.getParent();
if (parent != null && theParentPids.add(parent.getId())) {
parentPids(parent, theParentPids);
}
}
}
private void persistChildren(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap<TermConcept, Object> theConceptsStack, int theTotalConcepts) { private void persistChildren(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap<TermConcept, Object> theConceptsStack, int theTotalConcepts) {
if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) { if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) {
return; return;
@ -231,12 +233,8 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
theConcept.setCodeSystem(theCodeSystem); theConcept.setCodeSystem(theCodeSystem);
theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED); theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED);
Set<Long> parentPids = new HashSet<Long>();
parentPids(theConcept, parentPids);
theConcept.setParentPids(parentPids);
if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) { if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) {
myConceptDao.save(theConcept); saveConcept(theConcept);
} else { } else {
myConceptsToSaveLater.add(theConcept); myConceptsToSaveLater.add(theConcept);
} }
@ -247,7 +245,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
for (TermConceptParentChildLink next : theConcept.getChildren()) { for (TermConceptParentChildLink next : theConcept.getChildren()) {
if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) { if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) {
myConceptParentChildLinkDao.save(next); saveConceptLink(next);
} else { } else {
myConceptLinksToSaveLater.add(next); myConceptLinksToSaveLater.add(next);
} }
@ -255,6 +253,44 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
} }
private void saveConceptLink(TermConceptParentChildLink next) {
if (next.getId() == null) {
myConceptParentChildLinkDao.save(next);
}
}
private int saveConcept(TermConcept theConcept) {
int retVal = 0;
retVal += ensureParentsSaved(theConcept.getParents());
if (theConcept.getId() == null || theConcept.getIndexStatus() == null) {
retVal++;
theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED);
myConceptDao.saveAndFlush(theConcept);
}
ourLog.trace("Saved {} and got PID {}", theConcept.getCode(), theConcept.getId());
return retVal;
}
private int ensureParentsSaved(Collection<TermConceptParentChildLink> theParents) {
ourLog.trace("Checking {} parents", theParents.size());
int retVal = 0;
for (TermConceptParentChildLink nextLink : theParents) {
if (nextLink.getRelationshipType() == RelationshipTypeEnum.ISA) {
TermConcept nextParent = nextLink.getParent();
retVal += ensureParentsSaved(nextParent.getParents());
if (nextParent.getId() == null) {
myConceptDao.saveAndFlush(nextParent);
retVal++;
ourLog.debug("Saved parent code {} and got id {}", nextParent.getCode(), nextParent.getId());
}
}
}
return retVal;
}
private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) { private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) {
if (theNext.getCodeSystem() != null) { if (theNext.getCodeSystem() != null) {
return; return;
@ -270,30 +306,76 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
@Override @Override
public synchronized void saveDeferred() { public synchronized void saveDeferred() {
if (!myProcessDeferred || ((myConceptsToSaveLater.isEmpty() && myConceptLinksToSaveLater.isEmpty()))) { if (!myProcessDeferred || ((myConceptsToSaveLater.isEmpty() && myConceptLinksToSaveLater.isEmpty()))) {
return; processReindexing();
} }
int codeCount = 0, relCount = 0; int codeCount = 0, relCount = 0;
StopWatch stopwatch = new StopWatch();
int count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptsToSaveLater.size()); int count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptsToSaveLater.size());
ourLog.info("Saving {} deferred concepts...", count); ourLog.info("Saving {} deferred concepts...", count);
while (codeCount < count && myConceptsToSaveLater.size() > 0) { while (codeCount < count && myConceptsToSaveLater.size() > 0) {
TermConcept next = myConceptsToSaveLater.remove(0); TermConcept next = myConceptsToSaveLater.remove(0);
myConceptDao.save(next); codeCount += saveConcept(next);
codeCount++;
} }
if (codeCount > 0) {
ourLog.info("Saved {} deferred concepts ({} codes remain and {} relationships remain) in {}ms ({}ms / code)", new Object[] {codeCount, myConceptsToSaveLater.size(), myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)});
}
if (codeCount == 0) { if (codeCount == 0) {
count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptLinksToSaveLater.size()); count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptLinksToSaveLater.size());
ourLog.info("Saving {} deferred concept relationships...", count); ourLog.info("Saving {} deferred concept relationships...", count);
while (relCount < count && myConceptLinksToSaveLater.size() > 0) { while (relCount < count && myConceptLinksToSaveLater.size() > 0) {
TermConceptParentChildLink next = myConceptLinksToSaveLater.remove(0); TermConceptParentChildLink next = myConceptLinksToSaveLater.remove(0);
myConceptParentChildLinkDao.save(next); saveConceptLink(next);
relCount++; relCount++;
} }
} }
ourLog.info("Saved {} deferred concepts ({} remain) and {} deferred relationships ({} remain)", new Object[] {codeCount, myConceptsToSaveLater.size(), relCount, myConceptLinksToSaveLater.size()}); if (relCount > 0) {
ourLog.info("Saved {} deferred relationships ({} remain) in {}ms ({}ms / code)", new Object[] {relCount, myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)});
}
if ((myConceptsToSaveLater.size() + myConceptLinksToSaveLater.size()) == 0) {
ourLog.info("All deferred concepts and relationships have now been synchronized to the database");
}
}
@Autowired
private PlatformTransactionManager myTransactionMgr;
private void processReindexing() {
if (System.currentTimeMillis() < myNextReindexPass) {
return;
}
TransactionTemplate tt = new TransactionTemplate(myTransactionMgr);
tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
int maxResult = 1000;
Page<TermConcept> resources = myConceptDao.findResourcesRequiringReindexing(new PageRequest(0, maxResult));
if (resources.hasContent() == false) {
myNextReindexPass = System.currentTimeMillis() + DateUtils.MILLIS_PER_MINUTE;
return;
}
ourLog.info("Indexing {} / {} concepts", resources.getContent().size(), resources.getTotalElements());
int count = 0;
StopWatch stopwatch = new StopWatch();
for (TermConcept resourceTable : resources) {
saveConcept(resourceTable);
count++;
}
ourLog.info("Indexed {} / {} concepts in {}ms - Avg {}ms / resource", new Object[] { count, resources.getContent().size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(count) });
}
});
} }
@Override @Override

View File

@ -29,6 +29,7 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryWrapperFilter;
import org.hibernate.search.jpa.FullTextEntityManager; import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.FullTextQuery; import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.query.dsl.BooleanJunction; import org.hibernate.search.query.dsl.BooleanJunction;
@ -78,56 +79,38 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
@Autowired @Autowired
private ValueSetExpander myValueSetExpander; private ValueSetExpander myValueSetExpander;
@Override private void addCodeIfNotAlreadyAdded(String system, ValueSetExpansionComponent retVal, Set<String> addedCodes, TermConcept nextConcept) {
public List<VersionIndependentConcept> expandValueSet(String theValueSet) { if (addedCodes.add(nextConcept.getCode())) {
ValueSet source = new ValueSet(); ValueSetExpansionContainsComponent contains = retVal.addContains();
source.getCompose().addImport(theValueSet); contains.setCode(nextConcept.getCode());
try { contains.setSystem(system);
ArrayList<VersionIndependentConcept> retVal = new ArrayList<VersionIndependentConcept>(); contains.setDisplay(nextConcept.getDisplay());
ValueSetExpansionOutcome outcome = myValueSetExpander.expand(source);
for (ValueSetExpansionContainsComponent next : outcome.getValueset().getExpansion().getContains()) {
retVal.add(new VersionIndependentConcept(next.getSystem(), next.getCode()));
}
return retVal;
} catch (Exception e) {
throw new InternalErrorException(e);
} }
} }
@Override private void addDisplayFilterExact(QueryBuilder qb, BooleanJunction<?> bool, ConceptSetFilterComponent nextFilter) {
@Transactional(propagation=Propagation.REQUIRED) bool.must(qb.phrase().onField("myDisplay").sentence(nextFilter.getValue()).createQuery());
public void storeNewCodeSystemVersion(String theSystem, TermCodeSystemVersion theCodeSystemVersion, RequestDetails theRequestDetails) { }
CodeSystem cs = new org.hl7.fhir.dstu3.model.CodeSystem();
cs.setUrl(theSystem);
cs.setContent(CodeSystemContentMode.NOTPRESENT);
DaoMethodOutcome createOutcome = myCodeSystemResourceDao.create(cs, "CodeSystem?url=" + UrlUtil.escape(theSystem), theRequestDetails);
IIdType csId = createOutcome.getId().toUnqualifiedVersionless();
if (createOutcome.getCreated() != Boolean.TRUE) {
CodeSystem existing = myCodeSystemResourceDao.read(csId, theRequestDetails);
csId = myCodeSystemResourceDao.update(existing, theRequestDetails).getId();
ourLog.info("Created new version of CodeSystem, got ID: {}", csId.toUnqualified().getValue());
}
ResourceTable resource = (ResourceTable) myCodeSystemResourceDao.readEntity(csId);
Long codeSystemResourcePid = resource.getId();
ourLog.info("CodeSystem resource has ID: {}", csId.getValue());
theCodeSystemVersion.setResource(resource);
theCodeSystemVersion.setResourceVersionId(resource.getVersion());
super.storeNewCodeSystemVersion(codeSystemResourcePid, theSystem, theCodeSystemVersion);
private void addDisplayFilterInexact(QueryBuilder qb, BooleanJunction<?> bool, ConceptSetFilterComponent nextFilter) {
//@formatter:off
Query textQuery = qb
.phrase()
.withSlop(2)
.onField("myDisplay").boostedTo(4.0f)
.andField("myDisplayEdgeNGram").boostedTo(2.0f)
//.andField("myDisplayNGram").boostedTo(1.0f)
//.andField("myDisplayPhonetic").boostedTo(0.5f)
.sentence(nextFilter.getValue().toLowerCase()).createQuery();
bool.must(textQuery);
//@formatter:on
} }
@Override @Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) { public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
String system = theInclude.getSystem(); String system = theInclude.getSystem();
ourLog.info("Starting expansion around code system: {}", system);
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system); TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system);
TermCodeSystemVersion csv = cs.getCurrentVersion(); TermCodeSystemVersion csv = cs.getCurrentVersion();
@ -167,27 +150,40 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery()); bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery());
for (ConceptSetFilterComponent nextFilter : theInclude.getFilter()) { for (ConceptSetFilterComponent nextFilter : theInclude.getFilter()) {
if (nextFilter.getProperty().equals("display") && nextFilter.getOp() == FilterOperator.EQUAL) { if (isNotBlank(nextFilter.getValue())) {
if (isNotBlank(nextFilter.getValue())) { if (nextFilter.getProperty().equals("display:exact") && nextFilter.getOp() == FilterOperator.EQUAL) {
bool.must(qb.phrase().onField("myDisplay").sentence(nextFilter.getValue()).createQuery()); addDisplayFilterExact(qb, bool, nextFilter);
} } else if (nextFilter.getProperty().equals("display") && nextFilter.getOp() == FilterOperator.EQUAL) {
} else if (nextFilter.getOp() == FilterOperator.ISA) { if (nextFilter.getValue().trim().contains(" ")) {
if (isNotBlank(nextFilter.getValue())) { addDisplayFilterExact(qb, bool, nextFilter);
} else {
addDisplayFilterInexact(qb, bool, nextFilter);
}
} else if ((nextFilter.getProperty().equals("concept") || nextFilter.getProperty().equals("code")) && nextFilter.getOp() == FilterOperator.ISA) {
TermConcept code = super.findCode(system, nextFilter.getValue()); TermConcept code = super.findCode(system, nextFilter.getValue());
if (code == null) {
throw new InvalidRequestException("Invalid filter criteria - code does not exist: {" + system + "}" + nextFilter.getValue());
}
ourLog.info(" * Filtering on codes with a parent of {}/{}/{}", code.getId(), code.getCode(), code.getDisplay());
bool.must(qb.keyword().onField("myParentPids").matching("" + code.getId()).createQuery()); bool.must(qb.keyword().onField("myParentPids").matching("" + code.getId()).createQuery());
} else {
throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]");
} }
} else {
throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]");
} }
} }
Query luceneQuery = bool.createQuery(); Query luceneQuery = bool.createQuery();
FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class); FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class);
jpaQuery.setMaxResults(1000);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<TermConcept> result = jpaQuery.getResultList(); List<TermConcept> result = jpaQuery.getResultList();
for (TermConcept nextConcept : result) { for (TermConcept nextConcept : result) {
addCodeIfNotAlreadyAdded(system, retVal, addedCodes, nextConcept); addCodeIfNotAlreadyAdded(system, retVal, addedCodes, nextConcept);
} }
retVal.setTotal(jpaQuery.getResultSize());
} }
if (!haveIncludeCriteria) { if (!haveIncludeCriteria) {
@ -196,19 +192,29 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
addCodeIfNotAlreadyAdded(system, retVal, addedCodes, nextConcept); addCodeIfNotAlreadyAdded(system, retVal, addedCodes, nextConcept);
} }
} }
retVal.setTotal(retVal.getContains().size());
return retVal; return retVal;
} }
private void addCodeIfNotAlreadyAdded(String system, ValueSetExpansionComponent retVal, Set<String> addedCodes, TermConcept nextConcept) { @Override
if (addedCodes.add(nextConcept.getCode())) { public List<VersionIndependentConcept> expandValueSet(String theValueSet) {
ValueSetExpansionContainsComponent contains = retVal.addContains(); ValueSet source = new ValueSet();
contains.setCode(nextConcept.getCode()); source.getCompose().addImport(theValueSet);
contains.setSystem(system); try {
contains.setDisplay(nextConcept.getDisplay()); ArrayList<VersionIndependentConcept> retVal = new ArrayList<VersionIndependentConcept>();
ValueSetExpansionOutcome outcome = myValueSetExpander.expand(source);
for (ValueSetExpansionContainsComponent next : outcome.getValueset().getExpansion().getContains()) {
retVal.add(new VersionIndependentConcept(next.getSystem(), next.getCode()));
}
return retVal;
} catch (Exception e) {
throw new InternalErrorException(e);
} }
} }
@Override @Override
@ -238,6 +244,33 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
return super.supportsSystem(theSystem); return super.supportsSystem(theSystem);
} }
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void storeNewCodeSystemVersion(String theSystem, TermCodeSystemVersion theCodeSystemVersion, RequestDetails theRequestDetails) {
CodeSystem cs = new org.hl7.fhir.dstu3.model.CodeSystem();
cs.setUrl(theSystem);
cs.setContent(CodeSystemContentMode.NOTPRESENT);
DaoMethodOutcome createOutcome = myCodeSystemResourceDao.create(cs, "CodeSystem?url=" + UrlUtil.escape(theSystem), theRequestDetails);
IIdType csId = createOutcome.getId().toUnqualifiedVersionless();
if (createOutcome.getCreated() != Boolean.TRUE) {
CodeSystem existing = myCodeSystemResourceDao.read(csId, theRequestDetails);
csId = myCodeSystemResourceDao.update(existing, theRequestDetails).getId();
ourLog.info("Created new version of CodeSystem, got ID: {}", csId.toUnqualified().getValue());
}
ResourceTable resource = (ResourceTable) myCodeSystemResourceDao.readEntity(csId);
Long codeSystemResourcePid = resource.getId();
ourLog.info("CodeSystem resource has ID: {}", csId.getValue());
theCodeSystemVersion.setResource(resource);
theCodeSystemVersion.setResourceVersionId(resource.getVersion());
super.storeNewCodeSystemVersion(codeSystemResourcePid, theSystem, theCodeSystemVersion);
}
@CoverageIgnore @CoverageIgnore
@Override @Override
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {

View File

@ -24,15 +24,12 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader; import java.io.Reader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -47,7 +44,6 @@ import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord; import org.apache.commons.csv.CSVRecord;
import org.apache.commons.csv.QuoteMode; import org.apache.commons.csv.QuoteMode;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
@ -102,6 +98,8 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
} }
ourLog.info(b.toString(), theConcept.getCode()); ourLog.info(b.toString(), theConcept.getCode());
childIter.remove(); childIter.remove();
nextChild.getParents().remove(next);
} else { } else {
dropCircularRefs(nextChild, theChain, theCode2concept, theCircularCounter); dropCircularRefs(nextChild, theChain, theCode2concept, theCircularCounter);
} }

View File

@ -32,4 +32,14 @@ public class StopWatch {
return retVal; return retVal;
} }
public long getMillis() {
long now = System.currentTimeMillis();
long retVal = now - myStarted;
return retVal;
}
public double getMillisPerOperation(int theNumOperations) {
return ((double)getMillis()) / Math.max(1.0, theNumOperations);
}
} }

View File

@ -16,7 +16,6 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.composite.CodingDt;
@ -25,7 +24,6 @@ import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test { public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
@ -58,7 +56,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
StringDt display = null; StringDt display = null;
CodingDt coding = null; CodingDt coding = null;
CodeableConceptDt codeableConcept = null; CodeableConceptDt codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertFalse(result.isResult()); assertFalse(result.isResult());
} }
@ -71,7 +69,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
StringDt display = null; StringDt display = null;
CodingDt coding = null; CodingDt coding = null;
CodeableConceptDt codeableConcept = null; CodeableConceptDt codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult()); assertTrue(result.isResult());
assertEquals("Systolic blood pressure--expiration", result.getDisplay()); assertEquals("Systolic blood pressure--expiration", result.getDisplay());
} }
@ -85,7 +83,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
StringDt display = null; StringDt display = null;
CodingDt coding = null; CodingDt coding = null;
CodeableConceptDt codeableConcept = null; CodeableConceptDt codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult()); assertTrue(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
} }
@ -99,7 +97,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
StringDt display = new StringDt("Systolic blood pressure at First encounterXXXX"); StringDt display = new StringDt("Systolic blood pressure at First encounterXXXX");
CodingDt coding = null; CodingDt coding = null;
CodeableConceptDt codeableConcept = null; CodeableConceptDt codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertFalse(result.isResult()); assertFalse(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
} }
@ -113,7 +111,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
StringDt display = new StringDt("Systolic blood pressure at First encounter"); StringDt display = new StringDt("Systolic blood pressure at First encounter");
CodingDt coding = null; CodingDt coding = null;
CodeableConceptDt codeableConcept = null; CodeableConceptDt codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult()); assertTrue(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
} }
@ -127,7 +125,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
StringDt display = null; StringDt display = null;
CodingDt coding = null; CodingDt coding = null;
CodeableConceptDt codeableConcept = new CodeableConceptDt("http://loinc.org", "11378-7"); CodeableConceptDt codeableConcept = new CodeableConceptDt("http://loinc.org", "11378-7");
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult()); assertTrue(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
} }
@ -141,7 +139,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
StringDt display = null; StringDt display = null;
CodingDt coding = null; CodingDt coding = null;
CodeableConceptDt codeableConcept = null; CodeableConceptDt codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult()); assertTrue(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
} }
@ -150,7 +148,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
public void testExpandById() throws IOException { public void testExpandById() throws IOException {
String resp; String resp;
ValueSet expanded = myValueSetDao.expand(myExtensionalVsId, null); ValueSet expanded = myValueSetDao.expand(myExtensionalVsId, null, mySrd);
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
// @formatter:off // @formatter:off
@ -175,7 +173,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
* Filter with display name * Filter with display name
*/ */
expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic")); expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic"), mySrd);
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
//@formatter:off //@formatter:off
@ -188,7 +186,7 @@ public class FhirResourceDaoValueSetDstu2Test extends BaseJpaDstu2Test {
* Filter with code * Filter with code
*/ */
expanded = myValueSetDao.expand(myExtensionalVsId, ("11378")); expanded = myValueSetDao.expand(myExtensionalVsId, ("11378"), mySrd);
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
//@formatter:off //@formatter:off

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.dao.dstu3; package ca.uhn.fhir.jpa.dao.dstu3;
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsStringIgnoringCase;
import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@ -37,142 +38,25 @@ import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.TokenParamModifier; import ca.uhn.fhir.rest.param.TokenParamModifier;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
private static final String URL_MY_VALUE_SET = "http://example.com/my_value_set";
private static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3TerminologyTest.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3TerminologyTest.class);
public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system";
public static final String URL_MY_VALUE_SET = "http://example.com/my_value_set";
@AfterClass @After
public static void afterClassClearContext() { public void after() {
TestUtil.clearAllStaticFieldsForUnitTest(); myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize());
} }
@Test @Before
public void testCodeSystemWithDefinedCodes() { public void before() {
//@formatter:off myDaoConfig.setMaximumExpansionSize(5000);
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
codeSystem
.addConcept().setCode("A").setDisplay("Code A")
.addConcept(new ConceptDefinitionComponent().setCode("AA").setDisplay("Code AA"))
.addConcept(new ConceptDefinitionComponent().setCode("AB").setDisplay("Code AB"));
codeSystem
.addConcept().setCode("B").setDisplay("Code A")
.addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code AA"))
.addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code AB"));
//@formatter:on
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
Set<TermConcept> codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "A");
assertThat(toCodes(codes), containsInAnyOrder("A", "AA", "AB"));
} }
@Test
@Ignore
public void testSearchCodeInEmptyValueSet() {
ValueSet valueSet = new ValueSet();
valueSet.setUrl(URL_MY_VALUE_SET);
myValueSetDao.create(valueSet, mySrd);
Observation obsAA = new Observation();
obsAA.setStatus(ObservationStatus.FINAL);
obsAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("AA");
myObservationDao.create(obsAA, mySrd).getId().toUnqualifiedVersionless();
Observation obsBA = new Observation();
obsBA.setStatus(ObservationStatus.FINAL);
obsBA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("BA");
myObservationDao.create(obsBA, mySrd).getId().toUnqualifiedVersionless();
Observation obsCA = new Observation();
obsCA.setStatus(ObservationStatus.FINAL);
obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA");
myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params;
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
params.add(Observation.SP_STATUS, new TokenParam(null, "final"));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
}
@Test
public void testSearchCodeInLocalCodesystem() {
createLocalCsAndVs();
Observation obsAA = new Observation();
obsAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("AA");
IIdType idAA = myObservationDao.create(obsAA, mySrd).getId().toUnqualifiedVersionless();
Observation obsBA = new Observation();
obsBA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("BA");
IIdType idBA = myObservationDao.create(obsBA, mySrd).getId().toUnqualifiedVersionless();
Observation obsCA = new Observation();
obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA");
IIdType idCA = myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idAA.getValue(), idBA.getValue()));
}
@Test
public void testSearchCodeInExternalCodesystem() {
createExternalCsAndLocalVs();
Observation obsPA = new Observation();
obsPA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("ParentA");
IIdType idPA = myObservationDao.create(obsPA, mySrd).getId().toUnqualifiedVersionless();
Observation obsAAA = new Observation();
obsAAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("childAAA");
IIdType idAAA = myObservationDao.create(obsAAA, mySrd).getId().toUnqualifiedVersionless();
Observation obsAAB = new Observation();
obsAAB.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("childAAB");
IIdType idAAB = myObservationDao.create(obsAAB, mySrd).getId().toUnqualifiedVersionless();
Observation obsCA = new Observation();
obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA");
IIdType idCA = myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.BELOW));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idAAA.getValue(), idAAB.getValue()));
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.ABOVE));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idPA.getValue()));
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idPA.getValue(), idAAA.getValue(), idAAB.getValue()));
}
private void createExternalCsAndLocalVs() {
CodeSystem codeSystem = createExternalCs();
createLocalVs(codeSystem);
}
private CodeSystem createExternalCs() { private CodeSystem createExternalCs() {
CodeSystem codeSystem = new CodeSystem(); CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM); codeSystem.setUrl(URL_MY_CODE_SYSTEM);
@ -203,179 +87,97 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B"); TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B");
cs.getConcepts().add(parentB); cs.getConcepts().add(parentB);
TermConcept childBA = new TermConcept(cs, "childBA").setDisplay("Child BA");
childBA.addChild(childAAB, RelationshipTypeEnum.ISA);
parentB.addChild(childBA, RelationshipTypeEnum.ISA);
TermConcept parentC = new TermConcept(cs, "ParentC").setDisplay("Parent C");
cs.getConcepts().add(parentC);
TermConcept childCA = new TermConcept(cs, "childCA").setDisplay("Child CA");
parentC.addChild(childCA, RelationshipTypeEnum.ISA);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs); myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs);
return codeSystem; return codeSystem;
} }
private void createExternalCsAndLocalVs() {
CodeSystem codeSystem = createExternalCs();
createLocalVs(codeSystem);
}
private void createLocalCsAndVs() {
//@formatter:off
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
codeSystem
.addConcept().setCode("A").setDisplay("Code A")
.addConcept(new ConceptDefinitionComponent().setCode("AA").setDisplay("Code AA")
.addConcept(new ConceptDefinitionComponent().setCode("AAA").setDisplay("Code AAA"))
)
.addConcept(new ConceptDefinitionComponent().setCode("AB").setDisplay("Code AB"));
codeSystem
.addConcept().setCode("B").setDisplay("Code B")
.addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code BA"))
.addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code BB"));
//@formatter:on
myCodeSystemDao.create(codeSystem, mySrd);
createLocalVs(codeSystem);
}
private void createLocalVs(CodeSystem codeSystem) {
ValueSet valueSet = new ValueSet();
valueSet.setUrl(URL_MY_VALUE_SET);
valueSet.getCompose().addInclude().setSystem(codeSystem.getUrl());
myValueSetDao.create(valueSet, mySrd);
}
@Test @Test
public void testSearchCodeBelowAndAboveUnknownCodeSystem() { public void testCodeSystemCreateDuplicateFails() {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
SearchParameterMap params = new SearchParameterMap(); codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
try {
myCodeSystemDao.create(codeSystem, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Can not create multiple code systems with URI \"http://example.com/my_code_system\", already have one with resource ID: CodeSystem/" + id.getIdPart(), e.getMessage());
}
}
@Test
public void testCodeSystemWithDefinedCodes() {
//@formatter:off
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
codeSystem
.addConcept().setCode("A").setDisplay("Code A")
.addConcept(new ConceptDefinitionComponent().setCode("AA").setDisplay("Code AA"))
.addConcept(new ConceptDefinitionComponent().setCode("AB").setDisplay("Code AB"));
codeSystem
.addConcept().setCode("B").setDisplay("Code A")
.addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code AA"))
.addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code AB"));
//@formatter:on
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.BELOW)); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
Set<TermConcept> codes = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "A");
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.ABOVE)); assertThat(toCodes(codes), containsInAnyOrder("A", "AA", "AB"));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
} }
@Test
public void testSearchCodeInFhirCodesystem() {
createLocalCsAndVs();
AuditEvent aeIn1 = new AuditEvent();
aeIn1.getType().setSystem("http://nema.org/dicom/dicm").setCode("110102");
IIdType idIn1 = myAuditEventDao.create(aeIn1, mySrd).getId().toUnqualifiedVersionless();
AuditEvent aeIn2 = new AuditEvent();
aeIn2.getType().setSystem("http://hl7.org/fhir/audit-event-type").setCode("rest");
IIdType idIn2 = myAuditEventDao.create(aeIn2, mySrd).getId().toUnqualifiedVersionless();
AuditEvent aeOut1 = new AuditEvent();
aeOut1.getType().setSystem("http://example.com").setCode("foo");
IIdType idOut1 = myAuditEventDao.create(aeOut1, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params = new SearchParameterMap();
params.add(AuditEvent.SP_TYPE, new TokenParam(null, "http://hl7.org/fhir/ValueSet/audit-event-type").setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myAuditEventDao.search(params)), containsInAnyOrder(idIn1.getValue(), idIn2.getValue()));
params = new SearchParameterMap();
params.add(AuditEvent.SP_TYPE, new TokenParam(null, "http://hl7.org/fhir/ValueSet/v3-PurposeOfUse").setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myAuditEventDao.search(params)), empty());
}
/**
* Can't currently abort costly
*/
@Test
@Ignore
public void testRefuseCostlyExpansionFhirCodesystem() {
createLocalCsAndVs();
myDaoConfig.setMaximumExpansionSize(1);
SearchParameterMap params = new SearchParameterMap();
params.add(AuditEvent.SP_TYPE, new TokenParam(null, "http://hl7.org/fhir/ValueSet/audit-event-type").setModifier(TokenParamModifier.IN));
try {
myAuditEventDao.search(params);
fail();
} catch (InvalidRequestException e) {
assertEquals("", e.getMessage());
}
}
@Test
public void testRefuseCostlyExpansionLocalCodesystem() {
createLocalCsAndVs();
myDaoConfig.setMaximumExpansionSize(1);
SearchParameterMap params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "AAA").setModifier(TokenParamModifier.ABOVE));
try {
myObservationDao.search(params);
fail();
} catch (InvalidRequestException e) {
assertEquals("Expansion of ValueSet produced too many codes (maximum 1) - Operation aborted!", e.getMessage());
}
}
@Test
public void testExpandWithSystemAndCodesInLocalValueSet() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("A");
include.addConcept().setCode("AA");
include.addConcept().setCode("AAA");
include.addConcept().setCode("AB");
ValueSet result = myValueSetDao.expand(vs, null);
String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(encoded);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("A", "AA", "AAA", "AB"));
int idx = codes.indexOf("AAA");
assertEquals("AAA", result.getExpansion().getContains().get(idx).getCode());
assertEquals("Code AAA", result.getExpansion().getContains().get(idx).getDisplay());
assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem());
// ValueSet expansion = myValueSetDao.expandByIdentifier(URL_MY_VALUE_SET, "cervical");
// ValueSet expansion = myValueSetDao.expandByIdentifier(URL_MY_VALUE_SET, "cervical");
//
}
@Test
public void testExpandWithSystemAndCodesAndFilterInExternalValueSet() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ParentA");
include.addConcept().setCode("childAA");
include.addConcept().setCode("childAAA");
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("Parent B");
ValueSet result = myValueSetDao.expand(vs, null);
String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(encoded);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAA", "ParentB"));
int idx = codes.indexOf("childAA");
assertEquals("childAA", result.getExpansion().getContains().get(idx).getCode());
assertEquals("Child AA", result.getExpansion().getContains().get(idx).getDisplay());
assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem());
}
@Test
public void testIndexingIsDeferredForLargeCodeSystems() {
myDaoConfig.setDeferIndexingForCodesystemsOfSize(1);
myTermSvc.setProcessDeferred(false);
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setProperty("display").setOp(FilterOperator.ISA).setValue("ParentA");
ValueSet result = myValueSetDao.expand(vs, null);
String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(encoded);
assertEquals(0, result.getExpansion().getContains().size());
myTermSvc.setProcessDeferred(true);
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
result = myValueSetDao.expand(vs, null);
encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(encoded);
assertEquals(4, result.getExpansion().getContains().size());
}
@Test @Test
public void testExpandWithExcludeInExternalValueSet() { public void testExpandWithExcludeInExternalValueSet() {
createExternalCsAndLocalVs(); createExternalCsAndLocalVs();
@ -395,8 +197,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
ourLog.info(encoded); ourLog.info(encoded);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains()); ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA", "ParentB", "childAB", "childAAB")); assertThat(codes, containsInAnyOrder("ParentA", "ParentB", "childAB", "childAAB", "ParentC", "childBA", "childCA"));
} }
@Test @Test
@ -421,34 +222,6 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
} }
} }
@Test
public void testExpandWithSystemAndCodesAndFilterKeywordInLocalValueSet() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
// include.addConcept().setCode("A");
// include.addConcept().setCode("AA");
// include.addConcept().setCode("AAA");
// include.addConcept().setCode("AB");
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("AAA");
ValueSet result = myValueSetDao.expand(vs, null);
String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(encoded);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("AAA"));
assertEquals("AAA", result.getExpansion().getContains().get(0).getCode());
assertEquals("Code AAA", result.getExpansion().getContains().get(0).getDisplay());
assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(0).getSystem());
//
}
@Test @Test
public void testExpandWithNoResultsInLocalValueSet1() { public void testExpandWithNoResultsInLocalValueSet1() {
createLocalCsAndVs(); createLocalCsAndVs();
@ -484,14 +257,202 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
} }
} }
private ArrayList<String> toCodesContains(List<ValueSetExpansionContainsComponent> theContains) {
ArrayList<String> retVal = new ArrayList<String>();
for (ValueSetExpansionContainsComponent next : theContains) { @Test
retVal.add(next.getCode()); public void testExpandWithSystemAndCodesAndFilterInExternalValueSet() {
} createExternalCsAndLocalVs();
return retVal;
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ParentA");
include.addConcept().setCode("childAA");
include.addConcept().setCode("childAAA");
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("Parent B");
ValueSet result = myValueSetDao.expand(vs, null);
String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(encoded);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAA", "ParentB"));
int idx = codes.indexOf("childAA");
assertEquals("childAA", result.getExpansion().getContains().get(idx).getCode());
assertEquals("Child AA", result.getExpansion().getContains().get(idx).getDisplay());
assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem());
} }
@Test
public void testExpandWithDisplayInExternalValueSetFuzzyMatching() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("parent a");
ValueSet result = myValueSetDao.expand(vs, null);
String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(encoded);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA"));
vs = new ValueSet();
include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("pare");
result = myValueSetDao.expand(vs, null);
encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(encoded);
codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA", "ParentB", "ParentC"));
vs = new ValueSet();
include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setProperty("display:exact").setOp(FilterOperator.EQUAL).setValue("pare");
result = myValueSetDao.expand(vs, null);
encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(encoded);
codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, empty());
}
@Test
public void testExpandWithSystemAndCodesAndFilterKeywordInLocalValueSet() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
// include.addConcept().setCode("A");
// include.addConcept().setCode("AA");
// include.addConcept().setCode("AAA");
// include.addConcept().setCode("AB");
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("AAA");
ValueSet result = myValueSetDao.expand(vs, null);
String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(encoded);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("AAA"));
assertEquals("AAA", result.getExpansion().getContains().get(0).getCode());
assertEquals("Code AAA", result.getExpansion().getContains().get(0).getDisplay());
assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(0).getSystem());
//
}
@Test
public void testExpandWithSystemAndCodesInLocalValueSet() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("A");
include.addConcept().setCode("AA");
include.addConcept().setCode("AAA");
include.addConcept().setCode("AB");
ValueSet result = myValueSetDao.expand(vs, null);
String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(encoded);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("A", "AA", "AAA", "AB"));
int idx = codes.indexOf("AAA");
assertEquals("AAA", result.getExpansion().getContains().get(idx).getCode());
assertEquals("Code AAA", result.getExpansion().getContains().get(idx).getDisplay());
assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem());
// ValueSet expansion = myValueSetDao.expandByIdentifier(URL_MY_VALUE_SET, "cervical");
// ValueSet expansion = myValueSetDao.expandByIdentifier(URL_MY_VALUE_SET, "cervical");
//
}
@Test
public void testIndexingIsDeferredForLargeCodeSystems() {
myDaoConfig.setDeferIndexingForCodesystemsOfSize(1);
myTermSvc.setProcessDeferred(false);
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("ParentA");
ValueSet result = myValueSetDao.expand(vs, null);
String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(encoded);
assertEquals(0, result.getExpansion().getContains().size());
myTermSvc.setProcessDeferred(true);
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
vs = new ValueSet();
include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("ParentA");
result = myValueSetDao.expand(vs, null);
encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
ourLog.info(encoded);
assertEquals(4, result.getExpansion().getContains().size());
assertThat(encoded, containsStringIgnoringCase("<code value=\"childAAB\"/>"));
}
/**
* Can't currently abort costly
*/
@Test
@Ignore
public void testRefuseCostlyExpansionFhirCodesystem() {
createLocalCsAndVs();
myDaoConfig.setMaximumExpansionSize(1);
SearchParameterMap params = new SearchParameterMap();
params.add(AuditEvent.SP_TYPE, new TokenParam(null, "http://hl7.org/fhir/ValueSet/audit-event-type").setModifier(TokenParamModifier.IN));
try {
myAuditEventDao.search(params);
fail();
} catch (InvalidRequestException e) {
assertEquals("", e.getMessage());
}
}
@Test
public void testRefuseCostlyExpansionLocalCodesystem() {
createLocalCsAndVs();
myDaoConfig.setMaximumExpansionSize(1);
SearchParameterMap params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "AAA").setModifier(TokenParamModifier.ABOVE));
try {
myObservationDao.search(params);
fail();
} catch (InvalidRequestException e) {
assertEquals("Expansion of ValueSet produced too many codes (maximum 1) - Operation aborted!", e.getMessage());
}
}
@Test @Test
public void testSearchCodeAboveLocalCodesystem() { public void testSearchCodeAboveLocalCodesystem() {
@ -519,16 +480,23 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
} }
@Before @Test
public void before() { public void testSearchCodeBelowAndAboveUnknownCodeSystem() {
myDaoConfig.setMaximumExpansionSize(5000);
SearchParameterMap params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.BELOW));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.ABOVE));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
} }
@After
public void after() {
myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize());
}
@Test @Test
public void testSearchCodeBelowLocalCodesystem() { public void testSearchCodeBelowLocalCodesystem() {
createLocalCsAndVs(); createLocalCsAndVs();
@ -555,52 +523,134 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
} }
private void createLocalCsAndVs() { @Test
//@formatter:off @Ignore
CodeSystem codeSystem = new CodeSystem(); public void testSearchCodeInEmptyValueSet() {
codeSystem.setUrl(URL_MY_CODE_SYSTEM); ValueSet valueSet = new ValueSet();
codeSystem.setContent(CodeSystemContentMode.COMPLETE); valueSet.setUrl(URL_MY_VALUE_SET);
codeSystem myValueSetDao.create(valueSet, mySrd);
.addConcept().setCode("A").setDisplay("Code A")
.addConcept(new ConceptDefinitionComponent().setCode("AA").setDisplay("Code AA") Observation obsAA = new Observation();
.addConcept(new ConceptDefinitionComponent().setCode("AAA").setDisplay("Code AAA")) obsAA.setStatus(ObservationStatus.FINAL);
) obsAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("AA");
.addConcept(new ConceptDefinitionComponent().setCode("AB").setDisplay("Code AB")); myObservationDao.create(obsAA, mySrd).getId().toUnqualifiedVersionless();
codeSystem
.addConcept().setCode("B").setDisplay("Code B")
.addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code BA"))
.addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code BB"));
//@formatter:on
myCodeSystemDao.create(codeSystem, mySrd);
createLocalVs(codeSystem); Observation obsBA = new Observation();
obsBA.setStatus(ObservationStatus.FINAL);
obsBA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("BA");
myObservationDao.create(obsBA, mySrd).getId().toUnqualifiedVersionless();
Observation obsCA = new Observation();
obsCA.setStatus(ObservationStatus.FINAL);
obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA");
myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params;
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
params.add(Observation.SP_STATUS, new TokenParam(null, "final"));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
}
@Test
public void testSearchCodeInExternalCodesystem() {
createExternalCsAndLocalVs();
Observation obsPA = new Observation();
obsPA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("ParentA");
IIdType idPA = myObservationDao.create(obsPA, mySrd).getId().toUnqualifiedVersionless();
Observation obsAAA = new Observation();
obsAAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("childAAA");
IIdType idAAA = myObservationDao.create(obsAAA, mySrd).getId().toUnqualifiedVersionless();
Observation obsAAB = new Observation();
obsAAB.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("childAAB");
IIdType idAAB = myObservationDao.create(obsAAB, mySrd).getId().toUnqualifiedVersionless();
Observation obsCA = new Observation();
obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA");
IIdType idCA = myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.BELOW));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idAAA.getValue(), idAAB.getValue()));
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.ABOVE));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idPA.getValue()));
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idPA.getValue(), idAAA.getValue(), idAAB.getValue()));
}
@Test
public void testSearchCodeInFhirCodesystem() {
createLocalCsAndVs();
AuditEvent aeIn1 = new AuditEvent();
aeIn1.getType().setSystem("http://nema.org/dicom/dicm").setCode("110102");
IIdType idIn1 = myAuditEventDao.create(aeIn1, mySrd).getId().toUnqualifiedVersionless();
AuditEvent aeIn2 = new AuditEvent();
aeIn2.getType().setSystem("http://hl7.org/fhir/audit-event-type").setCode("rest");
IIdType idIn2 = myAuditEventDao.create(aeIn2, mySrd).getId().toUnqualifiedVersionless();
AuditEvent aeOut1 = new AuditEvent();
aeOut1.getType().setSystem("http://example.com").setCode("foo");
IIdType idOut1 = myAuditEventDao.create(aeOut1, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params = new SearchParameterMap();
params.add(AuditEvent.SP_TYPE, new TokenParam(null, "http://hl7.org/fhir/ValueSet/audit-event-type").setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myAuditEventDao.search(params)), containsInAnyOrder(idIn1.getValue(), idIn2.getValue()));
params = new SearchParameterMap();
params.add(AuditEvent.SP_TYPE, new TokenParam(null, "http://hl7.org/fhir/ValueSet/v3-PurposeOfUse").setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myAuditEventDao.search(params)), empty());
}
@Test
public void testSearchCodeInLocalCodesystem() {
createLocalCsAndVs();
Observation obsAA = new Observation();
obsAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("AA");
IIdType idAA = myObservationDao.create(obsAA, mySrd).getId().toUnqualifiedVersionless();
Observation obsBA = new Observation();
obsBA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("BA");
IIdType idBA = myObservationDao.create(obsBA, mySrd).getId().toUnqualifiedVersionless();
Observation obsCA = new Observation();
obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA");
IIdType idCA = myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idAA.getValue(), idBA.getValue()));
} }
private void createLocalVs(CodeSystem codeSystem) { private ArrayList<String> toCodesContains(List<ValueSetExpansionContainsComponent> theContains) {
ValueSet valueSet = new ValueSet(); ArrayList<String> retVal = new ArrayList<String>();
valueSet.setUrl(URL_MY_VALUE_SET); for (ValueSetExpansionContainsComponent next : theContains) {
valueSet.getCompose().addInclude().setSystem(codeSystem.getUrl()); retVal.add(next.getCode());
myValueSetDao.create(valueSet, mySrd); }
return retVal;
} }
@Test @AfterClass
public void testCodeSystemCreateDuplicateFails() { public static void afterClassClearContext() {
CodeSystem codeSystem = new CodeSystem(); TestUtil.clearAllStaticFieldsForUnitTest();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
try {
myCodeSystemDao.create(codeSystem, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Can not create multiple code systems with URI \"http://example.com/my_code_system\", already have one with resource ID: CodeSystem/" + id.getIdPart(), e.getMessage());
}
} }

View File

@ -26,7 +26,6 @@ import org.junit.Test;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test { public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test {
@ -62,7 +61,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test {
StringType display = null; StringType display = null;
Coding coding = null; Coding coding = null;
CodeableConcept codeableConcept = null; CodeableConcept codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertFalse(result.isResult()); assertFalse(result.isResult());
} }
@ -75,7 +74,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test {
StringType display = null; StringType display = null;
Coding coding = null; Coding coding = null;
CodeableConcept codeableConcept = null; CodeableConcept codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult()); assertTrue(result.isResult());
assertEquals("Systolic blood pressure--expiration", result.getDisplay()); assertEquals("Systolic blood pressure--expiration", result.getDisplay());
} }
@ -89,7 +88,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test {
StringType display = null; StringType display = null;
Coding coding = null; Coding coding = null;
CodeableConcept codeableConcept = null; CodeableConcept codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult()); assertTrue(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
} }
@ -103,7 +102,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test {
StringType display = new StringType("Systolic blood pressure at First encounterXXXX"); StringType display = new StringType("Systolic blood pressure at First encounterXXXX");
Coding coding = null; Coding coding = null;
CodeableConcept codeableConcept = null; CodeableConcept codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertFalse(result.isResult()); assertFalse(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
} }
@ -117,7 +116,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test {
StringType display = new StringType("Systolic blood pressure at First encounter"); StringType display = new StringType("Systolic blood pressure at First encounter");
Coding coding = null; Coding coding = null;
CodeableConcept codeableConcept = null; CodeableConcept codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult()); assertTrue(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
} }
@ -132,7 +131,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test {
Coding coding = null; Coding coding = null;
CodeableConcept codeableConcept = new CodeableConcept(); CodeableConcept codeableConcept = new CodeableConcept();
codeableConcept.addCoding().setSystem("http://acme.org").setCode("11378-7"); codeableConcept.addCoding().setSystem("http://acme.org").setCode("11378-7");
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult()); assertTrue(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
} }
@ -146,7 +145,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test {
StringType display = null; StringType display = null;
Coding coding = null; Coding coding = null;
CodeableConcept codeableConcept = null; CodeableConcept codeableConcept = null;
ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept); ValidateCodeResult result = myValueSetDao.validateCode(valueSetIdentifier, id, code, system, display, coding, codeableConcept, mySrd);
assertTrue(result.isResult()); assertTrue(result.isResult());
assertEquals("Systolic blood pressure at First encounter", result.getDisplay()); assertEquals("Systolic blood pressure at First encounter", result.getDisplay());
} }
@ -155,7 +154,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test {
public void testExpandById() throws IOException { public void testExpandById() throws IOException {
String resp; String resp;
ValueSet expanded = myValueSetDao.expand(myExtensionalVsId, null); ValueSet expanded = myValueSetDao.expand(myExtensionalVsId, null, mySrd);
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
assertThat(resp, containsString("<ValueSet xmlns=\"http://hl7.org/fhir\">")); assertThat(resp, containsString("<ValueSet xmlns=\"http://hl7.org/fhir\">"));
@ -176,7 +175,7 @@ public class FhirResourceDaoDstu3ValueSetTest extends BaseJpaDstu3Test {
* Filter with display name * Filter with display name
*/ */
expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic")); expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic"), mySrd);
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
//@formatter:off //@formatter:off

View File

@ -24,7 +24,6 @@ import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
public class ResourceProviderDstu2ValueSetTest extends BaseResourceProviderDstu2Test { public class ResourceProviderDstu2ValueSetTest extends BaseResourceProviderDstu2Test {
@ -251,7 +250,7 @@ public class ResourceProviderDstu2ValueSetTest extends BaseResourceProviderDstu2
expanded = (ValueSet) respParam.getParameter().get(0).getResource(); expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on //@formatter:on
expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic")); expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic"), mySrd);
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp); ourLog.info(resp);
//@formatter:off //@formatter:off

View File

@ -1,9 +1,12 @@
package ca.uhn.fhir.jpa.provider.dstu3; package ca.uhn.fhir.jpa.provider.dstu3;
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest.*;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.containsStringIgnoringCase;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.stringContainsInOrder; import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@ -12,25 +15,33 @@ import java.io.IOException;
import org.hl7.fhir.dstu3.model.BooleanType; import org.hl7.fhir.dstu3.model.BooleanType;
import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeType; import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.UriType; import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator;
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.TestUtil;
public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3Test { public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderDstu3ValueSetTest.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderDstu3ValueSetTest.class);
private IIdType myExtensionalVsId; private IIdType myExtensionalVsId;
private IIdType myLocalValueSetId;
private ValueSet myLocalVs;
@AfterClass @AfterClass
public static void afterClassClearContext() { public static void afterClassClearContext() {
@ -78,6 +89,140 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
} }
@Test
public void testExpandLocalVsAgainstExternalCs() throws IOException {
createExternalCsAndLocalVs();
assertNotNull(myLocalValueSetId);
//@formatter:off
Parameters respParam = ourClient
.operation()
.onInstance(myLocalValueSetId)
.named("expand")
.withNoParameters(Parameters.class)
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, containsStringIgnoringCase("<code value=\"childAAA\"/>"));
assertThat(resp, containsStringIgnoringCase("<code value=\"childAAB\"/>"));
assertThat(resp, not(containsStringIgnoringCase("<code value=\"ParentA\"/>")));
}
@Test
public void testExpandLocalVsWithUnknownCode() throws IOException {
createExternalCsAndLocalVsWithUnknownCode();
assertNotNull(myLocalValueSetId);
//@formatter:off
try {
ourClient
.operation()
.onInstance(myLocalValueSetId)
.named("expand")
.withNoParameters(Parameters.class)
.execute();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: Invalid filter criteria - code does not exist: {http://example.com/my_code_system}childFOOOOOOO", e.getMessage());
}
//@formatter:on
}
@Test
public void testExpandInlineVsAgainstBuiltInCs() throws IOException {
createLocalVsPointingAtBuiltInCodeSystem();
assertNotNull(myLocalValueSetId);
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", myLocalVs)
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, containsStringIgnoringCase("<code value=\"M\"/>"));
}
@Test
public void testExpandLocalVsAgainstBuiltInCs() throws IOException {
createLocalVsPointingAtBuiltInCodeSystem();
assertNotNull(myLocalValueSetId);
//@formatter:off
Parameters respParam = ourClient
.operation()
.onInstance(myLocalValueSetId)
.named("expand")
.withNoParameters(Parameters.class)
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, containsStringIgnoringCase("<code value=\"M\"/>"));
}
@Test
public void testExpandLocalVsCanonicalAgainstExternalCs() throws IOException {
createExternalCsAndLocalVs();
assertNotNull(myLocalValueSetId);
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "identifier", new UriType(URL_MY_VALUE_SET))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, containsStringIgnoringCase("<code value=\"childAAA\"/>"));
assertThat(resp, containsStringIgnoringCase("<code value=\"childAAB\"/>"));
assertThat(resp, not(containsStringIgnoringCase("<code value=\"ParentA\"/>")));
}
@Test
public void testExpandInlineVsAgainstExternalCs() throws IOException {
createExternalCsAndLocalVs();
assertNotNull(myLocalVs);
myLocalVs.setId("");
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", myLocalVs)
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, containsStringIgnoringCase("<code value=\"childAAA\"/>"));
assertThat(resp, containsStringIgnoringCase("<code value=\"childAAB\"/>"));
assertThat(resp, not(containsStringIgnoringCase("<code value=\"ParentA\"/>")));
}
@Test @Test
public void testExpandByIdWithFilter() throws IOException { public void testExpandByIdWithFilter() throws IOException {
@ -231,4 +376,102 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).booleanValue()); assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).booleanValue());
} }
private CodeSystem createExternalCs() {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong());
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
cs.setResourceVersionId(table.getVersion());
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA);
TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA");
parentA.addChild(childAA, RelationshipTypeEnum.ISA);
TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA");
childAA.addChild(childAAA, RelationshipTypeEnum.ISA);
TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB");
childAA.addChild(childAAB, RelationshipTypeEnum.ISA);
TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB");
parentA.addChild(childAB, RelationshipTypeEnum.ISA);
TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B");
cs.getConcepts().add(parentB);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs);
return codeSystem;
}
private void createExternalCsAndLocalVs() {
CodeSystem codeSystem = createExternalCs();
createLocalVs(codeSystem);
}
private void createExternalCsAndLocalVsWithUnknownCode() {
CodeSystem codeSystem = createExternalCs();
createLocalVsWithUnknownCode(codeSystem);
}
private void createLocalCsAndVs() {
//@formatter:off
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.COMPLETE);
codeSystem
.addConcept().setCode("A").setDisplay("Code A")
.addConcept(new ConceptDefinitionComponent().setCode("AA").setDisplay("Code AA")
.addConcept(new ConceptDefinitionComponent().setCode("AAA").setDisplay("Code AAA"))
)
.addConcept(new ConceptDefinitionComponent().setCode("AB").setDisplay("Code AB"));
codeSystem
.addConcept().setCode("B").setDisplay("Code B")
.addConcept(new ConceptDefinitionComponent().setCode("BA").setDisplay("Code BA"))
.addConcept(new ConceptDefinitionComponent().setCode("BB").setDisplay("Code BB"));
//@formatter:on
myCodeSystemDao.create(codeSystem, mySrd);
createLocalVs(codeSystem);
}
private void createLocalVs(CodeSystem codeSystem) {
myLocalVs = new ValueSet();
myLocalVs.setUrl(URL_MY_VALUE_SET);
ConceptSetComponent include = myLocalVs.getCompose().addInclude();
include.setSystem(codeSystem.getUrl());
include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("childAA");
myLocalValueSetId = myValueSetDao.create(myLocalVs, mySrd).getId().toUnqualifiedVersionless();
}
private void createLocalVsWithUnknownCode(CodeSystem codeSystem) {
myLocalVs = new ValueSet();
myLocalVs.setUrl(URL_MY_VALUE_SET);
ConceptSetComponent include = myLocalVs.getCompose().addInclude();
include.setSystem(codeSystem.getUrl());
include.addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("childFOOOOOOO");
myLocalValueSetId = myValueSetDao.create(myLocalVs, mySrd).getId().toUnqualifiedVersionless();
}
private void createLocalVsPointingAtBuiltInCodeSystem() {
myLocalVs = new ValueSet();
myLocalVs.setUrl(URL_MY_VALUE_SET);
ConceptSetComponent include = myLocalVs.getCompose().addInclude();
include.setSystem("http://hl7.org/fhir/v3/MaritalStatus");
myLocalValueSetId = myValueSetDao.create(myLocalVs, mySrd).getId().toUnqualifiedVersionless();
}
} }

View File

@ -367,6 +367,19 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
} }
} }
@Test
public void testMarkResourcesForReindexing() throws Exception {
HttpGet get = new HttpGet(ourServerBase + "/$mark-all-resources-for-reindexing");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String output = IOUtils.toString(http.getEntity().getContent());
ourLog.info(output);
assertEquals(200, http.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(http);;
}
}
@Transactional(propagation = Propagation.NEVER) @Transactional(propagation = Propagation.NEVER)
@Test @Test
public void testSuggestKeywords() throws Exception { public void testSuggestKeywords() throws Exception {

View File

@ -30,10 +30,6 @@ public class TerminologyLoaderSvcIntegrationTest extends BaseJpaDstu3Test {
@Test @Test
@Ignore @Ignore
public void testLoadAndStoreSnomedCt() { public void testLoadAndStoreSnomedCt() {
Map<String, File> files = new HashMap<String, File>();
files.put(TerminologyLoaderSvc.SCT_FILE_CONCEPT, new File("/Users/james/tmp/sct/SnomedCT_Release_INT_20160131_Full/Terminology/sct2_Concept_Full_INT_20160131.txt"));
files.put(TerminologyLoaderSvc.SCT_FILE_DESCRIPTION, new File("/Users/james/tmp/sct/SnomedCT_Release_INT_20160131_Full/Terminology/sct2_Description_Full-en_INT_20160131.txt"));
files.put(TerminologyLoaderSvc.SCT_FILE_RELATIONSHIP, new File("/Users/james/tmp/sct/SnomedCT_Release_INT_20160131_Full/Terminology/sct2_Relationship_Full_INT_20160131.txt"));
// myLoader.processSnomedCtFiles(files, mySrd); // myLoader.processSnomedCtFiles(files, mySrd);
} }

View File

@ -1,17 +1,17 @@
package ca.uhn.fhir.jpa.term; package ca.uhn.fhir.jpa.term;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInRelativeOrder; import static org.hamcrest.Matchers.containsInRelativeOrder;
import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.Mockito.any; import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -25,6 +25,7 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
@ -79,6 +80,21 @@ public class TerminologyLoaderSvcTest {
mySvc.loadLoinc(list(bos1.toByteArray(), bos2.toByteArray()), details); mySvc.loadLoinc(list(bos1.toByteArray(), bos2.toByteArray()), details);
} }
/**
* This is just for trying stuff, it won't run without
* local files external to the git repo
*/
@Ignore
@Test
public void testLoadSnomedCtAgainstRealFile() throws Exception {
byte[] bytes = IOUtils.toByteArray(new FileInputStream("/Users/james/Downloads/SnomedCT_Release_INT_20160131_Full.zip"));
RequestDetails details = mock(RequestDetails.class);
mySvc.loadSnomedCt(list(bytes), details);
}
@Test @Test
public void testLoadSnomedCt() throws Exception { public void testLoadSnomedCt() throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream();

View File

@ -17,3 +17,4 @@ id effectiveTime active moduleId definitionStatusId
207527008 20020131 1 900000000000207008 900000000000074008 207527008 20020131 1 900000000000207008 900000000000074008
207527008 20040731 1 900000000000207008 900000000000073002 207527008 20040731 1 900000000000207008 900000000000073002
207527008 20090731 0 900000000000207008 900000000000074008 207527008 20090731 0 900000000000207008 900000000000074008
404684003 20040131 1 900000000000207008 900000000000074008

View File

@ -67,6 +67,8 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
ValueSetExpansionOutcome vso; ValueSetExpansionOutcome vso;
try { try {
vso = getExpander().expand(theSource); vso = getExpander().expand(theSource);
} catch (InvalidRequestException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
throw new InternalErrorException(e); throw new InternalErrorException(e);
} }
@ -125,7 +127,9 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
@Override @Override
public ValueSetExpander getExpander() { public ValueSetExpander getExpander() {
return new ValueSetExpanderSimple(this, this); ValueSetExpanderSimple retVal = new ValueSetExpanderSimple(this, this);
retVal.setMaxExpansionSize(Integer.MAX_VALUE);
return retVal;
} }
@Override @Override

View File

@ -70,8 +70,10 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
private Set<String> excludeKeys = new HashSet<String>(); private Set<String> excludeKeys = new HashSet<String>();
private ValueSetExpanderFactory factory; private ValueSetExpanderFactory factory;
private ValueSet focus; private ValueSet focus;
private int maxExpansionSize = 500;
private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>(); private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
private int total;
public ValueSetExpanderSimple(IWorkerContext context, ValueSetExpanderFactory factory) { public ValueSetExpanderSimple(IWorkerContext context, ValueSetExpanderFactory factory) {
super(); super();
@ -79,6 +81,10 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
this.factory = factory; this.factory = factory;
} }
public void setMaxExpansionSize(int theMaxExpansionSize) {
maxExpansionSize = theMaxExpansionSize;
}
private void addCode(String system, String code, String display) { private void addCode(String system, String code, String display) {
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent(); ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
n.setSystem(system); n.setSystem(system);
@ -101,7 +107,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
} }
private void addCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params) throws ETooCostly { private void addCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params) throws ETooCostly {
if (expand.getContains().size() > 500) if (expand.getContains().size() > maxExpansionSize)
throw new ETooCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")"); throw new ETooCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")");
for (ValueSetExpansionParameterComponent p : expand.getParameter()) { for (ValueSetExpansionParameterComponent p : expand.getParameter()) {
if (!existsInParams(params, p.getName(), p.getValue())) if (!existsInParams(params, p.getName(), p.getValue()))
@ -111,6 +117,8 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
for (ValueSetExpansionContainsComponent c : expand.getContains()) { for (ValueSetExpansionContainsComponent c : expand.getContains()) {
addCode(c.getSystem(), c.getCode(), c.getDisplay()); addCode(c.getSystem(), c.getCode(), c.getDisplay());
} }
total = expand.getTotal();
} }
private void excludeCode(String theSystem, String theCode) { private void excludeCode(String theSystem, String theCode) {
@ -171,6 +179,11 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
focus.getExpansion().getContains().add(c); focus.getExpansion().getContains().add(c);
} }
} }
if (total > 0) {
focus.getExpansion().setTotal(total);
}
return new ValueSetExpansionOutcome(focus, null); return new ValueSetExpansionOutcome(focus, null);
} catch (RuntimeException e) { } catch (RuntimeException e) {
// TODO: we should put something more specific instead of just Exception below, since // TODO: we should put something more specific instead of just Exception below, since