Merge branch 'master' into improve_jpa_indexes
This commit is contained in:
commit
5b6cde8a9f
|
@ -23,7 +23,6 @@ package ca.uhn.fhir.rest.api;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
@ -170,7 +169,11 @@ public enum EncodingEnum {
|
|||
* @see #forContentType(String)
|
||||
*/
|
||||
public static EncodingEnum forContentTypeStrict(String theContentType) {
|
||||
return ourContentTypeToEncodingStrict.get(theContentType);
|
||||
if (theContentType == null) {
|
||||
return null;
|
||||
}
|
||||
String[] contentTypeSplitted = theContentType.split(";");
|
||||
return ourContentTypeToEncodingStrict.get(contentTypeSplitted[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1175,7 +1175,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
|||
public SearchBuilder newSearchBuilder() {
|
||||
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myFulltextSearchSvc, this, myResourceIndexedSearchParamUriDao,
|
||||
myForcedIdDao,
|
||||
myTerminologySvc, mySerarchParamRegistry);
|
||||
myTerminologySvc, mySerarchParamRegistry, myResourceHistoryTableDao, myResourceTagDao);
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
@ -1356,7 +1356,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <R extends IBaseResource> R populateResourceMetadataHapi(Class<R> theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation, IResource res) {
|
||||
private <R extends IBaseResource> R populateResourceMetadataHapi(Class<R> theResourceType, BaseHasResource theEntity, Collection<? extends BaseTag> theTagList, boolean theForHistoryOperation, IResource res) {
|
||||
R retVal = (R) res;
|
||||
if (theEntity.getDeleted() != null) {
|
||||
res = (IResource) myContext.getResourceDefinition(theResourceType).newInstance();
|
||||
|
@ -1385,7 +1385,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
|||
ResourceMetadataKeyEnum.UPDATED.put(res, theEntity.getUpdated());
|
||||
IDao.RESOURCE_PID.put(res, theEntity.getId());
|
||||
|
||||
Collection<? extends BaseTag> tags = theEntity.getTags();
|
||||
Collection<? extends BaseTag> tags = theTagList;
|
||||
if (theEntity.isHasTags()) {
|
||||
TagList tagList = new TagList();
|
||||
List<IBaseCoding> securityLabels = new ArrayList<>();
|
||||
|
@ -1422,7 +1422,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <R extends IBaseResource> R populateResourceMetadataRi(Class<R> theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation, IAnyResource res) {
|
||||
private <R extends IBaseResource> R populateResourceMetadataRi(Class<R> theResourceType, BaseHasResource theEntity, Collection<? extends BaseTag> theTagList, boolean theForHistoryOperation, IAnyResource res) {
|
||||
R retVal = (R) res;
|
||||
if (theEntity.getDeleted() != null) {
|
||||
res = (IAnyResource) myContext.getResourceDefinition(theResourceType).newInstance();
|
||||
|
@ -1455,7 +1455,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
|||
res.getMeta().setLastUpdated(theEntity.getUpdatedDate());
|
||||
IDao.RESOURCE_PID.put(res, theEntity.getId());
|
||||
|
||||
Collection<? extends BaseTag> tags = theEntity.getTags();
|
||||
Collection<? extends BaseTag> tags = theTagList;
|
||||
|
||||
if (theEntity.isHasTags()) {
|
||||
for (BaseTag next : tags) {
|
||||
|
@ -1602,19 +1602,25 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
|||
public IBaseResource toResource(BaseHasResource theEntity, boolean theForHistoryOperation) {
|
||||
RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType());
|
||||
Class<? extends IBaseResource> resourceType = type.getImplementingClass();
|
||||
return toResource(resourceType, theEntity, theForHistoryOperation);
|
||||
return toResource(resourceType, theEntity, null, null, theForHistoryOperation);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity,
|
||||
public <R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity, ResourceHistoryTable theHistory, Collection<ResourceTag> theTagList,
|
||||
boolean theForHistoryOperation) {
|
||||
|
||||
// May 28, 2018 - #936
|
||||
// Could set historyList to null, if it's not called in the loop for the backward compatibility
|
||||
ResourceHistoryTable history;
|
||||
if (theEntity instanceof ResourceHistoryTable) {
|
||||
history = (ResourceHistoryTable) theEntity;
|
||||
} else {
|
||||
history = myResourceHistoryTableDao.findForIdAndVersion(theEntity.getId(), theEntity.getVersion());
|
||||
if (theHistory == null) {
|
||||
history = myResourceHistoryTableDao.findForIdAndVersion(theEntity.getId(), theEntity.getVersion());
|
||||
} else {
|
||||
history = theHistory;
|
||||
}
|
||||
}
|
||||
|
||||
if (history == null) {
|
||||
|
@ -1640,12 +1646,21 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
|||
break;
|
||||
}
|
||||
|
||||
// get preload the tagList
|
||||
Collection<? extends BaseTag> myTagList;
|
||||
|
||||
if (theTagList == null)
|
||||
myTagList = theEntity.getTags();
|
||||
else
|
||||
myTagList = theTagList;
|
||||
|
||||
|
||||
/*
|
||||
* Use the appropriate custom type if one is specified in the context
|
||||
*/
|
||||
Class<R> resourceType = theResourceType;
|
||||
if (myContext.hasDefaultTypeForProfile()) {
|
||||
for (BaseTag nextTag : theEntity.getTags()) {
|
||||
for (BaseTag nextTag : myTagList) {
|
||||
if (nextTag.getTag().getTagType() == TagTypeEnum.PROFILE) {
|
||||
String profile = nextTag.getTag().getCode();
|
||||
if (isNotBlank(profile)) {
|
||||
|
@ -1692,10 +1707,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
|||
|
||||
if (retVal instanceof IResource) {
|
||||
IResource res = (IResource) retVal;
|
||||
retVal = populateResourceMetadataHapi(resourceType, theEntity, theForHistoryOperation, res);
|
||||
retVal = populateResourceMetadataHapi(resourceType, theEntity, myTagList, theForHistoryOperation, res);
|
||||
} else {
|
||||
IAnyResource res = (IAnyResource) retVal;
|
||||
retVal = populateResourceMetadataRi(resourceType, theEntity, theForHistoryOperation, res);
|
||||
retVal = populateResourceMetadataRi(resourceType, theEntity, myTagList, theForHistoryOperation, res);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1731,7 +1746,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
|||
return theResourceType + '/' + theId.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ResourceTable updateEntity(RequestDetails theRequest, final IBaseResource theResource, ResourceTable
|
||||
theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
|
||||
|
|
|
@ -207,7 +207,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
StopWatch w = new StopWatch();
|
||||
|
||||
T resourceToDelete = toResource(myResourceType, entity, false);
|
||||
T resourceToDelete = toResource(myResourceType, entity, null, null, false);
|
||||
|
||||
// Notify IServerOperationInterceptors about pre-action call
|
||||
if (theReques != null) {
|
||||
|
@ -289,7 +289,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
ResourceTable entity = myEntityManager.find(ResourceTable.class, pid);
|
||||
deletedResources.add(entity);
|
||||
|
||||
T resourceToDelete = toResource(myResourceType, entity, false);
|
||||
T resourceToDelete = toResource(myResourceType, entity, null, null, false);
|
||||
|
||||
// Notify IServerOperationInterceptors about pre-action call
|
||||
if (theRequest != null) {
|
||||
|
@ -854,7 +854,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
BaseHasResource entity = readEntity(theId);
|
||||
validateResourceType(entity);
|
||||
|
||||
T retVal = toResource(myResourceType, entity, false);
|
||||
T retVal = toResource(myResourceType, entity, null, null, false);
|
||||
|
||||
IPrimitiveType<Date> deleted;
|
||||
if (retVal instanceof IResource) {
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.entity.BaseHasResource;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTag;
|
||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -56,6 +59,6 @@ public interface IDao {
|
|||
|
||||
IBaseResource toResource(BaseHasResource theEntity, boolean theForHistoryOperation);
|
||||
|
||||
<R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation);
|
||||
<R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity, ResourceHistoryTable theHistory, Collection<ResourceTag> theTagList, boolean theForHistoryOperation);
|
||||
|
||||
}
|
||||
|
|
|
@ -110,12 +110,17 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
private IHapiTerminologySvc myTerminologySvc;
|
||||
private int myFetchSize;
|
||||
|
||||
protected IResourceHistoryTableDao myResourceHistoryTableDao;
|
||||
protected IResourceTagDao myResourceTagDao;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager, IFulltextSearchSvc theFulltextSearchSvc,
|
||||
BaseHapiFhirDao<?> theDao,
|
||||
IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao, IForcedIdDao theForcedIdDao, IHapiTerminologySvc theTerminologySvc, ISearchParamRegistry theSearchParamRegistry) {
|
||||
public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager,
|
||||
IFulltextSearchSvc theFulltextSearchSvc, BaseHapiFhirDao<?> theDao,
|
||||
IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao, IForcedIdDao theForcedIdDao,
|
||||
IHapiTerminologySvc theTerminologySvc, ISearchParamRegistry theSearchParamRegistry,
|
||||
IResourceHistoryTableDao theResourceHistoryTableDao, IResourceTagDao theResourceTagDao) {
|
||||
myContext = theFhirContext;
|
||||
myEntityManager = theEntityManager;
|
||||
myFulltextSearchSvc = theFulltextSearchSvc;
|
||||
|
@ -124,6 +129,8 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
myForcedIdDao = theForcedIdDao;
|
||||
myTerminologySvc = theTerminologySvc;
|
||||
mySearchParamRegistry = theSearchParamRegistry;
|
||||
myResourceHistoryTableDao = theResourceHistoryTableDao;
|
||||
myResourceTagDao = theResourceTagDao;
|
||||
}
|
||||
|
||||
private void addPredicateComposite(String theResourceName, RuntimeSearchParam theParamDef, List<? extends IQueryParameterType> theNextAnd) {
|
||||
|
@ -1597,9 +1604,15 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
List<ResourceTable> resultList = q.getResultList();
|
||||
|
||||
//-- Issue #963: Load resource histories based on pids once to improve the performance
|
||||
Map<Long, ResourceHistoryTable> historyMap = getResourceHistoryMap(pids);
|
||||
|
||||
//-- preload all tags with tag definition if any
|
||||
Map<Long, Collection<ResourceTag>> tagMap = getResourceTagMap(resultList);
|
||||
|
||||
for (ResourceTable next : resultList) {
|
||||
Class<? extends IBaseResource> resourceType = context.getResourceDefinition(next.getResourceType()).getImplementingClass();
|
||||
IBaseResource resource = theDao.toResource(resourceType, next, theForHistoryOperation);
|
||||
IBaseResource resource = theDao.toResource(resourceType, next, historyMap.get(next.getId()), tagMap.get(next.getId()), theForHistoryOperation);
|
||||
if (resource == null) {
|
||||
ourLog.warn("Unable to find resource {}/{}/_history/{} in database", next.getResourceType(), next.getIdDt().getIdPart(), next.getVersion());
|
||||
continue;
|
||||
|
@ -1628,6 +1641,62 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
//-- load all history in to the map
|
||||
private Map<Long, ResourceHistoryTable> getResourceHistoryMap(Collection<Long> pids) {
|
||||
|
||||
Map<Long, ResourceHistoryTable> historyMap = new HashMap<Long, ResourceHistoryTable>();
|
||||
|
||||
if (pids.size() == 0)
|
||||
return historyMap;
|
||||
|
||||
Collection<ResourceHistoryTable> historyList = myResourceHistoryTableDao.findByResourceIds(pids);
|
||||
|
||||
for (ResourceHistoryTable history : historyList) {
|
||||
|
||||
historyMap.put(history.getResourceId(), history);
|
||||
}
|
||||
|
||||
return historyMap;
|
||||
}
|
||||
|
||||
private Map<Long, Collection<ResourceTag>> getResourceTagMap(List<ResourceTable> resourceList) {
|
||||
|
||||
List<Long> idList = new ArrayList<Long>(resourceList.size());
|
||||
|
||||
//-- find all resource has tags
|
||||
for (ResourceTable resource: resourceList) {
|
||||
if (resource.isHasTags())
|
||||
idList.add(resource.getId());
|
||||
}
|
||||
|
||||
Map<Long, Collection<ResourceTag>> tagMap = new HashMap<Long, Collection<ResourceTag>>();
|
||||
|
||||
//-- no tags
|
||||
if (idList.size() == 0)
|
||||
return tagMap;
|
||||
|
||||
//-- get all tags for the idList
|
||||
Collection<ResourceTag> tagList = myResourceTagDao.findByResourceIds(idList);
|
||||
|
||||
//-- build the map, key = resourceId, value = list of ResourceTag
|
||||
Long resourceId;
|
||||
Collection<ResourceTag> tagCol;
|
||||
for (ResourceTag tag : tagList) {
|
||||
|
||||
resourceId = tag.getResourceId();
|
||||
tagCol = tagMap.get(resourceId);
|
||||
if (tagCol == null) {
|
||||
tagCol = new ArrayList<ResourceTag>();
|
||||
tagCol.add(tag);
|
||||
tagMap.put(resourceId, tagCol);
|
||||
} else {
|
||||
tagCol.add(tag);
|
||||
}
|
||||
}
|
||||
|
||||
return tagMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation,
|
||||
EntityManager entityManager, FhirContext context, IDao theDao) {
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package ca.uhn.fhir.jpa.dao.data;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
@ -8,8 +12,7 @@ import org.springframework.data.jpa.repository.Query;
|
|||
import org.springframework.data.jpa.repository.Temporal;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import javax.persistence.TemporalType;
|
||||
import java.util.Date;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -82,4 +85,10 @@ public interface IResourceHistoryTableDao extends JpaRepository<ResourceHistoryT
|
|||
"LEFT OUTER JOIN ResourceTable t ON (v.myResourceId = t.myId) " +
|
||||
"WHERE v.myResourceVersion != t.myVersion")
|
||||
Slice<Long> findIdsOfPreviousVersionsOfResources(Pageable thePage);
|
||||
|
||||
@Query("" +
|
||||
"SELECT h FROM ResourceHistoryTable h " +
|
||||
"INNER JOIN ResourceTable r ON (r.myId = h.myResourceId and r.myVersion = h.myResourceVersion) " +
|
||||
"WHERE r.myId in (:pids)")
|
||||
Collection<ResourceHistoryTable> findByResourceIds(@Param("pids") Collection<Long> pids);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.dao.data;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
|
@ -21,9 +23,15 @@ package ca.uhn.fhir.jpa.dao.data;
|
|||
*/
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTag;
|
||||
|
||||
public interface IResourceTagDao extends JpaRepository<ResourceTag, Long> {
|
||||
// nothing
|
||||
@Query("" +
|
||||
"SELECT t FROM ResourceTag t " +
|
||||
"INNER JOIN TagDefinition td ON (td.myId = t.myTagId) " +
|
||||
"WHERE t.myResourceId in (:pids)")
|
||||
Collection<ResourceTag> findByResourceIds(@Param("pids") Collection<Long> pids);
|
||||
}
|
||||
|
|
|
@ -36,19 +36,19 @@ import ca.uhn.fhir.jpa.entity.Search;
|
|||
public interface ISearchDao extends JpaRepository<Search, Long> {
|
||||
|
||||
@Query("SELECT s FROM Search s WHERE s.myUuid = :uuid")
|
||||
public Search findByUuid(@Param("uuid") String theUuid);
|
||||
Search findByUuid(@Param("uuid") String theUuid);
|
||||
|
||||
@Query("SELECT s.myId FROM Search s WHERE s.mySearchLastReturned < :cutoff")
|
||||
public Slice<Long> findWhereLastReturnedBefore(@Param("cutoff") Date theCutoff, Pageable thePage);
|
||||
Slice<Long> findWhereLastReturnedBefore(@Param("cutoff") Date theCutoff, Pageable thePage);
|
||||
|
||||
// @Query("SELECT s FROM Search s WHERE s.myCreated < :cutoff")
|
||||
// public Collection<Search> findWhereCreatedBefore(@Param("cutoff") Date theCutoff);
|
||||
|
||||
@Query("SELECT s FROM Search s WHERE s.myResourceType = :type AND mySearchQueryStringHash = :hash AND s.myCreated > :cutoff")
|
||||
public Collection<Search> find(@Param("type") String theResourceType, @Param("hash") int theHashCode, @Param("cutoff") Date theCreatedCutoff);
|
||||
Collection<Search> find(@Param("type") String theResourceType, @Param("hash") int theHashCode, @Param("cutoff") Date theCreatedCutoff);
|
||||
|
||||
@Modifying
|
||||
@Query("UPDATE Search s SET s.mySearchLastReturned = :last WHERE s.myId = :pid")
|
||||
public void updateSearchLastReturned(@Param("pid") long thePid, @Param("last") Date theDate);
|
||||
void updateSearchLastReturned(@Param("pid") long thePid, @Param("last") Date theDate);
|
||||
|
||||
}
|
||||
|
|
|
@ -38,8 +38,8 @@ public interface ISearchResultDao extends JpaRepository<SearchResult, Long> {
|
|||
@Query(value="SELECT r FROM SearchResult r WHERE r.mySearch = :search")
|
||||
Collection<SearchResult> findWithSearchUuid(@Param("search") Search theSearch);
|
||||
|
||||
@Query(value="SELECT r FROM SearchResult r WHERE r.mySearch = :search ORDER BY r.myOrder ASC")
|
||||
Page<SearchResult> findWithSearchUuid(@Param("search") Search theSearch, Pageable thePage);
|
||||
@Query(value="SELECT r.myResourcePid FROM SearchResult r WHERE r.mySearch = :search ORDER BY r.myOrder ASC")
|
||||
Page<Long> findWithSearchUuid(@Param("search") Search theSearch, Pageable thePage);
|
||||
|
||||
@Modifying
|
||||
@Query(value="DELETE FROM SearchResult r WHERE r.mySearchPid = :search")
|
||||
|
|
|
@ -94,10 +94,9 @@ public class Search implements Serializable {
|
|||
@OneToMany(mappedBy="mySearch")
|
||||
private Collection<SearchResult> myResults;
|
||||
|
||||
// TODO: change nullable to false after 2.5
|
||||
@NotNull
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name="SEARCH_LAST_RETURNED", nullable=true, updatable=false)
|
||||
@Column(name="SEARCH_LAST_RETURNED", nullable=false, updatable=false)
|
||||
private Date mySearchLastReturned;
|
||||
|
||||
@Lob()
|
||||
|
|
|
@ -78,7 +78,7 @@ public class TermConcept implements Serializable {
|
|||
private String myDisplay;
|
||||
|
||||
@OneToMany(mappedBy = "myConcept", orphanRemoval = true)
|
||||
@Field
|
||||
@Field(name = "PROPmyProperties", analyzer = @Analyzer(definition = "termConceptPropertyAnalyzer"))
|
||||
@FieldBridge(impl = TermConceptPropertyFieldBridge.class)
|
||||
private Collection<TermConceptProperty> myProperties;
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.entity;
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -21,18 +21,22 @@ package ca.uhn.fhir.jpa.entity;
|
|||
*/
|
||||
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.hibernate.search.bridge.FieldBridge;
|
||||
import org.hibernate.search.bridge.LuceneOptions;
|
||||
import org.hibernate.search.bridge.StringBridge;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/**
|
||||
* Allows hibernate search to index individual concepts' properties
|
||||
*/
|
||||
public class TermConceptPropertyFieldBridge implements FieldBridge, StringBridge {
|
||||
|
||||
public static final String PROP_PREFIX = "PROP__";
|
||||
public static final String CONCEPT_FIELD_PROPERTY_PREFIX = "PROP";
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -48,15 +52,17 @@ public class TermConceptPropertyFieldBridge implements FieldBridge, StringBridge
|
|||
|
||||
@Override
|
||||
public void set(String theName, Object theValue, Document theDocument, LuceneOptions theLuceneOptions) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<TermConceptProperty> properties = (Collection<TermConceptProperty>) theValue;
|
||||
|
||||
if (properties != null) {
|
||||
for (TermConceptProperty next : properties) {
|
||||
String propValue = next.getKey() + "=" + next.getValue();
|
||||
theLuceneOptions.addFieldToDocument(theName, propValue, theDocument);
|
||||
theDocument.add(new StringField(CONCEPT_FIELD_PROPERTY_PREFIX + next.getKey(), next.getValue(), Field.Store.YES));
|
||||
|
||||
if (next.getType() == TermConceptPropertyTypeEnum.CODING) {
|
||||
propValue = next.getKey() + "=" + next.getDisplay();
|
||||
theLuceneOptions.addFieldToDocument(theName, propValue, theDocument);
|
||||
if (isNotBlank(next.getDisplay())) {
|
||||
theDocument.add(new StringField(CONCEPT_FIELD_PROPERTY_PREFIX + next.getKey(), next.getDisplay(), Field.Store.YES));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,8 @@ public class LuceneSearchMappingFactory {
|
|||
.analyzerDef("standardAnalyzer", StandardTokenizerFactory.class)
|
||||
.filter(LowerCaseFilterFactory.class)
|
||||
.analyzerDef("exactAnalyzer", StandardTokenizerFactory.class)
|
||||
.analyzerDef("conceptParentPidsAnalyzer", WhitespaceTokenizerFactory.class);
|
||||
.analyzerDef("conceptParentPidsAnalyzer", WhitespaceTokenizerFactory.class)
|
||||
.analyzerDef("termConceptPropertyAnalyzer", WhitespaceTokenizerFactory.class);
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
|
|
@ -178,9 +178,9 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
@Override
|
||||
public List<Long> doInTransaction(TransactionStatus theStatus) {
|
||||
final List<Long> resultPids = new ArrayList<Long>();
|
||||
Page<SearchResult> searchResults = mySearchResultDao.findWithSearchUuid(foundSearch, page);
|
||||
for (SearchResult next : searchResults) {
|
||||
resultPids.add(next.getResourcePid());
|
||||
Page<Long> searchResultPids = mySearchResultDao.findWithSearchUuid(foundSearch, page);
|
||||
for (Long next : searchResultPids) {
|
||||
resultPids.add(next);
|
||||
}
|
||||
return resultPids;
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term;
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -40,9 +40,13 @@ import com.github.benmanes.caffeine.cache.Caffeine;
|
|||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.queries.TermsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.RegexpQuery;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.ScrollableResults;
|
||||
import org.hibernate.search.jpa.FullTextEntityManager;
|
||||
|
@ -131,10 +135,14 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
|||
private int myFetchSize = DEFAULT_FETCH_SIZE;
|
||||
private ApplicationContext myApplicationContext;
|
||||
|
||||
private void addCodeIfNotAlreadyAdded(String theCodeSystem, ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, TermConcept theConcept) {
|
||||
if (theAddedCodes.add(theConcept.getCode())) {
|
||||
/**
|
||||
* @param theAdd If true, add the code. If false, remove the code.
|
||||
*/
|
||||
private void addCodeIfNotAlreadyAdded(String theCodeSystem, ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, TermConcept theConcept, boolean theAdd) {
|
||||
String code = theConcept.getCode();
|
||||
if (theAdd && theAddedCodes.add(code)) {
|
||||
ValueSet.ValueSetExpansionContainsComponent contains = theExpansionComponent.addContains();
|
||||
contains.setCode(theConcept.getCode());
|
||||
contains.setCode(code);
|
||||
contains.setSystem(theCodeSystem);
|
||||
contains.setDisplay(theConcept.getDisplay());
|
||||
for (TermConceptDesignation nextDesignation : theConcept.getDesignations()) {
|
||||
|
@ -147,18 +155,32 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
|||
.setDisplay(nextDesignation.getUseDisplay());
|
||||
}
|
||||
}
|
||||
|
||||
if (!theAdd && theAddedCodes.remove(code)) {
|
||||
removeCodeFromExpansion(theCodeSystem, code, theExpansionComponent);
|
||||
}
|
||||
}
|
||||
|
||||
private void addConceptsToList(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, String theSystem, List<CodeSystem.ConceptDefinitionComponent> theConcept) {
|
||||
private void removeCodeFromExpansion(String theCodeSystem, String theCode, ValueSet.ValueSetExpansionComponent theExpansionComponent) {
|
||||
theExpansionComponent
|
||||
.getContains()
|
||||
.removeIf(t ->
|
||||
theCodeSystem.equals(t.getSystem()) &&
|
||||
theCode.equals(t.getCode()));
|
||||
}
|
||||
|
||||
private void addConceptsToList(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, String theSystem, List<CodeSystem.ConceptDefinitionComponent> theConcept, boolean theAdd) {
|
||||
for (CodeSystem.ConceptDefinitionComponent next : theConcept) {
|
||||
if (!theAddedCodes.contains(next.getCode())) {
|
||||
theAddedCodes.add(next.getCode());
|
||||
if (theAdd && theAddedCodes.add(next.getCode())) {
|
||||
ValueSet.ValueSetExpansionContainsComponent contains = theExpansionComponent.addContains();
|
||||
contains.setCode(next.getCode());
|
||||
contains.setSystem(theSystem);
|
||||
contains.setDisplay(next.getDisplay());
|
||||
}
|
||||
addConceptsToList(theExpansionComponent, theAddedCodes, theSystem, next.getConcept());
|
||||
if (!theAdd && theAddedCodes.remove(next.getCode())) {
|
||||
removeCodeFromExpansion(theSystem, next.getCode(), theExpansionComponent);
|
||||
}
|
||||
addConceptsToList(theExpansionComponent, theAddedCodes, theSystem, next.getConcept(), theAdd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -296,133 +318,17 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
|||
public ValueSet expandValueSet(ValueSet theValueSetToExpand) {
|
||||
ValueSet.ValueSetExpansionComponent expansionComponent = new ValueSet.ValueSetExpansionComponent();
|
||||
Set<String> addedCodes = new HashSet<>();
|
||||
boolean haveIncludeCriteria = false;
|
||||
|
||||
// Handle includes
|
||||
for (ValueSet.ConceptSetComponent include : theValueSetToExpand.getCompose().getInclude()) {
|
||||
String system = include.getSystem();
|
||||
if (isNotBlank(system)) {
|
||||
ourLog.info("Starting expansion around code system: {}", system);
|
||||
boolean add = true;
|
||||
expandValueSetHandleIncludeOrExclude(expansionComponent, addedCodes, include, add);
|
||||
}
|
||||
|
||||
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system);
|
||||
if (cs != null) {
|
||||
TermCodeSystemVersion csv = cs.getCurrentVersion();
|
||||
|
||||
/*
|
||||
* Include Concepts
|
||||
*/
|
||||
for (ValueSet.ConceptReferenceComponent next : include.getConcept()) {
|
||||
String nextCode = next.getCode();
|
||||
if (isNotBlank(nextCode) && !addedCodes.contains(nextCode)) {
|
||||
haveIncludeCriteria = true;
|
||||
TermConcept code = findCode(system, nextCode);
|
||||
if (code != null) {
|
||||
addCodeIfNotAlreadyAdded(system, expansionComponent, addedCodes, code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Filters
|
||||
*/
|
||||
|
||||
if (include.getFilter().size() > 0) {
|
||||
haveIncludeCriteria = true;
|
||||
|
||||
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
|
||||
QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get();
|
||||
BooleanJunction<?> bool = qb.bool();
|
||||
|
||||
bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery());
|
||||
|
||||
for (ValueSet.ConceptSetFilterComponent nextFilter : include.getFilter()) {
|
||||
if (isBlank(nextFilter.getValue()) && nextFilter.getOp() == null && isBlank(nextFilter.getProperty())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isBlank(nextFilter.getValue()) || nextFilter.getOp() == null || isBlank(nextFilter.getProperty())) {
|
||||
throw new InvalidRequestException("Invalid filter, must have fields populated: property op value");
|
||||
}
|
||||
|
||||
|
||||
if (nextFilter.getProperty().equals("display:exact") && nextFilter.getOp() == ValueSet.FilterOperator.EQUAL) {
|
||||
addDisplayFilterExact(qb, bool, nextFilter);
|
||||
} else if ("display".equals(nextFilter.getProperty()) && nextFilter.getOp() == ValueSet.FilterOperator.EQUAL) {
|
||||
if (nextFilter.getValue().trim().contains(" ")) {
|
||||
addDisplayFilterExact(qb, bool, nextFilter);
|
||||
} else {
|
||||
addDisplayFilterInexact(qb, bool, nextFilter);
|
||||
}
|
||||
} else if ((nextFilter.getProperty().equals("concept") || nextFilter.getProperty().equals("code")) && nextFilter.getOp() == ValueSet.FilterOperator.ISA) {
|
||||
|
||||
TermConcept code = 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());
|
||||
|
||||
} else {
|
||||
|
||||
bool.must(qb.phrase().onField("myProperties").sentence(nextFilter.getProperty() + "=" + nextFilter.getValue()).createQuery());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Query luceneQuery = bool.createQuery();
|
||||
FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class);
|
||||
jpaQuery.setMaxResults(1000);
|
||||
|
||||
StopWatch sw = new StopWatch();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<TermConcept> result = jpaQuery.getResultList();
|
||||
|
||||
ourLog.info("Expansion completed in {}ms", sw.getMillis());
|
||||
|
||||
for (TermConcept nextConcept : result) {
|
||||
addCodeIfNotAlreadyAdded(system, expansionComponent, addedCodes, nextConcept);
|
||||
}
|
||||
|
||||
expansionComponent.setTotal(jpaQuery.getResultSize());
|
||||
}
|
||||
|
||||
if (!haveIncludeCriteria) {
|
||||
List<TermConcept> allCodes = findCodes(system);
|
||||
for (TermConcept nextConcept : allCodes) {
|
||||
addCodeIfNotAlreadyAdded(system, expansionComponent, addedCodes, nextConcept);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// No codesystem matching the URL found in the database
|
||||
|
||||
CodeSystem codeSystemFromContext = getCodeSystemFromContext(system);
|
||||
if (codeSystemFromContext == null) {
|
||||
throw new InvalidRequestException("Unknown code system: " + system);
|
||||
}
|
||||
|
||||
if (include.getConcept().isEmpty() == false) {
|
||||
for (ValueSet.ConceptReferenceComponent next : include.getConcept()) {
|
||||
String nextCode = next.getCode();
|
||||
if (isNotBlank(nextCode) && !addedCodes.contains(nextCode)) {
|
||||
CodeSystem.ConceptDefinitionComponent code = findCode(codeSystemFromContext.getConcept(), nextCode);
|
||||
if (code != null) {
|
||||
addedCodes.add(nextCode);
|
||||
ValueSet.ValueSetExpansionContainsComponent contains = expansionComponent.addContains();
|
||||
contains.setCode(nextCode);
|
||||
contains.setSystem(system);
|
||||
contains.setDisplay(code.getDisplay());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List<CodeSystem.ConceptDefinitionComponent> concept = codeSystemFromContext.getConcept();
|
||||
addConceptsToList(expansionComponent, addedCodes, system, concept);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// Handle excludes
|
||||
for (ValueSet.ConceptSetComponent include : theValueSetToExpand.getCompose().getExclude()) {
|
||||
boolean add = false;
|
||||
expandValueSetHandleIncludeOrExclude(expansionComponent, addedCodes, include, add);
|
||||
}
|
||||
|
||||
ValueSet valueSet = new ValueSet();
|
||||
|
@ -430,6 +336,162 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
|||
return valueSet;
|
||||
}
|
||||
|
||||
public void expandValueSetHandleIncludeOrExclude(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, ValueSet.ConceptSetComponent include, boolean theAdd) {
|
||||
String system = include.getSystem();
|
||||
if (isNotBlank(system)) {
|
||||
ourLog.info("Starting expansion around code system: {}", system);
|
||||
|
||||
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system);
|
||||
if (cs != null) {
|
||||
TermCodeSystemVersion csv = cs.getCurrentVersion();
|
||||
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
|
||||
QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get();
|
||||
BooleanJunction<?> bool = qb.bool();
|
||||
|
||||
bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery());
|
||||
|
||||
/*
|
||||
* Include Concepts
|
||||
*/
|
||||
|
||||
String codes = include
|
||||
.getConcept()
|
||||
.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(ValueSet.ConceptReferenceComponent::getCode)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.collect(Collectors.joining(" "));
|
||||
if (isNotBlank(codes)) {
|
||||
bool.must(qb.keyword().onField("myCode").matching(codes).createQuery());
|
||||
}
|
||||
|
||||
/*
|
||||
* Filters
|
||||
*/
|
||||
|
||||
if (include.getFilter().size() > 0) {
|
||||
|
||||
for (ValueSet.ConceptSetFilterComponent nextFilter : include.getFilter()) {
|
||||
if (isBlank(nextFilter.getValue()) && nextFilter.getOp() == null && isBlank(nextFilter.getProperty())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isBlank(nextFilter.getValue()) || nextFilter.getOp() == null || isBlank(nextFilter.getProperty())) {
|
||||
throw new InvalidRequestException("Invalid filter, must have fields populated: property op value");
|
||||
}
|
||||
|
||||
|
||||
if (nextFilter.getProperty().equals("display:exact") && nextFilter.getOp() == ValueSet.FilterOperator.EQUAL) {
|
||||
addDisplayFilterExact(qb, bool, nextFilter);
|
||||
} else if ("display".equals(nextFilter.getProperty()) && nextFilter.getOp() == ValueSet.FilterOperator.EQUAL) {
|
||||
if (nextFilter.getValue().trim().contains(" ")) {
|
||||
addDisplayFilterExact(qb, bool, nextFilter);
|
||||
} else {
|
||||
addDisplayFilterInexact(qb, bool, nextFilter);
|
||||
}
|
||||
} else if (nextFilter.getProperty().equals("concept") || nextFilter.getProperty().equals("code")) {
|
||||
|
||||
TermConcept code = findCode(system, nextFilter.getValue());
|
||||
if (code == null) {
|
||||
throw new InvalidRequestException("Invalid filter criteria - code does not exist: {" + system + "}" + nextFilter.getValue());
|
||||
}
|
||||
|
||||
if (nextFilter.getOp() == ValueSet.FilterOperator.ISA) {
|
||||
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());
|
||||
} else {
|
||||
throw new InvalidRequestException("Don't know how to handle op=" + nextFilter.getOp() + " on property " + nextFilter.getProperty());
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (nextFilter.getOp() == ValueSet.FilterOperator.REGEX) {
|
||||
|
||||
/*
|
||||
* We treat the regex filter as a match on the regex
|
||||
* anywhere in the property string. The spec does not
|
||||
* say whether or not this is the right behaviour, but
|
||||
* there are examples that seem to suggest that it is.
|
||||
*/
|
||||
String value = nextFilter.getValue();
|
||||
if (value.endsWith("$")) {
|
||||
value = value.substring(0, value.length() - 1);
|
||||
} else if (value.endsWith(".*") == false) {
|
||||
value = value + ".*";
|
||||
}
|
||||
if (value.startsWith("^") == false && value.startsWith(".*") == false) {
|
||||
value = ".*" + value;
|
||||
} else if (value.startsWith("^")) {
|
||||
value = value.substring(1);
|
||||
}
|
||||
|
||||
Term term = new Term(TermConceptPropertyFieldBridge.CONCEPT_FIELD_PROPERTY_PREFIX + nextFilter.getProperty(), value);
|
||||
RegexpQuery query = new RegexpQuery(term);
|
||||
bool.must(query);
|
||||
|
||||
} else {
|
||||
|
||||
String value = nextFilter.getValue();
|
||||
Term term = new Term(TermConceptPropertyFieldBridge.CONCEPT_FIELD_PROPERTY_PREFIX + nextFilter.getProperty(), value);
|
||||
bool.must(new TermsQuery(term));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Query luceneQuery = bool.createQuery();
|
||||
FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class);
|
||||
jpaQuery.setMaxResults(1000);
|
||||
|
||||
StopWatch sw = new StopWatch();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<TermConcept> result = jpaQuery.getResultList();
|
||||
|
||||
ourLog.info("Expansion completed in {}ms", sw.getMillis());
|
||||
|
||||
for (TermConcept nextConcept : result) {
|
||||
addCodeIfNotAlreadyAdded(system, theExpansionComponent, theAddedCodes, nextConcept, theAdd);
|
||||
}
|
||||
|
||||
} else {
|
||||
// No codesystem matching the URL found in the database
|
||||
|
||||
CodeSystem codeSystemFromContext = getCodeSystemFromContext(system);
|
||||
if (codeSystemFromContext == null) {
|
||||
throw new InvalidRequestException("Unknown code system: " + system);
|
||||
}
|
||||
|
||||
if (include.getConcept().isEmpty() == false) {
|
||||
for (ValueSet.ConceptReferenceComponent next : include.getConcept()) {
|
||||
String nextCode = next.getCode();
|
||||
if (isNotBlank(nextCode) && !theAddedCodes.contains(nextCode)) {
|
||||
CodeSystem.ConceptDefinitionComponent code = findCode(codeSystemFromContext.getConcept(), nextCode);
|
||||
if (code != null) {
|
||||
if (theAdd && theAddedCodes.add(nextCode)) {
|
||||
ValueSet.ValueSetExpansionContainsComponent contains = theExpansionComponent.addContains();
|
||||
contains.setCode(nextCode);
|
||||
contains.setSystem(system);
|
||||
contains.setDisplay(code.getDisplay());
|
||||
}
|
||||
if (!theAdd && theAddedCodes.remove(nextCode)) {
|
||||
removeCodeFromExpansion(system, nextCode, theExpansionComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List<CodeSystem.ConceptDefinitionComponent> concept = codeSystemFromContext.getConcept();
|
||||
addConceptsToList(theExpansionComponent, theAddedCodes, system, concept, theAdd);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected List<VersionIndependentConcept> expandValueSetAndReturnVersionIndependentConcepts(org.hl7.fhir.r4.model.ValueSet theValueSetToExpandR4) {
|
||||
org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent expandedR4 = expandValueSet(theValueSetToExpandR4).getExpansion();
|
||||
|
||||
|
@ -905,7 +967,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
|||
codeSystem.setCurrentVersion(theCodeSystemVersion);
|
||||
codeSystem = myCodeSystemDao.saveAndFlush(codeSystem);
|
||||
|
||||
ourLog.info("Setting codesystemversion on {} concepts...", totalCodeCount);
|
||||
ourLog.info("Setting CodeSystemVersion[{}] on {} concepts...", codeSystem.getPid(), totalCodeCount);
|
||||
|
||||
for (TermConcept next : theCodeSystemVersion.getConcepts()) {
|
||||
populateVersion(next, codeSystemVersion);
|
||||
|
|
|
@ -62,18 +62,18 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc {
|
|||
public static final String SCT_FILE_CONCEPT = "Terminology/sct2_Concept_Full_";
|
||||
public static final String SCT_FILE_DESCRIPTION = "Terminology/sct2_Description_Full-en";
|
||||
public static final String SCT_FILE_RELATIONSHIP = "Terminology/sct2_Relationship_Full";
|
||||
public static final String LOINC_ANSWERLIST_FILE = "AnswerList_Beta_1.csv";
|
||||
public static final String LOINC_ANSWERLIST_LINK_FILE = "LoincAnswerListLink_Beta_1.csv";
|
||||
public static final String LOINC_ANSWERLIST_FILE = "AnswerList.csv";
|
||||
public static final String LOINC_ANSWERLIST_LINK_FILE = "LoincAnswerListLink.csv";
|
||||
public static final String LOINC_DOCUMENT_ONTOLOGY_FILE = "DocumentOntology.csv";
|
||||
public static final String LOINC_UPLOAD_PROPERTIES_FILE = "loincupload.properties";
|
||||
public static final String LOINC_FILE = "loinc.csv";
|
||||
public static final String LOINC_HIERARCHY_FILE = "MULTI-AXIAL_HIERARCHY.CSV";
|
||||
public static final String LOINC_PART_FILE = "Part_Beta_1.csv";
|
||||
public static final String LOINC_PART_LINK_FILE = "LoincPartLink_Beta_1.csv";
|
||||
public static final String LOINC_PART_RELATED_CODE_MAPPING_FILE = "PartRelatedCodeMapping_Beta_1.csv";
|
||||
public static final String LOINC_FILE = "Loinc.csv";
|
||||
public static final String LOINC_HIERARCHY_FILE = "MultiAxialHierarchy.csv";
|
||||
public static final String LOINC_PART_FILE = "Part.csv";
|
||||
public static final String LOINC_PART_LINK_FILE = "LoincPartLink.csv";
|
||||
public static final String LOINC_PART_RELATED_CODE_MAPPING_FILE = "PartRelatedCodeMapping.csv";
|
||||
public static final String LOINC_RSNA_PLAYBOOK_FILE = "LoincRsnaRadiologyPlaybook.csv";
|
||||
public static final String LOINC_TOP2000_COMMON_LAB_RESULTS_US_FILE = "Top2000CommonLabResultsUS.csv";
|
||||
public static final String LOINC_TOP2000_COMMON_LAB_RESULTS_SI_FILE = "Top2000CommonLabResultsSI.csv";
|
||||
public static final String LOINC_TOP2000_COMMON_LAB_RESULTS_US_FILE = "Top2000CommonLabResultsUs.csv";
|
||||
public static final String LOINC_TOP2000_COMMON_LAB_RESULTS_SI_FILE = "Top2000CommonLabResultsSi.csv";
|
||||
public static final String LOINC_UNIVERSAL_LAB_ORDER_VALUESET_FILE = "LoincUniversalLabOrdersValueSet.csv";
|
||||
public static final String LOINC_IEEE_MEDICAL_DEVICE_CODE_MAPPING_TABLE_CSV = "LoincIeeeMedicalDeviceCodeMappingTable.csv";
|
||||
public static final String LOINC_IMAGING_DOCUMENT_CODES_FILE = "ImagingDocumentCodes.csv";
|
||||
|
@ -275,7 +275,7 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc {
|
|||
iterateOverZipFile(theDescriptors, LOINC_PART_LINK_FILE, handler, ',', QuoteMode.NON_NUMERIC);
|
||||
|
||||
// Part related code mapping
|
||||
handler = new LoincPartRelatedCodeMappingHandler(codeSystemVersion, code2concept, valueSets, conceptMaps, uploadProperties);
|
||||
handler = new LoincPartRelatedCodeMappingHandler(code2concept, valueSets, conceptMaps, uploadProperties);
|
||||
iterateOverZipFile(theDescriptors, LOINC_PART_RELATED_CODE_MAPPING_FILE, handler, ',', QuoteMode.NON_NUMERIC);
|
||||
|
||||
// Document Ontology File
|
||||
|
|
|
@ -20,7 +20,6 @@ package ca.uhn.fhir.jpa.term.loinc;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
|
||||
import ca.uhn.fhir.jpa.term.IRecordHandler;
|
||||
|
@ -41,23 +40,20 @@ public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler impleme
|
|||
|
||||
public static final String LOINC_SCT_PART_MAP_ID = "loinc-parts-to-snomed-ct";
|
||||
public static final String LOINC_SCT_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-snomed-ct";
|
||||
public static final String LOINC_SCT_PART_MAP_NAME = "LOINC Part Map to SNOMED CT";
|
||||
public static final String LOINC_RXNORM_PART_MAP_ID = "loinc-parts-to-rxnorm";
|
||||
public static final String LOINC_RXNORM_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-rxnorm";
|
||||
public static final String LOINC_RXNORM_PART_MAP_NAME = "LOINC Part Map to RxNORM";
|
||||
public static final String LOINC_RADLEX_PART_MAP_ID = "loinc-parts-to-radlex";
|
||||
public static final String LOINC_RADLEX_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-radlex";
|
||||
public static final String LOINC_RADLEX_PART_MAP_NAME = "LOINC Part Map to RADLEX";
|
||||
private static final String LOINC_SCT_PART_MAP_NAME = "LOINC Part Map to SNOMED CT";
|
||||
private static final String LOINC_RXNORM_PART_MAP_ID = "loinc-parts-to-rxnorm";
|
||||
private static final String LOINC_RXNORM_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-rxnorm";
|
||||
private static final String LOINC_RXNORM_PART_MAP_NAME = "LOINC Part Map to RxNORM";
|
||||
private static final String LOINC_RADLEX_PART_MAP_ID = "loinc-parts-to-radlex";
|
||||
private static final String LOINC_RADLEX_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-radlex";
|
||||
private static final String LOINC_RADLEX_PART_MAP_NAME = "LOINC Part Map to RADLEX";
|
||||
private static final String CM_COPYRIGHT = "This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at https://loinc.org/license/. The LOINC Part File, LOINC/SNOMED CT Expression Association and Map Sets File, RELMA database and associated search index files include SNOMED Clinical Terms (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights are reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org. Under the terms of the Affiliate License, use of SNOMED CT in countries that are not IHTSDO Members is subject to reporting and fee payment obligations. However, IHTSDO agrees to waive the requirements to report and pay fees for use of SNOMED CT content included in the LOINC Part Mapping and LOINC Term Associations for purposes that support or enable more effective use of LOINC. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.";
|
||||
private final Map<String, TermConcept> myCode2Concept;
|
||||
private final TermCodeSystemVersion myCodeSystemVersion;
|
||||
private final List<ConceptMap> myConceptMaps;
|
||||
private static final String LOINC_PUBCHEM_PART_MAP_URI = "http://pubchem.ncbi.nlm.nih.gov";
|
||||
private static final String LOINC_PUBCHEM_PART_MAP_ID = "loinc-parts-to-pubchem";
|
||||
private static final String LOINC_PUBCHEM_PART_MAP_NAME = "LOINC Part Map to PubChem";
|
||||
|
||||
public LoincPartRelatedCodeMappingHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps, Properties theUploadProperties) {
|
||||
public LoincPartRelatedCodeMappingHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps, Properties theUploadProperties) {
|
||||
super(theCode2concept, theValueSets, theConceptMaps, theUploadProperties);
|
||||
myCodeSystemVersion = theCodeSystemVersion;
|
||||
myCode2Concept = theCode2concept;
|
||||
myConceptMaps = theConceptMaps;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -112,8 +108,16 @@ public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler impleme
|
|||
loincPartMapUri = LOINC_RADLEX_PART_MAP_URI;
|
||||
loincPartMapName = LOINC_RADLEX_PART_MAP_NAME;
|
||||
break;
|
||||
case "http://pubchem.ncbi.nlm.nih.gov":
|
||||
loincPartMapId = LOINC_PUBCHEM_PART_MAP_ID;
|
||||
loincPartMapUri = LOINC_PUBCHEM_PART_MAP_URI;
|
||||
loincPartMapName = LOINC_PUBCHEM_PART_MAP_NAME;
|
||||
break;
|
||||
default:
|
||||
throw new InternalErrorException("Don't know how to handle mapping to system: " + extCodeSystem);
|
||||
loincPartMapId = extCodeSystem.replaceAll("[^a-zA-Z]", "");
|
||||
loincPartMapUri = extCodeSystem;
|
||||
loincPartMapName = "Unknown Mapping";
|
||||
break;
|
||||
}
|
||||
|
||||
addConceptMapEntry(
|
||||
|
|
|
@ -37,10 +37,10 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs
|
|||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ZipOutputStream zos = new ZipOutputStream(bos);
|
||||
|
||||
zos.putNextEntry(new ZipEntry("loinc.csv"));
|
||||
zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/loinc.csv")));
|
||||
zos.putNextEntry(new ZipEntry("LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV"));
|
||||
zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV")));
|
||||
zos.putNextEntry(new ZipEntry("Loinc.csv"));
|
||||
zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/Loinc.csv")));
|
||||
zos.putNextEntry(new ZipEntry("MultiAxialHierarchy.csv"));
|
||||
zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/MultiAxialHierarchy.csv")));
|
||||
zos.close();
|
||||
|
||||
byte[] packageBytes = bos.toByteArray();
|
||||
|
|
|
@ -29,10 +29,10 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes
|
|||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ZipOutputStream zos = new ZipOutputStream(bos);
|
||||
|
||||
zos.putNextEntry(new ZipEntry("loinc.csv"));
|
||||
zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/loinc.csv")));
|
||||
zos.putNextEntry(new ZipEntry("LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV"));
|
||||
zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV")));
|
||||
zos.putNextEntry(new ZipEntry("Loinc.csv"));
|
||||
zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/Loinc.csv")));
|
||||
zos.putNextEntry(new ZipEntry("MultiAxialHierarchy.csv"));
|
||||
zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/MultiAxialHierarchy.csv")));
|
||||
zos.close();
|
||||
|
||||
byte[] packageBytes = bos.toByteArray();
|
||||
|
|
|
@ -321,18 +321,18 @@ public class SearchCoordinatorSvcImplTest {
|
|||
// ignore
|
||||
}
|
||||
|
||||
when(mySearchResultDao.findWithSearchUuid(any(Search.class), any(Pageable.class))).thenAnswer(new Answer<Page<SearchResult>>() {
|
||||
when(mySearchResultDao.findWithSearchUuid(any(Search.class), any(Pageable.class))).thenAnswer(new Answer<Page<Long>>() {
|
||||
@Override
|
||||
public Page<SearchResult> answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
public Page<Long> answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
Pageable page = (Pageable) theInvocation.getArguments()[1];
|
||||
|
||||
ArrayList<SearchResult> results = new ArrayList<SearchResult>();
|
||||
ArrayList<Long> results = new ArrayList<Long>();
|
||||
int max = (page.getPageNumber() * page.getPageSize()) + page.getPageSize();
|
||||
for (long i = page.getOffset(); i < max; i++) {
|
||||
results.add(new SearchResult().setResourcePid(i + 10L));
|
||||
results.add(i + 10L);
|
||||
}
|
||||
|
||||
return new PageImpl<SearchResult>(results);
|
||||
return new PageImpl<Long>(results);
|
||||
}
|
||||
});
|
||||
search.setStatus(SearchStatusEnum.FINISHED);
|
||||
|
|
|
@ -18,7 +18,7 @@ import org.junit.runner.RunWith;
|
|||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
@ -341,19 +341,19 @@ public class TerminologyLoaderSvcLoincTest {
|
|||
mySvc.loadLoinc(myFiles.getFiles(), details);
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertEquals("Could not find the following mandatory files in input: [loinc.csv, MULTI-AXIAL_HIERARCHY.CSV]", e.getMessage());
|
||||
assertEquals("Could not find the following mandatory files in input: [Loinc.csv, MultiAxialHierarchy.csv]", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void addLoincMandatoryFilesToZip(ZipCollectionBuilder theFiles) throws IOException {
|
||||
theFiles.addFileZip("/loinc/", "loinc.csv", TerminologyLoaderSvcImpl.LOINC_FILE);
|
||||
theFiles.addFileZip("/loinc/", "hierarchy.csv", TerminologyLoaderSvcImpl.LOINC_HIERARCHY_FILE);
|
||||
theFiles.addFileZip("/loinc/", "Loinc.csv", TerminologyLoaderSvcImpl.LOINC_FILE);
|
||||
theFiles.addFileZip("/loinc/", "MultiAxialHierarchy.csv", TerminologyLoaderSvcImpl.LOINC_HIERARCHY_FILE);
|
||||
}
|
||||
|
||||
static void addLoincOptionalFilesToZip(ZipCollectionBuilder theFiles) throws IOException {
|
||||
theFiles.addFileZip("/loinc/", "loincupload.properties");
|
||||
theFiles.addFileZip("/loinc/", "AnswerList_Beta_1.csv", TerminologyLoaderSvcImpl.LOINC_ANSWERLIST_FILE);
|
||||
theFiles.addFileZip("/loinc/", "AnswerList.csv", TerminologyLoaderSvcImpl.LOINC_ANSWERLIST_FILE);
|
||||
theFiles.addFileZip("/loinc/", TerminologyLoaderSvcImpl.LOINC_ANSWERLIST_LINK_FILE, TerminologyLoaderSvcImpl.LOINC_ANSWERLIST_LINK_FILE);
|
||||
theFiles.addFileZip("/loinc/", TerminologyLoaderSvcImpl.LOINC_PART_FILE, TerminologyLoaderSvcImpl.LOINC_PART_FILE);
|
||||
theFiles.addFileZip("/loinc/", TerminologyLoaderSvcImpl.LOINC_PART_LINK_FILE, TerminologyLoaderSvcImpl.LOINC_PART_LINK_FILE);
|
||||
|
@ -368,8 +368,8 @@ public class TerminologyLoaderSvcLoincTest {
|
|||
* Top 2000 files have versions in the filename so don't use the
|
||||
* constant.. that way this is a better test
|
||||
*/
|
||||
theFiles.addFilePlain("/loinc/", "LOINC_1.6_Top2000CommonLabResultsSI.csv");
|
||||
theFiles.addFilePlain("/loinc/", "LOINC_1.6_Top2000CommonLabResultsUS.csv");
|
||||
theFiles.addFilePlain("/loinc/", "Top2000CommonLabResultsSi.csv");
|
||||
theFiles.addFilePlain("/loinc/", "Top2000CommonLabResultsUs.csv");
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
|
|
@ -34,6 +34,12 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
@Autowired
|
||||
private ITermCodeSystemDao myTermCodeSystemDao;
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize());
|
||||
BaseHapiTerminologySvcImpl.setForceSaveDeferredAlwaysForUnitTest(false);
|
||||
}
|
||||
|
||||
private IIdType createCodeSystem() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl(CS_URL);
|
||||
|
@ -104,6 +110,31 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
return id;
|
||||
}
|
||||
|
||||
public void createLoincSystemWithSomeCodes() {
|
||||
runInTransaction(() -> {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl(CS_URL);
|
||||
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
|
||||
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
|
||||
|
||||
ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new);
|
||||
|
||||
TermCodeSystemVersion cs = new TermCodeSystemVersion();
|
||||
cs.setResource(table);
|
||||
|
||||
TermConcept code;
|
||||
code = new TermConcept(cs, "50015-7");
|
||||
code.addPropertyString("SYSTEM", "Bld/Bone mar^Donor");
|
||||
cs.getConcepts().add(code);
|
||||
|
||||
code = new TermConcept(cs, "43343-3");
|
||||
code.addPropertyString("SYSTEM", "Ser");
|
||||
cs.getConcepts().add(code);
|
||||
|
||||
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateDuplicateCodeSystemUri() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
|
@ -143,6 +174,40 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePropertiesAndDesignationsWithDeferredConcepts() {
|
||||
myDaoConfig.setDeferIndexingForCodesystemsOfSize(1);
|
||||
BaseHapiTerminologySvcImpl.setForceSaveDeferredAlwaysForUnitTest(true);
|
||||
|
||||
createCodeSystem();
|
||||
|
||||
Validate.notNull(myTermSvc);
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include.addConcept().setCode("childAAB");
|
||||
ValueSet outcome = myTermSvc.expandValueSet(vs);
|
||||
|
||||
List<String> codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("childAAB"));
|
||||
|
||||
ValueSet.ValueSetExpansionContainsComponent concept = outcome.getExpansion().getContains().get(0);
|
||||
assertEquals("childAAB", concept.getCode());
|
||||
assertEquals("http://example.com/my_code_system", concept.getSystem());
|
||||
assertEquals(null, concept.getDisplay());
|
||||
assertEquals("D1S", concept.getDesignation().get(0).getUse().getSystem());
|
||||
assertEquals("D1C", concept.getDesignation().get(0).getUse().getCode());
|
||||
assertEquals("D1D", concept.getDesignation().get(0).getUse().getDisplay());
|
||||
assertEquals("D1V", concept.getDesignation().get(0).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandValueSetPropertySearch() {
|
||||
createCodeSystem();
|
||||
|
@ -194,6 +259,123 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandValueSetPropertySearchWithRegexExclude() {
|
||||
createLoincSystemWithSomeCodes();
|
||||
|
||||
List<String> codes;
|
||||
ValueSet vs;
|
||||
ValueSet outcome;
|
||||
ValueSet.ConceptSetComponent exclude;
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
vs.getCompose()
|
||||
.addInclude()
|
||||
.setSystem(CS_URL);
|
||||
|
||||
exclude = vs.getCompose().addExclude();
|
||||
exclude.setSystem(CS_URL);
|
||||
exclude
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue(".*\\^Donor$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandValueSetPropertySearchWithRegexInclude() {
|
||||
// create codes with "SYSTEM" property "Bld/Bone mar^Donor" and "Ser"
|
||||
createLoincSystemWithSomeCodes();
|
||||
|
||||
List<String> codes;
|
||||
ValueSet vs;
|
||||
ValueSet outcome;
|
||||
ValueSet.ConceptSetComponent include;
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue(".*\\^Donor$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("\\^Donor$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("\\^Dono$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, empty());
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("^Donor$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, empty());
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("\\^Dono");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("^Ser$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandValueSetWholeSystem() {
|
||||
createCodeSystem();
|
||||
|
@ -209,73 +391,6 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
assertThat(codes, containsInAnyOrder("ParentWithNoChildrenA", "ParentWithNoChildrenB", "ParentWithNoChildrenC", "ParentA", "childAAA", "childAAB", "childAA", "childAB", "ParentB"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertiesAndDesignationsPreservedInExpansion() {
|
||||
createCodeSystem();
|
||||
|
||||
List<String> codes;
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include.addConcept().setCode("childAAB");
|
||||
ValueSet outcome = myTermSvc.expandValueSet(vs);
|
||||
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("childAAB"));
|
||||
|
||||
ValueSet.ValueSetExpansionContainsComponent concept = outcome.getExpansion().getContains().get(0);
|
||||
assertEquals("childAAB", concept.getCode());
|
||||
assertEquals("http://example.com/my_code_system", concept.getSystem());
|
||||
assertEquals(null, concept.getDisplay());
|
||||
assertEquals("D1S", concept.getDesignation().get(0).getUse().getSystem());
|
||||
assertEquals("D1C", concept.getDesignation().get(0).getUse().getCode());
|
||||
assertEquals("D1D", concept.getDesignation().get(0).getUse().getDisplay());
|
||||
assertEquals("D1V", concept.getDesignation().get(0).getValue());
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize());
|
||||
BaseHapiTerminologySvcImpl.setForceSaveDeferredAlwaysForUnitTest(false);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreatePropertiesAndDesignationsWithDeferredConcepts() {
|
||||
myDaoConfig.setDeferIndexingForCodesystemsOfSize(1);
|
||||
BaseHapiTerminologySvcImpl.setForceSaveDeferredAlwaysForUnitTest(true);
|
||||
|
||||
createCodeSystem();
|
||||
|
||||
Validate.notNull(myTermSvc);
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include.addConcept().setCode("childAAB");
|
||||
ValueSet outcome = myTermSvc.expandValueSet(vs);
|
||||
|
||||
List<String> codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("childAAB"));
|
||||
|
||||
ValueSet.ValueSetExpansionContainsComponent concept = outcome.getExpansion().getContains().get(0);
|
||||
assertEquals("childAAB", concept.getCode());
|
||||
assertEquals("http://example.com/my_code_system", concept.getSystem());
|
||||
assertEquals(null, concept.getDisplay());
|
||||
assertEquals("D1S", concept.getDesignation().get(0).getUse().getSystem());
|
||||
assertEquals("D1C", concept.getDesignation().get(0).getUse().getCode());
|
||||
assertEquals("D1D", concept.getDesignation().get(0).getUse().getDisplay());
|
||||
assertEquals("D1V", concept.getDesignation().get(0).getValue());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testFindCodesAbove() {
|
||||
IIdType id = createCodeSystem();
|
||||
|
@ -377,6 +492,31 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
assertThat(codes, empty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertiesAndDesignationsPreservedInExpansion() {
|
||||
createCodeSystem();
|
||||
|
||||
List<String> codes;
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include.addConcept().setCode("childAAB");
|
||||
ValueSet outcome = myTermSvc.expandValueSet(vs);
|
||||
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("childAAB"));
|
||||
|
||||
ValueSet.ValueSetExpansionContainsComponent concept = outcome.getExpansion().getContains().get(0);
|
||||
assertEquals("childAAB", concept.getCode());
|
||||
assertEquals("http://example.com/my_code_system", concept.getSystem());
|
||||
assertEquals(null, concept.getDisplay());
|
||||
assertEquals("D1S", concept.getDesignation().get(0).getUse().getSystem());
|
||||
assertEquals("D1C", concept.getDesignation().get(0).getUse().getCode());
|
||||
assertEquals("D1D", concept.getDesignation().get(0).getUse().getDisplay());
|
||||
assertEquals("D1V", concept.getDesignation().get(0).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReindexTerminology() {
|
||||
IIdType id = createCodeSystem();
|
||||
|
|
Can't render this file because it contains an unexpected character in line 1 and column 31.
|
Can't render this file because it contains an unexpected character in line 1 and column 23.
|
Can't render this file because it contains an unexpected character in line 1 and column 30.
|
|
@ -38,3 +38,4 @@
|
|||
"LP7057-5","SYSTEM","Bld","Blood","ACTIVE"
|
||||
"LP6838-9","PROPERTY","NFr","Number Fraction","ACTIVE"
|
||||
"LP6141-8","METHOD","Automated count","Automated count","ACTIVE"
|
||||
"LP15842-5","COMPONENT","Pyridoxine","Pyridoxine","ACTIVE"
|
|
|
@ -1,10 +1,12 @@
|
|||
"PartNumber","PartName","PartTypeName","ExtCodeId","ExtCodeDisplayName","ExtCodeSystem","MapType","ContentOrigin","ExtCodeSystemVersion","ExtCodeSystemCopyrightNotice"
|
||||
"LP18172-4","Interferon.beta","COMPONENT"," 420710006","Interferon beta (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP31706-2","Nornicotine","COMPONENT","1018001","Nornicotine (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP15826-8","Prostaglandin F2","COMPONENT","10192006","Prostaglandin PGF2 (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP7400-7","Liver","SYSTEM","10200004","Liver structure (body structure)","http://snomed.info/sct","LOINC broader","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP29165-5","Liver.FNA","SYSTEM","10200004","Liver structure (body structure)","http://snomed.info/sct","LOINC broader","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP15666-8","Inosine","COMPONENT","102640000","Inosine (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP15943-1","Uronate","COMPONENT","102641001","Uronic acid (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP15791-4","Phenylketones","COMPONENT","102642008","Phenylketones (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP15721-1","Malonate","COMPONENT","102648007","Malonic acid (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"PartNumber","PartName","PartTypeName","ExtCodeId","ExtCodeDisplayName","ExtCodeSystem","MapType","ContentOrigin","ExtCodeSystemVersion","ExtCodeSystemCopyrightNotice"
|
||||
"LP18172-4","Interferon.beta","COMPONENT"," 420710006","Interferon beta (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP31706-2","Nornicotine","COMPONENT","1018001","Nornicotine (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP15826-8","Prostaglandin F2","COMPONENT","10192006","Prostaglandin PGF2 (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP7400-7","Liver","SYSTEM","10200004","Liver structure (body structure)","http://snomed.info/sct","LOINC broader","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP29165-5","Liver.FNA","SYSTEM","10200004","Liver structure (body structure)","http://snomed.info/sct","LOINC broader","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP15666-8","Inosine","COMPONENT","102640000","Inosine (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP15943-1","Uronate","COMPONENT","102641001","Uronic acid (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP15791-4","Phenylketones","COMPONENT","102642008","Phenylketones (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP15721-1","Malonate","COMPONENT","102648007","Malonic acid (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP15842-5","Pyridoxine","COMPONENT","1054","Pyridoxine","http://pubchem.ncbi.nlm.nih.gov","Exact",,,
|
||||
"LP15842-5","Pyridoxine","COMPONENT","1054","Pyridoxine","http://foo/bar","Exact",,,
|
|
|
@ -1,10 +0,0 @@
|
|||
PATH_TO_ROOT,SEQUENCE,IMMEDIATE_PARENT,CODE,CODE_TEXT
|
||||
,1,,LP31755-9,Microbiology
|
||||
LP31755-9,1,LP31755-9,LP14559-6,Microorganism
|
||||
LP31755-9.LP14559-6,1,LP14559-6,LP98185-9,Bacteria
|
||||
LP31755-9.LP14559-6.LP98185-9,1,LP98185-9,LP14082-9,Bacteria
|
||||
LP31755-9.LP14559-6.LP98185-9.LP14082-9,1,LP14082-9,LP52258-8,Bacteria | Body Fluid
|
||||
LP31755-9.LP14559-6.LP98185-9.LP14082-9.LP52258-8,1,LP52258-8,41599-2,Bacteria Fld Ql Micro
|
||||
LP31755-9.LP14559-6.LP98185-9.LP14082-9,2,LP14082-9,LP52260-4,Bacteria | Cerebral spinal fluid
|
||||
LP31755-9.LP14559-6.LP98185-9.LP14082-9.LP52260-4,1,LP52260-4,41602-4,Bacteria CSF Ql Micro
|
||||
LP31755-9.LP14559-6.LP98185-9.LP14082-9,3,LP14082-9,LP52960-9,Bacteria | Cervix
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.search;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import org.apache.lucene.analysis.core.WhitespaceTokenizerFactory;
|
||||
import org.hibernate.search.elasticsearch.analyzer.definition.ElasticsearchAnalysisDefinitionRegistryBuilder;
|
||||
import org.hibernate.search.elasticsearch.analyzer.definition.ElasticsearchAnalysisDefinitionProvider;
|
||||
|
||||
|
@ -57,5 +58,8 @@ public class ElasticsearchMappingProvider implements ElasticsearchAnalysisDefini
|
|||
builder.analyzer("exactAnalyzer").withTokenizer("standard");
|
||||
|
||||
builder.analyzer("conceptParentPidsAnalyzer").withTokenizer("whitespace");
|
||||
|
||||
builder.analyzer("termConceptPropertyAnalyzer").withTokenizer("whitespace");
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -13,6 +14,8 @@ import java.util.concurrent.TimeUnit;
|
|||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.*;
|
||||
|
@ -56,6 +59,34 @@ public class BinaryDstu2Test {
|
|||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourPort = PortUtil.findFreePort();
|
||||
ourServer = new Server(ourPort);
|
||||
|
||||
ResourceProvider binaryProvider = new ResourceProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer(ourCtx);
|
||||
servlet.setResourceProviders(binaryProvider);
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
|
||||
int timeout = 5;
|
||||
RequestConfig config = RequestConfig.custom()
|
||||
.setConnectTimeout(timeout * 1000)
|
||||
.setConnectionRequestTimeout(timeout * 1000)
|
||||
.setSocketTimeout(timeout * 1000).build();
|
||||
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.setDefaultRequestConfig(config).build();
|
||||
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
|
@ -65,53 +96,56 @@ public class BinaryDstu2Test {
|
|||
@Test
|
||||
public void testReadWithExplicitTypeXml() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo?_format=xml");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), "UTF-8");
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
try (CloseableHttpResponse response = ourClient.execute(httpGet)) {
|
||||
String responseContent = IOUtils.toString(response.getEntity().getContent(), "UTF-8");
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
|
||||
ourLog.info(responseContent);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertThat(status.getFirstHeader("content-type").getValue(), startsWith(Constants.CT_FHIR_XML + ";"));
|
||||
|
||||
Binary bin = ourCtx.newXmlParser().parseResource(Binary.class, responseContent);
|
||||
assertEquals("foo", bin.getContentType());
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, bin.getContent());
|
||||
ourLog.info(responseContent);
|
||||
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
assertThat(response.getFirstHeader("content-type").getValue(), startsWith(Constants.CT_FHIR_XML + ";"));
|
||||
|
||||
Binary bin = ourCtx.newXmlParser().parseResource(Binary.class, responseContent);
|
||||
assertEquals("foo", bin.getContentType());
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, bin.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithExplicitTypeJson() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo?_format=json");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), "UTF-8");
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
try (CloseableHttpResponse response = ourClient.execute(httpGet)){
|
||||
String responseContent = IOUtils.toString(response.getEntity().getContent(), "UTF-8");
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
|
||||
ourLog.info(responseContent);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertThat(status.getFirstHeader("content-type").getValue(), startsWith(Constants.CT_FHIR_JSON + ";"));
|
||||
|
||||
Binary bin = ourCtx.newJsonParser().parseResource(Binary.class, responseContent);
|
||||
assertEquals("foo", bin.getContentType());
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, bin.getContent());
|
||||
ourLog.info(responseContent);
|
||||
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
assertThat(response.getFirstHeader("content-type").getValue(), startsWith(Constants.CT_FHIR_JSON + ";"));
|
||||
|
||||
Binary bin = ourCtx.newJsonParser().parseResource(Binary.class, responseContent);
|
||||
assertEquals("foo", bin.getContentType());
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, bin.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// posts Binary directly
|
||||
@Test
|
||||
public void testCreate() throws Exception {
|
||||
public void testPostBinary() throws Exception {
|
||||
HttpPost http = new HttpPost("http://localhost:" + ourPort + "/Binary");
|
||||
http.setEntity(new ByteArrayEntity(new byte[] { 1, 2, 3, 4 }, ContentType.create("foo/bar", "UTF-8")));
|
||||
|
||||
HttpResponse status = ourClient.execute(http);
|
||||
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||
|
||||
assertEquals("foo/bar; charset=UTF-8", ourLast.getContentType());
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, ourLast.getContent());
|
||||
try (CloseableHttpResponse response = ourClient.execute(http)){
|
||||
assertEquals(201, response.getStatusLine().getStatusCode());
|
||||
|
||||
assertEquals("foo/bar; charset=UTF-8", ourLast.getContentType());
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, ourLast.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
// posts Binary as FHIR Resource
|
||||
@Test
|
||||
public void testCreateWrongType() throws Exception {
|
||||
public void testPostFhirBinary() throws Exception {
|
||||
Binary res = new Binary();
|
||||
res.setContent(new byte[] { 1, 2, 3, 4 });
|
||||
res.setContentType("text/plain");
|
||||
|
@ -120,114 +154,92 @@ public class BinaryDstu2Test {
|
|||
HttpPost http = new HttpPost("http://localhost:" + ourPort + "/Binary");
|
||||
http.setEntity(new StringEntity(stringContent, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
|
||||
|
||||
HttpResponse status = ourClient.execute(http);
|
||||
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||
|
||||
assertEquals("application/json+fhir;charset=utf-8", ourLast.getContentType().replace(" ","").toLowerCase());
|
||||
try (CloseableHttpResponse response = ourClient.execute(http)) {
|
||||
assertEquals(201, response.getStatusLine().getStatusCode());
|
||||
|
||||
assertEquals("text/plain", ourLast.getContentType().replace(" ", "").toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBinaryReadAcceptMissing() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo");
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
byte[] responseContent = IOUtils.toByteArray(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("foo", status.getFirstHeader("content-type").getValue());
|
||||
assertEquals("Attachment;", status.getFirstHeader("Content-Disposition").getValue());
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, responseContent);
|
||||
HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary/foo");
|
||||
|
||||
binaryRead(http);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBinaryReadAcceptBrowser() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo");
|
||||
httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1");
|
||||
httpGet.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
byte[] responseContent = IOUtils.toByteArray(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("foo", status.getFirstHeader("content-type").getValue());
|
||||
assertEquals("Attachment;", status.getFirstHeader("Content-Disposition").getValue()); // This is a security requirement!
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, responseContent);
|
||||
HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary/foo");
|
||||
http.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1");
|
||||
http.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||
|
||||
binaryRead(http);
|
||||
}
|
||||
|
||||
|
||||
private void binaryRead(HttpGet http) throws IOException {
|
||||
try (CloseableHttpResponse status = ourClient.execute(http)) {
|
||||
byte[] responseContent = IOUtils.toByteArray(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("foo", status.getFirstHeader("content-type").getValue());
|
||||
assertEquals("Attachment;", status.getFirstHeader("Content-Disposition").getValue()); // This is a security requirement!
|
||||
assertArrayEquals(new byte[]{1, 2, 3, 4}, responseContent);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBinaryReadAcceptFhirJson() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo");
|
||||
httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1");
|
||||
httpGet.addHeader("Accept", Constants.CT_FHIR_JSON);
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(Constants.CT_FHIR_JSON + ";charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").toLowerCase());
|
||||
assertNull(status.getFirstHeader("Content-Disposition"));
|
||||
assertEquals("{\"resourceType\":\"Binary\",\"id\":\"1\",\"contentType\":\"foo\",\"content\":\"AQIDBA==\"}", responseContent);
|
||||
HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary/foo");
|
||||
http.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1");
|
||||
http.addHeader("Accept", Constants.CT_FHIR_JSON);
|
||||
|
||||
try (CloseableHttpResponse status = ourClient.execute(http)) {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(Constants.CT_FHIR_JSON + ";charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").toLowerCase());
|
||||
assertNull(status.getFirstHeader("Content-Disposition"));
|
||||
assertEquals("{\"resourceType\":\"Binary\",\"id\":\"1\",\"contentType\":\"foo\",\"content\":\"AQIDBA==\"}", responseContent);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchJson() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary?_pretty=true&_format=json");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(Constants.CT_FHIR_JSON + ";charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").replace("UTF", "utf"));
|
||||
HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary?_pretty=true&_format=json");
|
||||
try (CloseableHttpResponse response = ourClient.execute(http)) {
|
||||
String responseContent = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
assertEquals(Constants.CT_FHIR_JSON + ";charset=utf-8", response.getFirstHeader("content-type").getValue().replace(" ", "").replace("UTF", "utf"));
|
||||
|
||||
ourLog.info(responseContent);
|
||||
ourLog.info(responseContent);
|
||||
|
||||
Bundle bundle = ourCtx.newJsonParser().parseResource(Bundle.class, responseContent);
|
||||
Binary bin = (Binary) bundle.getEntry().get(0).getResource();
|
||||
Bundle bundle = ourCtx.newJsonParser().parseResource(Bundle.class, responseContent);
|
||||
Binary bin = (Binary) bundle.getEntry().get(0).getResource();
|
||||
|
||||
assertEquals("text/plain", bin.getContentType());
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, bin.getContent());
|
||||
assertEquals("text/plain", bin.getContentType());
|
||||
assertArrayEquals(new byte[]{1, 2, 3, 4}, bin.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchXml() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary?_pretty=true");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(Constants.CT_FHIR_XML + ";charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").replace("UTF", "utf"));
|
||||
HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary?_pretty=true");
|
||||
try (CloseableHttpResponse response = ourClient.execute(http)) {
|
||||
String responseContent = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
assertEquals(Constants.CT_FHIR_XML + ";charset=utf-8", response.getFirstHeader("content-type").getValue().replace(" ", "").replace("UTF", "utf"));
|
||||
|
||||
ourLog.info(responseContent);
|
||||
ourLog.info(responseContent);
|
||||
|
||||
Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent);
|
||||
Binary bin = (Binary) bundle.getEntry().get(0).getResource();
|
||||
|
||||
assertEquals("text/plain", bin.getContentType());
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, bin.getContent());
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourPort = PortUtil.findFreePort();
|
||||
ourServer = new Server(ourPort);
|
||||
|
||||
ResourceProvider patientProvider = new ResourceProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer(ourCtx);
|
||||
servlet.setResourceProviders(patientProvider);
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent);
|
||||
Binary bin = (Binary) bundle.getEntry().get(0).getResource();
|
||||
|
||||
assertEquals("text/plain", bin.getContentType());
|
||||
assertArrayEquals(new byte[]{1, 2, 3, 4}, bin.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
public static class ResourceProvider implements IResourceProvider {
|
||||
|
@ -260,7 +272,5 @@ public class BinaryDstu2Test {
|
|||
retVal.setContentType("text/plain");
|
||||
return Collections.singletonList(retVal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package fluentpath;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.hl7.fhir.dstu3.model.Encounter;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
|
||||
public class Example20_ValidateResource {
|
||||
public static void main(String[] args) {
|
||||
|
||||
// Create an incomplete encounter (status is required)
|
||||
Encounter enc = new Encounter();
|
||||
enc.addIdentifier().setSystem("http://acme.org/encNums").setValue("12345");
|
||||
|
||||
// Create a new validator
|
||||
FhirContext ctx = FhirContext.forDstu3();
|
||||
FhirValidator validator = ctx.newValidator();
|
||||
|
||||
// Did we succeed?
|
||||
ValidationResult result = validator.validateWithResult(enc);
|
||||
System.out.println("Success: " + result.isSuccessful());
|
||||
|
||||
// What was the result
|
||||
OperationOutcome outcome = (OperationOutcome) result.toOperationOutcome();
|
||||
IParser parser = ctx.newXmlParser().setPrettyPrint(true);
|
||||
System.out.println(parser.encodeResourceToString(outcome));
|
||||
}
|
||||
}
|
|
@ -66,6 +66,16 @@
|
|||
<![CDATA[<code>setFlags(flags)</code>]]>
|
||||
can be used to maintain the previous behaviour.
|
||||
</action>
|
||||
<action type="974">
|
||||
JPA server loading logic has been improved to enhance performance when
|
||||
loading a large number of results in a page, or when loading multiple
|
||||
search results with tags. Thanks to Frank Tao for the pull request!
|
||||
</action>
|
||||
<action type="add" issue="1000">
|
||||
LOINC uploader has been updated to support the new LOINC filename
|
||||
scheme introduced in LOINC 2.64. Thanks to Rob Hausam for the
|
||||
pull request!
|
||||
</action>
|
||||
</release>
|
||||
<release version="3.4.0" date="2018-05-28">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue