Keep the complete resource history in the history table, including the
current version
This commit is contained in:
parent
3bfcb57783
commit
6f79f6c5e2
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
|
@ -27,7 +28,6 @@ import java.text.Normalizer;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -35,6 +35,7 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
|
@ -66,14 +67,8 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
|||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
|
@ -83,6 +78,9 @@ import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
|
|||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||
import ca.uhn.fhir.jpa.dao.dstu3.SearchParamExtractorDstu3;
|
||||
import ca.uhn.fhir.jpa.entity.BaseHasResource;
|
||||
import ca.uhn.fhir.jpa.entity.BaseResourceIndexedSearchParam;
|
||||
|
@ -101,10 +99,12 @@ import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamUri;
|
|||
import ca.uhn.fhir.jpa.entity.ResourceLink;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTag;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
|
||||
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
||||
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
|
||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterAnd;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
|
@ -151,10 +151,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
public static final long INDEX_STATUS_INDEXED = Long.valueOf(1L);
|
||||
public static final long INDEX_STATUS_INDEXING_FAILED = Long.valueOf(2L);
|
||||
public static final String NS_JPA_PROFILE = "https://github.com/jamesagnew/hapi-fhir/ns/jpa/profile";
|
||||
|
||||
public static final String OO_SEVERITY_ERROR = "error";
|
||||
public static final String OO_SEVERITY_INFO = "information";
|
||||
|
||||
public static final String OO_SEVERITY_WARN = "warning";
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirDao.class);
|
||||
|
@ -204,10 +202,19 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
@Autowired
|
||||
private List<IFhirResourceDao<?>> myResourceDaos;
|
||||
|
||||
@Autowired
|
||||
private IResourceHistoryTableDao myResourceHistoryTableDao;
|
||||
|
||||
private Map<Class<? extends IBaseResource>, IFhirResourceDao<?>> myResourceTypeToDao;
|
||||
|
||||
@Autowired
|
||||
private ISearchDao mySearchDao;
|
||||
|
||||
private ISearchParamExtractor mySearchParamExtractor;
|
||||
|
||||
@Autowired
|
||||
private ISearchResultDao mySearchResultDao;
|
||||
|
||||
protected void createForcedIdIfNeeded(ResourceTable entity, IIdType id) {
|
||||
if (id.isEmpty() == false && id.hasIdPart()) {
|
||||
if (isValidPid(id)) {
|
||||
|
@ -269,19 +276,22 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
|
||||
String typeString = nextValue.getReferenceElement().getResourceType();
|
||||
if (isBlank(typeString)) {
|
||||
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource type - " + nextValue.getReferenceElement().getValue());
|
||||
throw new InvalidRequestException(
|
||||
"Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource type - " + nextValue.getReferenceElement().getValue());
|
||||
}
|
||||
RuntimeResourceDefinition resourceDefinition;
|
||||
try {
|
||||
resourceDefinition = getContext().getResourceDefinition(typeString);
|
||||
} catch (DataFormatException e) {
|
||||
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " + nextValue.getReferenceElement().getValue());
|
||||
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - "
|
||||
+ nextValue.getReferenceElement().getValue());
|
||||
}
|
||||
|
||||
Class<? extends IBaseResource> type = resourceDefinition.getImplementingClass();
|
||||
String id = nextValue.getReferenceElement().getIdPart();
|
||||
if (StringUtils.isBlank(id)) {
|
||||
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource ID - " + nextValue.getReferenceElement().getValue());
|
||||
throw new InvalidRequestException(
|
||||
"Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource ID - " + nextValue.getReferenceElement().getValue());
|
||||
}
|
||||
|
||||
IFhirResourceDao<?> dao = getDao(type);
|
||||
|
@ -311,7 +321,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
|
||||
if (!typeString.equals(target.getResourceType())) {
|
||||
throw new UnprocessableEntityException("Resource contains reference to " + nextValue.getReferenceElement().getValue() + " but resource with ID " + nextValue.getReferenceElement().getIdPart() + " is actually of type " + target.getResourceType());
|
||||
throw new UnprocessableEntityException("Resource contains reference to " + nextValue.getReferenceElement().getValue() + " but resource with ID "
|
||||
+ nextValue.getReferenceElement().getIdPart() + " is actually of type " + target.getResourceType());
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -364,15 +375,15 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
protected Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
||||
return mySearchParamExtractor.extractSearchParamCoords(theEntity, theResource);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void setResourceDaos(List<IFhirResourceDao<?>> theResourceDaos) {
|
||||
// myResourceDaos = theResourceDaos;
|
||||
// }
|
||||
|
||||
protected Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
||||
return mySearchParamExtractor.extractSearchParamCoords(theEntity, theResource);
|
||||
}
|
||||
|
||||
protected Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IBaseResource theResource) {
|
||||
return mySearchParamExtractor.extractSearchParamDates(theEntity, theResource);
|
||||
}
|
||||
|
@ -501,6 +512,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
return myConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FhirContext getContext() {
|
||||
return myContext;
|
||||
}
|
||||
|
@ -608,90 +620,48 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
|
||||
protected IBundleProvider history(String theResourceName, Long theId, Date theSince) {
|
||||
final List<HistoryTuple> tuples = new ArrayList<HistoryTuple>();
|
||||
|
||||
final InstantDt end = createHistoryToTimestamp();
|
||||
String resourceName = defaultIfBlank(theResourceName, null);
|
||||
|
||||
StopWatch timer = new StopWatch();
|
||||
Search search = new Search();
|
||||
search.setCreated(new Date());
|
||||
search.setLastUpdated(null, theSince);
|
||||
search.setUuid(UUID.randomUUID().toString());
|
||||
search.setResourceType(resourceName);
|
||||
search.setResourceId(theId);
|
||||
search.setSearchType(SearchTypeEnum.HISTORY);
|
||||
|
||||
int limit = 10000;
|
||||
|
||||
// Get list of IDs
|
||||
searchHistoryCurrentVersion(theResourceName, theId, theSince, end.getValue(), limit, tuples);
|
||||
ourLog.info("Retrieved {} history IDs from current versions in {} ms", tuples.size(), timer.getMillisAndRestart());
|
||||
|
||||
searchHistoryHistory(theResourceName, theId, theSince, end.getValue(), limit, tuples);
|
||||
ourLog.info("Retrieved {} history IDs from previous versions in {} ms", tuples.size(), timer.getMillisAndRestart());
|
||||
|
||||
// Sort merged list
|
||||
Collections.sort(tuples, Collections.reverseOrder());
|
||||
assert tuples.size() < 2 || !tuples.get(tuples.size() - 2).getUpdated().before(tuples.get(tuples.size() - 1).getUpdated()) : tuples.toString();
|
||||
|
||||
return new IBundleProvider() {
|
||||
|
||||
@Override
|
||||
public InstantDt getPublished() {
|
||||
return end;
|
||||
if (theSince != null) {
|
||||
if (resourceName == null) {
|
||||
search.setTotalCount(myResourceHistoryTableDao.countForAllResourceTypes(theSince));
|
||||
} else if (theId == null) {
|
||||
search.setTotalCount(myResourceHistoryTableDao.countForResourceType(resourceName, theSince));
|
||||
} else {
|
||||
search.setTotalCount(myResourceHistoryTableDao.countForResourceInstance(theId, theSince));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> getResources(final int theFromIndex, final int theToIndex) {
|
||||
final StopWatch grTimer = new StopWatch();
|
||||
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
|
||||
return template.execute(new TransactionCallback<List<IBaseResource>>() {
|
||||
@Override
|
||||
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
|
||||
List<BaseHasResource> resEntities = Lists.newArrayList();
|
||||
|
||||
List<HistoryTuple> tupleSubList = tuples.subList(theFromIndex, theToIndex);
|
||||
searchHistoryCurrentVersion(tupleSubList, resEntities);
|
||||
ourLog.info("Loaded history from current versions in {} ms", grTimer.getMillisAndRestart());
|
||||
|
||||
searchHistoryHistory(tupleSubList, resEntities);
|
||||
ourLog.info("Loaded history from previous versions in {} ms", grTimer.getMillisAndRestart());
|
||||
|
||||
Collections.sort(resEntities, new Comparator<BaseHasResource>() {
|
||||
@Override
|
||||
public int compare(BaseHasResource theO1, BaseHasResource theO2) {
|
||||
return theO2.getUpdated().getValue().compareTo(theO1.getUpdated().getValue());
|
||||
}
|
||||
});
|
||||
|
||||
int grLimit = theToIndex - theFromIndex;
|
||||
if (resEntities.size() > grLimit) {
|
||||
resEntities = resEntities.subList(0, grLimit);
|
||||
}
|
||||
|
||||
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||
for (BaseHasResource next : resEntities) {
|
||||
RuntimeResourceDefinition type;
|
||||
try {
|
||||
type = myContext.getResourceDefinition(next.getResourceType());
|
||||
} catch (DataFormatException e) {
|
||||
if (next.getFhirVersion() != getContext().getVersion().getVersion()) {
|
||||
ourLog.info("Ignoring history resource of type[{}] because it is not compatible with version[{}]", next.getResourceType(), getContext().getVersion().getVersion());
|
||||
continue;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
IBaseResource resource = toResource(type.getImplementingClass(), next, true);
|
||||
retVal.add(resource);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (resourceName == null) {
|
||||
search.setTotalCount(myResourceHistoryTableDao.countForAllResourceTypes());
|
||||
} else if (theId == null) {
|
||||
search.setTotalCount(myResourceHistoryTableDao.countForResourceType(resourceName));
|
||||
} else {
|
||||
search.setTotalCount(myResourceHistoryTableDao.countForResourceInstance(theId));
|
||||
}
|
||||
}
|
||||
|
||||
search = mySearchDao.save(search);
|
||||
|
||||
@Override
|
||||
public Integer preferredPageSize() {
|
||||
return null;
|
||||
}
|
||||
return new PersistedJpaBundleProvider(search.getUuid(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return tuples.size();
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void injectDependenciesIntoBundleProvider(PersistedJpaBundleProvider theProvider) {
|
||||
theProvider.setContext(getContext());
|
||||
theProvider.setEntityManager(myEntityManager);
|
||||
theProvider.setPlatformTransactionManager(myPlatformTransactionManager);
|
||||
theProvider.setResourceHistoryTableDao(myResourceHistoryTableDao);
|
||||
theProvider.setSearchDao(mySearchDao);
|
||||
theProvider.setSearchResultDao(mySearchResultDao);
|
||||
}
|
||||
|
||||
protected void notifyInterceptors(RestOperationTypeEnum operationType, ActionRequestDetails requestDetails) {
|
||||
|
@ -808,8 +778,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
} else if (theForHistoryOperation) {
|
||||
/*
|
||||
* If the create and update times match, this was when the resource was created so we should mark it as a POST.
|
||||
* Otherwise, it's a PUT.
|
||||
* If the create and update times match, this was when the resource was created so we should mark it as a POST. Otherwise, it's a PUT.
|
||||
*/
|
||||
Date published = theEntity.getPublished().getValue();
|
||||
Date updated = theEntity.getUpdated().getValue();
|
||||
|
@ -863,7 +832,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
ResourceMetadataKeyEnum.PROFILES.put(res, profiles);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -879,8 +848,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
} else if (theForHistoryOperation) {
|
||||
/*
|
||||
* If the create and update times match, this was when the resource was created so we should mark it as a POST.
|
||||
* Otherwise, it's a PUT.
|
||||
* If the create and update times match, this was when the resource was created so we should mark it as a POST. Otherwise, it's a PUT.
|
||||
*/
|
||||
Date published = theEntity.getPublished().getValue();
|
||||
Date updated = theEntity.getUpdated().getValue();
|
||||
|
@ -896,13 +864,14 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
res.getMeta().getSecurity().clear();
|
||||
res.getMeta().setLastUpdated(null);
|
||||
res.getMeta().setVersionId(null);
|
||||
|
||||
|
||||
populateResourceId(res, theEntity);
|
||||
|
||||
res.getMeta().setLastUpdated(theEntity.getUpdatedDate());
|
||||
IDao.RESOURCE_PID.put(res, theEntity.getId());
|
||||
|
||||
Collection<? extends BaseTag> tags = theEntity.getTags();
|
||||
|
||||
if (theEntity.isHasTags()) {
|
||||
for (BaseTag next : tags) {
|
||||
switch (next.getTag().getTagType()) {
|
||||
|
@ -928,8 +897,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the
|
||||
* first time.
|
||||
* Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the first time.
|
||||
*
|
||||
* @param theEntity
|
||||
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
|
||||
|
@ -943,8 +911,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the
|
||||
* first time.
|
||||
* Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the first time.
|
||||
*
|
||||
* @param theEntity
|
||||
* The resource
|
||||
|
@ -977,146 +944,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
throw new NotImplementedException("");
|
||||
}
|
||||
|
||||
private void searchHistoryCurrentVersion(List<HistoryTuple> theTuples, List<BaseHasResource> theRetVal) {
|
||||
Collection<HistoryTuple> tuples = Collections2.filter(theTuples, new com.google.common.base.Predicate<HistoryTuple>() {
|
||||
@Override
|
||||
public boolean apply(HistoryTuple theInput) {
|
||||
return theInput.isHistory() == false;
|
||||
}
|
||||
});
|
||||
Collection<Long> ids = Collections2.transform(tuples, new Function<HistoryTuple, Long>() {
|
||||
@Override
|
||||
public Long apply(HistoryTuple theInput) {
|
||||
return theInput.getId();
|
||||
}
|
||||
});
|
||||
if (ids.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ResourceTable> cq = builder.createQuery(ResourceTable.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.where(from.get("myId").in(ids));
|
||||
|
||||
cq.orderBy(builder.desc(from.get("myUpdated")));
|
||||
TypedQuery<ResourceTable> q = myEntityManager.createQuery(cq);
|
||||
for (ResourceTable next : q.getResultList()) {
|
||||
theRetVal.add(next);
|
||||
}
|
||||
}
|
||||
|
||||
private void searchHistoryCurrentVersion(String theResourceName, Long theId, Date theSince, Date theEnd, Integer theLimit, List<HistoryTuple> tuples) {
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Tuple> cq = builder.createTupleQuery();
|
||||
Root<?> from = cq.from(ResourceTable.class);
|
||||
cq.multiselect(from.get("myId").as(Long.class), from.get("myUpdated").as(Date.class));
|
||||
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
if (theSince != null) {
|
||||
Predicate low = builder.greaterThanOrEqualTo(from.<Date> get("myUpdated"), theSince);
|
||||
predicates.add(low);
|
||||
}
|
||||
|
||||
Predicate high = builder.lessThan(from.<Date> get("myUpdated"), theEnd);
|
||||
predicates.add(high);
|
||||
|
||||
if (theResourceName != null) {
|
||||
predicates.add(builder.equal(from.get("myResourceType"), theResourceName));
|
||||
}
|
||||
if (theId != null) {
|
||||
predicates.add(builder.equal(from.get("myId"), theId));
|
||||
}
|
||||
|
||||
cq.where(builder.and(predicates.toArray(new Predicate[0])));
|
||||
|
||||
cq.orderBy(builder.desc(from.get("myUpdated")));
|
||||
TypedQuery<Tuple> q = myEntityManager.createQuery(cq);
|
||||
if (theLimit != null && theLimit < myConfig.getHardSearchLimit()) {
|
||||
q.setMaxResults(theLimit);
|
||||
} else {
|
||||
q.setMaxResults(myConfig.getHardSearchLimit());
|
||||
}
|
||||
for (Tuple next : q.getResultList()) {
|
||||
long id = next.get(0, Long.class);
|
||||
Date updated = next.get(1, Date.class);
|
||||
tuples.add(new HistoryTuple(false, updated, id));
|
||||
}
|
||||
}
|
||||
|
||||
private void searchHistoryHistory(List<HistoryTuple> theTuples, List<BaseHasResource> theRetVal) {
|
||||
Collection<HistoryTuple> tuples = Collections2.filter(theTuples, new com.google.common.base.Predicate<HistoryTuple>() {
|
||||
@Override
|
||||
public boolean apply(HistoryTuple theInput) {
|
||||
return theInput.isHistory() == true;
|
||||
}
|
||||
});
|
||||
Collection<Long> ids = Collections2.transform(tuples, new Function<HistoryTuple, Long>() {
|
||||
@Override
|
||||
public Long apply(HistoryTuple theInput) {
|
||||
return (Long) theInput.getId();
|
||||
}
|
||||
});
|
||||
if (ids.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ourLog.info("Retrieving {} history elements from ResourceHistoryTable", ids.size());
|
||||
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ResourceHistoryTable> cq = builder.createQuery(ResourceHistoryTable.class);
|
||||
Root<ResourceHistoryTable> from = cq.from(ResourceHistoryTable.class);
|
||||
cq.where(from.get("myId").in(ids));
|
||||
|
||||
cq.orderBy(builder.desc(from.get("myUpdated")));
|
||||
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery(cq);
|
||||
for (ResourceHistoryTable next : q.getResultList()) {
|
||||
theRetVal.add(next);
|
||||
}
|
||||
}
|
||||
|
||||
private void searchHistoryHistory(String theResourceName, Long theResourceId, Date theSince, Date theEnd, Integer theLimit, List<HistoryTuple> tuples) {
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Tuple> cq = builder.createTupleQuery();
|
||||
Root<?> from = cq.from(ResourceHistoryTable.class);
|
||||
cq.multiselect(from.get("myId").as(Long.class), from.get("myUpdated").as(Date.class));
|
||||
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
if (theSince != null) {
|
||||
Predicate low = builder.greaterThanOrEqualTo(from.<Date> get("myUpdated"), theSince);
|
||||
predicates.add(low);
|
||||
}
|
||||
|
||||
Predicate high = builder.lessThan(from.<Date> get("myUpdated"), theEnd);
|
||||
predicates.add(high);
|
||||
|
||||
if (theResourceName != null) {
|
||||
predicates.add(builder.equal(from.get("myResourceType"), theResourceName));
|
||||
}
|
||||
if (theResourceId != null) {
|
||||
predicates.add(builder.equal(from.get("myResourceId"), theResourceId));
|
||||
}
|
||||
|
||||
cq.where(builder.and(predicates.toArray(new Predicate[0])));
|
||||
|
||||
cq.orderBy(builder.desc(from.get("myUpdated")));
|
||||
TypedQuery<Tuple> q = myEntityManager.createQuery(cq);
|
||||
if (theLimit != null && theLimit < myConfig.getHardSearchLimit()) {
|
||||
q.setMaxResults(theLimit);
|
||||
} else {
|
||||
q.setMaxResults(myConfig.getHardSearchLimit());
|
||||
}
|
||||
for (Tuple next : q.getResultList()) {
|
||||
Long id = next.get(0, Long.class);
|
||||
Date updated = (Date) next.get(1);
|
||||
tuples.add(new HistoryTuple(true, updated, id));
|
||||
}
|
||||
}
|
||||
|
||||
public void setConfig(DaoConfig theConfig) {
|
||||
myConfig = theConfig;
|
||||
}
|
||||
|
||||
// protected MetaDt toMetaDt(Collection<TagDefinition> tagDefinitions) {
|
||||
// MetaDt retVal = new MetaDt();
|
||||
// for (TagDefinition next : tagDefinitions) {
|
||||
|
@ -1135,6 +962,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
// return retVal;
|
||||
// }
|
||||
|
||||
public void setConfig(DaoConfig theConfig) {
|
||||
myConfig = theConfig;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setContext(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
|
@ -1162,15 +993,12 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
|
||||
/**
|
||||
* This method is called when an update to an existing resource detects that the resource supplied for update is
|
||||
* missing a tag/profile/security label that the currently persisted resource holds.
|
||||
* This method is called when an update to an existing resource detects that the resource supplied for update is missing a tag/profile/security label that the currently persisted resource holds.
|
||||
* <p>
|
||||
* The default implementation removes any profile declarations, but leaves tags and security labels in place.
|
||||
* Subclasses may choose to override and change this behaviour.
|
||||
* The default implementation removes any profile declarations, but leaves tags and security labels in place. Subclasses may choose to override and change this behaviour.
|
||||
* </p>
|
||||
* <p>
|
||||
* See <a href="http://hl7.org/fhir/2015Sep/resource.html#1.11.3.7">Updates to Tags, Profiles, and Security
|
||||
* Labels</a> for a description of the logic that the default behaviour folows.
|
||||
* See <a href="http://hl7.org/fhir/2015Sep/resource.html#1.11.3.7">Updates to Tags, Profiles, and Security Labels</a> for a description of the logic that the default behaviour folows.
|
||||
* </p>
|
||||
*
|
||||
* @param theEntity
|
||||
|
@ -1200,53 +1028,53 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
return toResource(type.getImplementingClass(), theEntity, theForHistoryOperation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation) {
|
||||
String resourceText = null;
|
||||
switch (theEntity.getEncoding()) {
|
||||
case JSON:
|
||||
try {
|
||||
resourceText = new String(theEntity.getResource(), "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new Error("Should not happen", e);
|
||||
}
|
||||
break;
|
||||
case JSONC:
|
||||
resourceText = GZipUtil.decompress(theEntity.getResource());
|
||||
break;
|
||||
}
|
||||
|
||||
IParser parser = theEntity.getEncoding().newParser(getContext(theEntity.getFhirVersion()));
|
||||
R retVal;
|
||||
@Override
|
||||
public <R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation) {
|
||||
String resourceText = null;
|
||||
switch (theEntity.getEncoding()) {
|
||||
case JSON:
|
||||
try {
|
||||
retVal = parser.parseResource(theResourceType, resourceText);
|
||||
} catch (Exception e) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("Failed to parse database resource[");
|
||||
b.append(theResourceType);
|
||||
b.append("/");
|
||||
b.append(theEntity.getIdDt().getIdPart());
|
||||
b.append(" (pid ");
|
||||
b.append(theEntity.getId());
|
||||
b.append(", version ");
|
||||
b.append(myContext.getVersion().getVersion());
|
||||
b.append("): ");
|
||||
b.append(e.getMessage());
|
||||
String msg = b.toString();
|
||||
ourLog.error(msg, e);
|
||||
throw new DataFormatException(msg, e);
|
||||
resourceText = new String(theEntity.getResource(), "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new Error("Should not happen", e);
|
||||
}
|
||||
|
||||
if (retVal instanceof IResource) {
|
||||
IResource res = (IResource) retVal;
|
||||
retVal = populateResourceMetadataHapi(theResourceType, theEntity, theForHistoryOperation, res);
|
||||
} else {
|
||||
IAnyResource res = (IAnyResource) retVal;
|
||||
retVal = populateResourceMetadataRi(theResourceType, theEntity, theForHistoryOperation, res);
|
||||
}
|
||||
return retVal;
|
||||
break;
|
||||
case JSONC:
|
||||
resourceText = GZipUtil.decompress(theEntity.getResource());
|
||||
break;
|
||||
}
|
||||
|
||||
IParser parser = theEntity.getEncoding().newParser(getContext(theEntity.getFhirVersion()));
|
||||
R retVal;
|
||||
try {
|
||||
retVal = parser.parseResource(theResourceType, resourceText);
|
||||
} catch (Exception e) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("Failed to parse database resource[");
|
||||
b.append(theResourceType);
|
||||
b.append("/");
|
||||
b.append(theEntity.getIdDt().getIdPart());
|
||||
b.append(" (pid ");
|
||||
b.append(theEntity.getId());
|
||||
b.append(", version ");
|
||||
b.append(myContext.getVersion().getVersion());
|
||||
b.append("): ");
|
||||
b.append(e.getMessage());
|
||||
String msg = b.toString();
|
||||
ourLog.error(msg, e);
|
||||
throw new DataFormatException(msg, e);
|
||||
}
|
||||
|
||||
if (retVal instanceof IResource) {
|
||||
IResource res = (IResource) retVal;
|
||||
retVal = populateResourceMetadataHapi(theResourceType, theEntity, theForHistoryOperation, res);
|
||||
} else {
|
||||
IAnyResource res = (IAnyResource) retVal;
|
||||
retVal = populateResourceMetadataRi(theResourceType, theEntity, theForHistoryOperation, res);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
protected String toResourceName(Class<? extends IBaseResource> theResourceType) {
|
||||
return myContext.getResourceDefinition(theResourceType).getName();
|
||||
}
|
||||
|
@ -1268,12 +1096,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
}
|
||||
|
||||
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, Date theUpdateTime, RequestDetails theRequestDetails) {
|
||||
return updateEntity(theResource, entity, theUpdateHistory, theDeletedTimestampOrNull, true, true, theUpdateTime, theRequestDetails);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ResourceTable updateEntity(final IBaseResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, Date theUpdateTime, RequestDetails theRequestDetails) {
|
||||
protected ResourceTable updateEntity(final IBaseResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
|
||||
boolean theUpdateVersion, Date theUpdateTime, RequestDetails theRequestDetails) {
|
||||
ourLog.info("Starting entity update");
|
||||
|
||||
/*
|
||||
* This should be the very first thing..
|
||||
|
@ -1284,7 +1110,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
String resourceType = myContext.getResourceDefinition(theResource).getName();
|
||||
if (isNotBlank(theEntity.getResourceType()) && !theEntity.getResourceType().equals(resourceType)) {
|
||||
throw new UnprocessableEntityException("Existing resource ID[" + theEntity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + theEntity.getResourceType() + "] - Cannot update with [" + resourceType + "]");
|
||||
throw new UnprocessableEntityException(
|
||||
"Existing resource ID[" + theEntity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + theEntity.getResourceType() + "] - Cannot update with [" + resourceType + "]");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1292,10 +1119,15 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
theEntity.setPublished(theUpdateTime);
|
||||
}
|
||||
|
||||
if (theUpdateHistory) {
|
||||
final ResourceHistoryTable historyEntry = theEntity.toHistory();
|
||||
myEntityManager.persist(historyEntry);
|
||||
}
|
||||
// if (theUpdateHistory) {
|
||||
// Long existingId = theEntity.getId();
|
||||
// ResourceHistoryTable existingHistory = existingId != null ? myResourceHistoryTableDao.findForIdAndVersion(existingId, theEntity.getVersion()) : null;
|
||||
// final ResourceHistoryTable historyEntry = theEntity.toHistory(existingHistory);
|
||||
//
|
||||
// ourLog.info("Saving history entry for update {}", historyEntry.getIdDt());
|
||||
// myResourceHistoryTableDao.save(historyEntry);
|
||||
//
|
||||
// }
|
||||
|
||||
if (theUpdateVersion) {
|
||||
theEntity.setVersion(theEntity.getVersion() + 1);
|
||||
|
@ -1384,9 +1216,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
|
||||
/*
|
||||
* Handle references within the resource that are match URLs, for example
|
||||
* references like "Patient?identifier=foo". These match URLs are resolved
|
||||
* and replaced with the ID of the matching resource.
|
||||
* Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the matching
|
||||
* resource.
|
||||
*/
|
||||
if (myConfig.isAllowInlineMatchUrlReferences()) {
|
||||
FhirTerser terser = getContext().newTerser();
|
||||
|
@ -1425,12 +1256,11 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
links = extractResourceLinks(theEntity, theResource);
|
||||
|
||||
/*
|
||||
* If the existing resource already has links and those match links we still want, use them instead of
|
||||
* removing them and re adding them
|
||||
* If the existing resource already has links and those match links we still want, use them instead of removing them and re adding them
|
||||
*/
|
||||
for (Iterator<ResourceLink> existingLinkIter = existingResourceLinks.iterator(); existingLinkIter.hasNext();) {
|
||||
ResourceLink nextExisting = existingLinkIter.next();
|
||||
|
@ -1479,6 +1309,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the resource itself
|
||||
*/
|
||||
if (theEntity.getId() == null) {
|
||||
myEntityManager.persist(theEntity);
|
||||
|
||||
|
@ -1494,6 +1327,19 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
postUpdate(theEntity, (T) theResource);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create history entry
|
||||
*/
|
||||
if (theUpdateVersion) {
|
||||
final ResourceHistoryTable historyEntry = theEntity.toHistory(null);
|
||||
|
||||
ourLog.info("Saving history entry {}", historyEntry.getIdDt());
|
||||
myResourceHistoryTableDao.save(historyEntry);
|
||||
}
|
||||
|
||||
/*
|
||||
* Indexing
|
||||
*/
|
||||
if (thePerformIndexing) {
|
||||
|
||||
for (ResourceIndexedSearchParamString next : paramsString) {
|
||||
|
@ -1569,6 +1415,11 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
return theEntity;
|
||||
}
|
||||
|
||||
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, Date theUpdateTime,
|
||||
RequestDetails theRequestDetails) {
|
||||
return updateEntity(theResource, entity, theUpdateHistory, theDeletedTimestampOrNull, true, true, theUpdateTime, theRequestDetails);
|
||||
}
|
||||
|
||||
protected void validateDeleteConflictsEmptyOrThrowException(List<DeleteConflict> theDeleteConflicts) {
|
||||
if (theDeleteConflicts.isEmpty()) {
|
||||
return;
|
||||
|
@ -1576,24 +1427,25 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
|
||||
IBaseOperationOutcome oo = OperationOutcomeUtil.newInstance(getContext());
|
||||
for (DeleteConflict next : theDeleteConflicts) {
|
||||
String msg = "Unable to delete " + next.getTargetId().toUnqualifiedVersionless().getValue() + " because at least one resource has a reference to this resource. First reference found was resource " + next.getTargetId().toUnqualifiedVersionless().getValue() + " in path "
|
||||
String msg = "Unable to delete " + next.getTargetId().toUnqualifiedVersionless().getValue()
|
||||
+ " because at least one resource has a reference to this resource. First reference found was resource " + next.getTargetId().toUnqualifiedVersionless().getValue() + " in path "
|
||||
+ next.getSourcePath();
|
||||
OperationOutcomeUtil.addIssue(getContext(), oo, OO_SEVERITY_ERROR, msg, null, "processing");
|
||||
}
|
||||
|
||||
throw new ResourceVersionConflictException("Delete failed because of constraint failure", oo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is invoked immediately before storing a new resource, or an update to an existing resource to allow
|
||||
* the DAO to ensure that it is valid for persistence. By default, checks for the "subsetted" tag and rejects
|
||||
* resources which have it. Subclasses should call the superclass implementation to preserve this check.
|
||||
* This method is invoked immediately before storing a new resource, or an update to an existing resource to allow the DAO to ensure that it is valid for persistence. By default, checks for the
|
||||
* "subsetted" tag and rejects resources which have it. Subclasses should call the superclass implementation to preserve this check.
|
||||
*
|
||||
* @param theResource
|
||||
* The resource that is about to be persisted
|
||||
* @param theEntityToSave
|
||||
* TODO
|
||||
* @param theRequestDetails TODO
|
||||
* @param theRequestDetails
|
||||
* TODO
|
||||
*/
|
||||
protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave, RequestDetails theRequestDetails) {
|
||||
Object tag = null;
|
||||
|
@ -1604,10 +1456,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
tag = tagList.getTag(Constants.TAG_SUBSETTED_SYSTEM, Constants.TAG_SUBSETTED_CODE);
|
||||
}
|
||||
} else {
|
||||
IAnyResource res = (IAnyResource)theResource;
|
||||
IAnyResource res = (IAnyResource) theResource;
|
||||
tag = res.getMeta().getTag(Constants.TAG_SUBSETTED_SYSTEM, Constants.TAG_SUBSETTED_CODE);
|
||||
}
|
||||
|
||||
|
||||
if (tag != null) {
|
||||
throw new UnprocessableEntityException("Resource contains the 'subsetted' tag, and must not be stored as it may contain a subset of available data");
|
||||
}
|
||||
|
@ -1691,22 +1543,22 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
b.append(characters.getData()).append(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new DataFormatException("Unable to convert DIV to string", e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private static List<BaseCodingDt> toBaseCodingList(List<IBaseCoding> theSecurityLabels) {
|
||||
ArrayList<BaseCodingDt> retVal = new ArrayList<BaseCodingDt>(theSecurityLabels.size());
|
||||
for (IBaseCoding next : theSecurityLabels) {
|
||||
retVal.add((BaseCodingDt) next);
|
||||
ArrayList<BaseCodingDt> retVal = new ArrayList<BaseCodingDt>(theSecurityLabels.size());
|
||||
for (IBaseCoding next : theSecurityLabels) {
|
||||
retVal.add((BaseCodingDt) next);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
static Long translateForcedIdToPid(IIdType theId, EntityManager entityManager) {
|
||||
if (isValidPid(theId)) {
|
||||
|
@ -1820,7 +1672,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
|
||||
public static void validateResourceType(BaseHasResource theEntity, String theResourceName) {
|
||||
if (!theResourceName.equals(theEntity.getResourceType())) {
|
||||
throw new ResourceNotFoundException("Resource with ID " + theEntity.getIdDt().getIdPart() + " exists but it is not of type " + theResourceName + ", found resource of type " + theEntity.getResourceType());
|
||||
throw new ResourceNotFoundException(
|
||||
"Resource with ID " + theEntity.getIdDt().getIdPart() + " exists but it is not of type " + theResourceName + ", found resource of type " + theEntity.getResourceType());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@ import java.util.Set;
|
|||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
|
@ -55,6 +54,7 @@ import ca.uhn.fhir.context.ConfigurationException;
|
|||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||
import ca.uhn.fhir.jpa.entity.BaseHasResource;
|
||||
|
@ -73,7 +73,6 @@ import ca.uhn.fhir.model.api.Include;
|
|||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
|
||||
|
@ -387,113 +386,15 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, getResourceName(), getContext(), theRequestDetails);
|
||||
notifyInterceptors(RestOperationTypeEnum.HISTORY_INSTANCE, requestDetails);
|
||||
|
||||
final InstantDt end = createHistoryToTimestamp();
|
||||
final String resourceType = getContext().getResourceDefinition(myResourceType).getName();
|
||||
StopWatch w = new StopWatch();
|
||||
|
||||
T currentTmp;
|
||||
try {
|
||||
BaseHasResource entity = readEntity(theId.toVersionless(), false);
|
||||
validateResourceType(entity);
|
||||
currentTmp = toResource(myResourceType, entity, true);
|
||||
Date lastUpdated;
|
||||
if (currentTmp instanceof IResource) {
|
||||
lastUpdated = ResourceMetadataKeyEnum.UPDATED.get((IResource) currentTmp).getValue();
|
||||
} else {
|
||||
lastUpdated = ((IAnyResource) currentTmp).getMeta().getLastUpdated();
|
||||
}
|
||||
if (lastUpdated.after(end.getValue())) {
|
||||
currentTmp = null;
|
||||
}
|
||||
} catch (ResourceNotFoundException e) {
|
||||
currentTmp = null;
|
||||
}
|
||||
|
||||
final T current = currentTmp;
|
||||
|
||||
StringBuilder B = new StringBuilder();
|
||||
B.append("SELECT count(h) FROM ResourceHistoryTable h ");
|
||||
B.append("WHERE h.myResourceId = :PID AND h.myResourceType = :RESTYPE");
|
||||
B.append(" AND h.myUpdated < :END");
|
||||
B.append((theSince != null ? " AND h.myUpdated >= :SINCE" : ""));
|
||||
String querySring = B.toString();
|
||||
|
||||
TypedQuery<Long> countQuery = myEntityManager.createQuery(querySring, Long.class);
|
||||
countQuery.setParameter("PID", translateForcedIdToPid(theId));
|
||||
countQuery.setParameter("RESTYPE", resourceType);
|
||||
countQuery.setParameter("END", end.getValue(), TemporalType.TIMESTAMP);
|
||||
if (theSince != null) {
|
||||
countQuery.setParameter("SINCE", theSince, TemporalType.TIMESTAMP);
|
||||
}
|
||||
int historyCount = countQuery.getSingleResult().intValue();
|
||||
|
||||
final int offset;
|
||||
final int count;
|
||||
if (current != null) {
|
||||
count = historyCount + 1;
|
||||
offset = 1;
|
||||
} else {
|
||||
offset = 0;
|
||||
count = historyCount;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
|
||||
return new IBundleProvider() {
|
||||
|
||||
@Override
|
||||
public InstantDt getPublished() {
|
||||
return end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> getResources(int theFromIndex, int theToIndex) {
|
||||
List<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||
if (theFromIndex == 0 && current != null) {
|
||||
retVal.add(current);
|
||||
}
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("SELECT h FROM ResourceHistoryTable h WHERE h.myResourceId = :PID AND h.myResourceType = :RESTYPE AND h.myUpdated < :END ");
|
||||
b.append((theSince != null ? " AND h.myUpdated >= :SINCE" : ""));
|
||||
b.append(" ORDER BY h.myUpdated DESC");
|
||||
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery(b.toString(), ResourceHistoryTable.class);
|
||||
q.setParameter("PID", translateForcedIdToPid(theId));
|
||||
q.setParameter("RESTYPE", resourceType);
|
||||
q.setParameter("END", end.getValue(), TemporalType.TIMESTAMP);
|
||||
if (theSince != null) {
|
||||
q.setParameter("SINCE", theSince, TemporalType.TIMESTAMP);
|
||||
}
|
||||
|
||||
int firstResult = Math.max(0, theFromIndex - offset);
|
||||
q.setFirstResult(firstResult);
|
||||
|
||||
int maxResults = (theToIndex - theFromIndex) + 1;
|
||||
q.setMaxResults(maxResults);
|
||||
|
||||
List<ResourceHistoryTable> results = q.getResultList();
|
||||
for (ResourceHistoryTable next : results) {
|
||||
if (retVal.size() == maxResults) {
|
||||
break;
|
||||
}
|
||||
retVal.add(toResource(myResourceType, next, true));
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer preferredPageSize() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return count;
|
||||
}
|
||||
};
|
||||
IIdType id = theId.withResourceType(myResourceName).toUnqualifiedVersionless();
|
||||
BaseHasResource entity = readEntity(id);
|
||||
|
||||
IBundleProvider retVal = super.history(myResourceName, entity.getId(), theSince);
|
||||
|
||||
ourLog.info("Processed history on {} in {}ms", id, w.getMillisAndRestart());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -508,6 +409,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private IResourceHistoryTableDao myResourceHistoryTableDao;
|
||||
|
||||
@Override
|
||||
public <MT extends IBaseMetaType> MT metaAddOperation(IIdType theResourceId, MT theMetaAdd, RequestDetails theRequestDetails) {
|
||||
// Notify interceptors
|
||||
|
@ -520,6 +424,25 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
throw new ResourceNotFoundException(theResourceId);
|
||||
}
|
||||
|
||||
ResourceTable latestVersion = readEntityLatestVersion(theResourceId);
|
||||
if (latestVersion.getVersion() != entity.getVersion()) {
|
||||
doMetaAdd(theMetaAdd, entity);
|
||||
} else {
|
||||
doMetaAdd(theMetaAdd, latestVersion);
|
||||
|
||||
// Also update history entry
|
||||
ResourceHistoryTable history = myResourceHistoryTableDao.findForIdAndVersion(entity.getId(), entity.getVersion());
|
||||
doMetaAdd(theMetaAdd, history);
|
||||
}
|
||||
|
||||
ourLog.info("Processed metaAddOperation on {} in {}ms", new Object[] { theResourceId, w.getMillisAndRestart() });
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
MT retVal = (MT) metaGetOperation(theMetaAdd.getClass(), theResourceId, theRequestDetails);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private <MT extends IBaseMetaType> void doMetaAdd(MT theMetaAdd, BaseHasResource entity) {
|
||||
List<TagDefinition> tags = toTagList(theMetaAdd);
|
||||
|
||||
//@formatter:off
|
||||
|
@ -546,11 +469,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
//@formatter:on
|
||||
|
||||
myEntityManager.merge(entity);
|
||||
ourLog.info("Processed metaAddOperation on {} in {}ms", new Object[] { theResourceId, w.getMillisAndRestart() });
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
MT retVal = (MT) metaGetOperation(theMetaAdd.getClass(), theResourceId, theRequestDetails);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
// @Override
|
||||
|
@ -647,6 +565,27 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
throw new ResourceNotFoundException(theResourceId);
|
||||
}
|
||||
|
||||
ResourceTable latestVersion = readEntityLatestVersion(theResourceId);
|
||||
if (latestVersion.getVersion() != entity.getVersion()) {
|
||||
doMetaDelete(theMetaDel, entity);
|
||||
} else {
|
||||
doMetaDelete(theMetaDel, latestVersion);
|
||||
|
||||
// Also update history entry
|
||||
ResourceHistoryTable history = myResourceHistoryTableDao.findForIdAndVersion(entity.getId(), entity.getVersion());
|
||||
doMetaDelete(theMetaDel, history);
|
||||
}
|
||||
|
||||
myEntityManager.flush();
|
||||
|
||||
ourLog.info("Processed metaDeleteOperation on {} in {}ms", new Object[] { theResourceId.getValue(), w.getMillisAndRestart() });
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
MT retVal = (MT) metaGetOperation(theMetaDel.getClass(), theResourceId, theRequestDetails);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private <MT extends IBaseMetaType> void doMetaDelete(MT theMetaDel, BaseHasResource entity) {
|
||||
List<TagDefinition> tags = toTagList(theMetaDel);
|
||||
|
||||
//@formatter:off
|
||||
|
@ -667,13 +606,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
|
||||
myEntityManager.merge(entity);
|
||||
myEntityManager.flush();
|
||||
|
||||
ourLog.info("Processed metaDeleteOperation on {} in {}ms", new Object[] { theResourceId.getValue(), w.getMillisAndRestart() });
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
MT retVal = (MT) metaGetOperation(theMetaDel.getClass(), theResourceId, theRequestDetails);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -3,7 +3,9 @@ package ca.uhn.fhir.jpa.dao;
|
|||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.entity.BaseHasResource;
|
||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum.ResourceMetadataKeySupportingAnyResource;
|
||||
|
||||
|
@ -33,30 +35,35 @@ public interface IDao {
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Long get(IResource theResource) {
|
||||
return (Long) theResource.getResourceMetadata().get(RESOURCE_PID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(IResource theResource, Long theObject) {
|
||||
theResource.getResourceMetadata().put(RESOURCE_PID, theObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long get(IAnyResource theResource) {
|
||||
return (Long) theResource.getUserData(RESOURCE_PID.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long get(IResource theResource) {
|
||||
return (Long) theResource.getResourceMetadata().get(RESOURCE_PID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(IAnyResource theResource, Long theObject) {
|
||||
theResource.setUserData(RESOURCE_PID.name(), theObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(IResource theResource, Long theObject) {
|
||||
theResource.getResourceMetadata().put(RESOURCE_PID, theObject);
|
||||
}
|
||||
};
|
||||
|
||||
FhirContext getContext();
|
||||
|
||||
/**
|
||||
* Populate all of the runtime dependencies that a bundle provider requires in order to work
|
||||
*/
|
||||
void injectDependenciesIntoBundleProvider(PersistedJpaBundleProvider theProvider);
|
||||
|
||||
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, boolean theForHistoryOperation);
|
||||
}
|
||||
|
|
|
@ -42,7 +42,6 @@ import java.util.Set;
|
|||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.Tuple;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
|
@ -62,9 +61,6 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
|
@ -93,8 +89,10 @@ import ca.uhn.fhir.jpa.entity.ResourceTag;
|
|||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.entity.SearchInclude;
|
||||
import ca.uhn.fhir.jpa.entity.SearchResult;
|
||||
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
|
||||
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
||||
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
|
||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
|
@ -943,21 +941,6 @@ public class SearchBuilder {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private static List<Predicate> createLastUpdatedPredicates(final DateRangeParam theLastUpdated, CriteriaBuilder builder, From<?, ResourceTable> from) {
|
||||
List<Predicate> lastUpdatedPredicates = new ArrayList<Predicate>();
|
||||
if (theLastUpdated != null) {
|
||||
if (theLastUpdated.getLowerBoundAsInstant() != null) {
|
||||
Predicate predicateLower = builder.greaterThanOrEqualTo(from.<Date> get("myUpdated"), theLastUpdated.getLowerBoundAsInstant());
|
||||
lastUpdatedPredicates.add(predicateLower);
|
||||
}
|
||||
if (theLastUpdated.getUpperBoundAsInstant() != null) {
|
||||
Predicate predicateUpper = builder.lessThanOrEqualTo(from.<Date> get("myUpdated"), theLastUpdated.getUpperBoundAsInstant());
|
||||
lastUpdatedPredicates.add(predicateUpper);
|
||||
}
|
||||
}
|
||||
return lastUpdatedPredicates;
|
||||
}
|
||||
|
||||
private Predicate createPredicateDate(CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamDate, ResourceIndexedSearchParamDate> theFrom, IQueryParameterType theParam) {
|
||||
Predicate p;
|
||||
if (theParam instanceof DateParam) {
|
||||
|
@ -1307,7 +1290,7 @@ public class SearchBuilder {
|
|||
mySearchEntity.setCreated(new Date());
|
||||
mySearchEntity.setTotalCount(-1);
|
||||
mySearchEntity.setPreferredPageSize(myParams.getCount());
|
||||
mySearchEntity.setEverythingMode(myParams.getEverythingMode());
|
||||
mySearchEntity.setSearchType(myParams.getEverythingMode() != null ? SearchTypeEnum.EVERYTHING : SearchTypeEnum.SEARCH);
|
||||
mySearchEntity.setLastUpdated(myParams.getLastUpdated());
|
||||
|
||||
for (Include next : myParams.getIncludes()) {
|
||||
|
@ -1329,7 +1312,7 @@ public class SearchBuilder {
|
|||
|
||||
private IBundleProvider doReturnProvider() {
|
||||
if (myParams.isPersistResults()) {
|
||||
return new BundleProviderPersisted(mySearchEntity.getUuid(), myPlatformTransactionManager, mySearchResultDao, myEntityManager, myContext, myCallingDao);
|
||||
return new PersistedJpaBundleProvider(mySearchEntity.getUuid(), myCallingDao);
|
||||
} else {
|
||||
if (myPids == null) {
|
||||
return new SimpleBundleProvider();
|
||||
|
@ -1381,22 +1364,6 @@ public class SearchBuilder {
|
|||
doSetPids(resultList);
|
||||
}
|
||||
|
||||
private static List<Long> filterResourceIdsByLastUpdated(EntityManager theEntityManager, final DateRangeParam theLastUpdated, Collection<Long> thePids) {
|
||||
CriteriaBuilder builder = theEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.select(from.get("myId").as(Long.class));
|
||||
|
||||
List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(theLastUpdated, builder, from);
|
||||
lastUpdatedPredicates.add(from.get("myId").as(Long.class).in(thePids));
|
||||
|
||||
cq.where(SearchBuilder.toArray(lastUpdatedPredicates));
|
||||
TypedQuery<Long> query = theEntityManager.createQuery(cq);
|
||||
|
||||
List<Long> resultList = query.getResultList();
|
||||
return resultList;
|
||||
}
|
||||
|
||||
private void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation) {
|
||||
EntityManager entityManager = myEntityManager;
|
||||
FhirContext context = myContext;
|
||||
|
@ -1405,179 +1372,6 @@ public class SearchBuilder {
|
|||
loadResourcesByPid(theIncludePids, theResourceListToPopulate, theRevIncludedPids, theForHistoryOperation, entityManager, context, dao);
|
||||
}
|
||||
|
||||
private static void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation,
|
||||
EntityManager entityManager, FhirContext context, IDao theDao) {
|
||||
if (theIncludePids.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<Long, Integer> position = new HashMap<Long, Integer>();
|
||||
for (Long next : theIncludePids) {
|
||||
position.put(next, theResourceListToPopulate.size());
|
||||
theResourceListToPopulate.add(null);
|
||||
}
|
||||
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ResourceTable> cq = builder.createQuery(ResourceTable.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.where(from.get("myId").in(theIncludePids));
|
||||
TypedQuery<ResourceTable> q = entityManager.createQuery(cq);
|
||||
|
||||
for (ResourceTable next : q.getResultList()) {
|
||||
Class<? extends IBaseResource> resourceType = context.getResourceDefinition(next.getResourceType()).getImplementingClass();
|
||||
IBaseResource resource = (IBaseResource) theDao.toResource(resourceType, next, theForHistoryOperation);
|
||||
Integer index = position.get(next.getId());
|
||||
if (index == null) {
|
||||
ourLog.warn("Got back unexpected resource PID {}", next.getId());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resource instanceof IResource) {
|
||||
if (theRevIncludedPids.contains(next.getId())) {
|
||||
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put((IResource) resource, BundleEntrySearchModeEnum.INCLUDE);
|
||||
} else {
|
||||
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put((IResource) resource, BundleEntrySearchModeEnum.MATCH);
|
||||
}
|
||||
} else {
|
||||
if (theRevIncludedPids.contains(next.getId())) {
|
||||
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put((IAnyResource) resource, BundleEntrySearchModeEnum.INCLUDE.getCode());
|
||||
} else {
|
||||
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put((IAnyResource) resource, BundleEntrySearchModeEnum.MATCH.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
theResourceListToPopulate.set(index, resource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* THIS SHOULD RETURN HASHSET and not jsut Set because we add to it later (so it can't be Collections.emptySet())
|
||||
*
|
||||
* @param theLastUpdated
|
||||
*/
|
||||
private static HashSet<Long> loadReverseIncludes(FhirContext theContext, EntityManager theEntityManager, Collection<Long> theMatches, Set<Include> theRevIncludes, boolean theReverseMode,
|
||||
DateRangeParam theLastUpdated) {
|
||||
if (theMatches.size() == 0) {
|
||||
return new HashSet<Long>();
|
||||
}
|
||||
if (theRevIncludes == null || theRevIncludes.isEmpty()) {
|
||||
return new HashSet<Long>();
|
||||
}
|
||||
String searchFieldName = theReverseMode ? "myTargetResourcePid" : "mySourceResourcePid";
|
||||
|
||||
Collection<Long> nextRoundMatches = theMatches;
|
||||
HashSet<Long> allAdded = new HashSet<Long>();
|
||||
HashSet<Long> original = new HashSet<Long>(theMatches);
|
||||
ArrayList<Include> includes = new ArrayList<Include>(theRevIncludes);
|
||||
|
||||
int roundCounts = 0;
|
||||
StopWatch w = new StopWatch();
|
||||
|
||||
boolean addedSomeThisRound;
|
||||
do {
|
||||
roundCounts++;
|
||||
|
||||
HashSet<Long> pidsToInclude = new HashSet<Long>();
|
||||
Set<Long> nextRoundOmit = new HashSet<Long>();
|
||||
|
||||
for (Iterator<Include> iter = includes.iterator(); iter.hasNext();) {
|
||||
Include nextInclude = iter.next();
|
||||
if (nextInclude.isRecurse() == false) {
|
||||
iter.remove();
|
||||
}
|
||||
|
||||
boolean matchAll = "*".equals(nextInclude.getValue());
|
||||
if (matchAll) {
|
||||
String sql;
|
||||
sql = "SELECT r FROM ResourceLink r WHERE r." + searchFieldName + " IN (:target_pids)";
|
||||
TypedQuery<ResourceLink> q = theEntityManager.createQuery(sql, ResourceLink.class);
|
||||
q.setParameter("target_pids", nextRoundMatches);
|
||||
List<ResourceLink> results = q.getResultList();
|
||||
for (ResourceLink resourceLink : results) {
|
||||
if (theReverseMode) {
|
||||
// if (theEverythingModeEnum.isEncounter()) {
|
||||
// if (resourceLink.getSourcePath().equals("Encounter.subject") ||
|
||||
// resourceLink.getSourcePath().equals("Encounter.patient")) {
|
||||
// nextRoundOmit.add(resourceLink.getSourceResourcePid());
|
||||
// }
|
||||
// }
|
||||
pidsToInclude.add(resourceLink.getSourceResourcePid());
|
||||
} else {
|
||||
pidsToInclude.add(resourceLink.getTargetResourcePid());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
List<String> paths;
|
||||
if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
|
||||
paths = Collections.singletonList(nextInclude.getValue());
|
||||
} else {
|
||||
String resType = nextInclude.getParamType();
|
||||
if (isBlank(resType)) {
|
||||
continue;
|
||||
}
|
||||
RuntimeResourceDefinition def = theContext.getResourceDefinition(resType);
|
||||
if (def == null) {
|
||||
ourLog.warn("Unknown resource type in include/revinclude=" + nextInclude.getValue());
|
||||
continue;
|
||||
}
|
||||
|
||||
String paramName = nextInclude.getParamName();
|
||||
RuntimeSearchParam param = isNotBlank(paramName) ? def.getSearchParam(paramName) : null;
|
||||
if (param == null) {
|
||||
ourLog.warn("Unknown param name in include/revinclude=" + nextInclude.getValue());
|
||||
continue;
|
||||
}
|
||||
|
||||
paths = param.getPathsSplit();
|
||||
}
|
||||
|
||||
String targetResourceType = defaultString(nextInclude.getParamTargetType(), null);
|
||||
for (String nextPath : paths) {
|
||||
String sql;
|
||||
if (targetResourceType != null) {
|
||||
sql = "SELECT r FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r." + searchFieldName + " IN (:target_pids) AND r.myTargetResourceType = :target_resource_type";
|
||||
} else {
|
||||
sql = "SELECT r FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r." + searchFieldName + " IN (:target_pids)";
|
||||
}
|
||||
TypedQuery<ResourceLink> q = theEntityManager.createQuery(sql, ResourceLink.class);
|
||||
q.setParameter("src_path", nextPath);
|
||||
q.setParameter("target_pids", nextRoundMatches);
|
||||
if (targetResourceType != null) {
|
||||
q.setParameter("target_resource_type", targetResourceType);
|
||||
}
|
||||
List<ResourceLink> results = q.getResultList();
|
||||
for (ResourceLink resourceLink : results) {
|
||||
if (theReverseMode) {
|
||||
pidsToInclude.add(resourceLink.getSourceResourcePid());
|
||||
} else {
|
||||
pidsToInclude.add(resourceLink.getTargetResourcePid());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theLastUpdated != null && (theLastUpdated.getLowerBoundAsInstant() != null || theLastUpdated.getUpperBoundAsInstant() != null)) {
|
||||
pidsToInclude = new HashSet<Long>(filterResourceIdsByLastUpdated(theEntityManager, theLastUpdated, pidsToInclude));
|
||||
}
|
||||
for (Long next : pidsToInclude) {
|
||||
if (original.contains(next) == false && allAdded.contains(next) == false) {
|
||||
theMatches.add(next);
|
||||
}
|
||||
}
|
||||
|
||||
pidsToInclude.removeAll(nextRoundOmit);
|
||||
|
||||
addedSomeThisRound = allAdded.addAll(pidsToInclude);
|
||||
nextRoundMatches = pidsToInclude;
|
||||
} while (includes.size() > 0 && nextRoundMatches.size() > 0 && addedSomeThisRound);
|
||||
|
||||
ourLog.info("Loaded {} {} in {} rounds and {} ms", new Object[] { allAdded.size(), theReverseMode ? "_revincludes" : "_includes", roundCounts, w.getMillisAndRestart() });
|
||||
|
||||
return allAdded;
|
||||
}
|
||||
|
||||
private void processSort(final SearchParameterMap theParams) {
|
||||
|
||||
// Set<Long> loadPids = theLoadPids;
|
||||
|
@ -1957,10 +1751,214 @@ public class SearchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private static List<Predicate> createLastUpdatedPredicates(final DateRangeParam theLastUpdated, CriteriaBuilder builder, From<?, ResourceTable> from) {
|
||||
List<Predicate> lastUpdatedPredicates = new ArrayList<Predicate>();
|
||||
if (theLastUpdated != null) {
|
||||
if (theLastUpdated.getLowerBoundAsInstant() != null) {
|
||||
Predicate predicateLower = builder.greaterThanOrEqualTo(from.<Date> get("myUpdated"), theLastUpdated.getLowerBoundAsInstant());
|
||||
lastUpdatedPredicates.add(predicateLower);
|
||||
}
|
||||
if (theLastUpdated.getUpperBoundAsInstant() != null) {
|
||||
Predicate predicateUpper = builder.lessThanOrEqualTo(from.<Date> get("myUpdated"), theLastUpdated.getUpperBoundAsInstant());
|
||||
lastUpdatedPredicates.add(predicateUpper);
|
||||
}
|
||||
}
|
||||
return lastUpdatedPredicates;
|
||||
}
|
||||
|
||||
private static String createLeftMatchLikeExpression(String likeExpression) {
|
||||
return likeExpression.replace("%", "[%]") + "%";
|
||||
}
|
||||
|
||||
private static List<Long> filterResourceIdsByLastUpdated(EntityManager theEntityManager, final DateRangeParam theLastUpdated, Collection<Long> thePids) {
|
||||
CriteriaBuilder builder = theEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.select(from.get("myId").as(Long.class));
|
||||
|
||||
List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(theLastUpdated, builder, from);
|
||||
lastUpdatedPredicates.add(from.get("myId").as(Long.class).in(thePids));
|
||||
|
||||
cq.where(SearchBuilder.toArray(lastUpdatedPredicates));
|
||||
TypedQuery<Long> query = theEntityManager.createQuery(cq);
|
||||
|
||||
List<Long> resultList = query.getResultList();
|
||||
return resultList;
|
||||
}
|
||||
|
||||
public static void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation,
|
||||
EntityManager entityManager, FhirContext context, IDao theDao) {
|
||||
if (theIncludePids.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<Long, Integer> position = new HashMap<Long, Integer>();
|
||||
for (Long next : theIncludePids) {
|
||||
position.put(next, theResourceListToPopulate.size());
|
||||
theResourceListToPopulate.add(null);
|
||||
}
|
||||
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ResourceTable> cq = builder.createQuery(ResourceTable.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.where(from.get("myId").in(theIncludePids));
|
||||
TypedQuery<ResourceTable> q = entityManager.createQuery(cq);
|
||||
|
||||
for (ResourceTable next : q.getResultList()) {
|
||||
Class<? extends IBaseResource> resourceType = context.getResourceDefinition(next.getResourceType()).getImplementingClass();
|
||||
IBaseResource resource = (IBaseResource) theDao.toResource(resourceType, next, theForHistoryOperation);
|
||||
Integer index = position.get(next.getId());
|
||||
if (index == null) {
|
||||
ourLog.warn("Got back unexpected resource PID {}", next.getId());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resource instanceof IResource) {
|
||||
if (theRevIncludedPids.contains(next.getId())) {
|
||||
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put((IResource) resource, BundleEntrySearchModeEnum.INCLUDE);
|
||||
} else {
|
||||
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put((IResource) resource, BundleEntrySearchModeEnum.MATCH);
|
||||
}
|
||||
} else {
|
||||
if (theRevIncludedPids.contains(next.getId())) {
|
||||
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put((IAnyResource) resource, BundleEntrySearchModeEnum.INCLUDE.getCode());
|
||||
} else {
|
||||
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put((IAnyResource) resource, BundleEntrySearchModeEnum.MATCH.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
theResourceListToPopulate.set(index, resource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* THIS SHOULD RETURN HASHSET and not jsut Set because we add to it later (so it can't be Collections.emptySet())
|
||||
*
|
||||
* @param theLastUpdated
|
||||
*/
|
||||
public static HashSet<Long> loadReverseIncludes(FhirContext theContext, EntityManager theEntityManager, Collection<Long> theMatches, Set<Include> theRevIncludes, boolean theReverseMode,
|
||||
DateRangeParam theLastUpdated) {
|
||||
if (theMatches.size() == 0) {
|
||||
return new HashSet<Long>();
|
||||
}
|
||||
if (theRevIncludes == null || theRevIncludes.isEmpty()) {
|
||||
return new HashSet<Long>();
|
||||
}
|
||||
String searchFieldName = theReverseMode ? "myTargetResourcePid" : "mySourceResourcePid";
|
||||
|
||||
Collection<Long> nextRoundMatches = theMatches;
|
||||
HashSet<Long> allAdded = new HashSet<Long>();
|
||||
HashSet<Long> original = new HashSet<Long>(theMatches);
|
||||
ArrayList<Include> includes = new ArrayList<Include>(theRevIncludes);
|
||||
|
||||
int roundCounts = 0;
|
||||
StopWatch w = new StopWatch();
|
||||
|
||||
boolean addedSomeThisRound;
|
||||
do {
|
||||
roundCounts++;
|
||||
|
||||
HashSet<Long> pidsToInclude = new HashSet<Long>();
|
||||
Set<Long> nextRoundOmit = new HashSet<Long>();
|
||||
|
||||
for (Iterator<Include> iter = includes.iterator(); iter.hasNext();) {
|
||||
Include nextInclude = iter.next();
|
||||
if (nextInclude.isRecurse() == false) {
|
||||
iter.remove();
|
||||
}
|
||||
|
||||
boolean matchAll = "*".equals(nextInclude.getValue());
|
||||
if (matchAll) {
|
||||
String sql;
|
||||
sql = "SELECT r FROM ResourceLink r WHERE r." + searchFieldName + " IN (:target_pids)";
|
||||
TypedQuery<ResourceLink> q = theEntityManager.createQuery(sql, ResourceLink.class);
|
||||
q.setParameter("target_pids", nextRoundMatches);
|
||||
List<ResourceLink> results = q.getResultList();
|
||||
for (ResourceLink resourceLink : results) {
|
||||
if (theReverseMode) {
|
||||
// if (theEverythingModeEnum.isEncounter()) {
|
||||
// if (resourceLink.getSourcePath().equals("Encounter.subject") ||
|
||||
// resourceLink.getSourcePath().equals("Encounter.patient")) {
|
||||
// nextRoundOmit.add(resourceLink.getSourceResourcePid());
|
||||
// }
|
||||
// }
|
||||
pidsToInclude.add(resourceLink.getSourceResourcePid());
|
||||
} else {
|
||||
pidsToInclude.add(resourceLink.getTargetResourcePid());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
List<String> paths;
|
||||
if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
|
||||
paths = Collections.singletonList(nextInclude.getValue());
|
||||
} else {
|
||||
String resType = nextInclude.getParamType();
|
||||
if (isBlank(resType)) {
|
||||
continue;
|
||||
}
|
||||
RuntimeResourceDefinition def = theContext.getResourceDefinition(resType);
|
||||
if (def == null) {
|
||||
ourLog.warn("Unknown resource type in include/revinclude=" + nextInclude.getValue());
|
||||
continue;
|
||||
}
|
||||
|
||||
String paramName = nextInclude.getParamName();
|
||||
RuntimeSearchParam param = isNotBlank(paramName) ? def.getSearchParam(paramName) : null;
|
||||
if (param == null) {
|
||||
ourLog.warn("Unknown param name in include/revinclude=" + nextInclude.getValue());
|
||||
continue;
|
||||
}
|
||||
|
||||
paths = param.getPathsSplit();
|
||||
}
|
||||
|
||||
String targetResourceType = defaultString(nextInclude.getParamTargetType(), null);
|
||||
for (String nextPath : paths) {
|
||||
String sql;
|
||||
if (targetResourceType != null) {
|
||||
sql = "SELECT r FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r." + searchFieldName + " IN (:target_pids) AND r.myTargetResourceType = :target_resource_type";
|
||||
} else {
|
||||
sql = "SELECT r FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r." + searchFieldName + " IN (:target_pids)";
|
||||
}
|
||||
TypedQuery<ResourceLink> q = theEntityManager.createQuery(sql, ResourceLink.class);
|
||||
q.setParameter("src_path", nextPath);
|
||||
q.setParameter("target_pids", nextRoundMatches);
|
||||
if (targetResourceType != null) {
|
||||
q.setParameter("target_resource_type", targetResourceType);
|
||||
}
|
||||
List<ResourceLink> results = q.getResultList();
|
||||
for (ResourceLink resourceLink : results) {
|
||||
if (theReverseMode) {
|
||||
pidsToInclude.add(resourceLink.getSourceResourcePid());
|
||||
} else {
|
||||
pidsToInclude.add(resourceLink.getTargetResourcePid());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theLastUpdated != null && (theLastUpdated.getLowerBoundAsInstant() != null || theLastUpdated.getUpperBoundAsInstant() != null)) {
|
||||
pidsToInclude = new HashSet<Long>(filterResourceIdsByLastUpdated(theEntityManager, theLastUpdated, pidsToInclude));
|
||||
}
|
||||
for (Long next : pidsToInclude) {
|
||||
if (original.contains(next) == false && allAdded.contains(next) == false) {
|
||||
theMatches.add(next);
|
||||
}
|
||||
}
|
||||
|
||||
pidsToInclude.removeAll(nextRoundOmit);
|
||||
|
||||
addedSomeThisRound = allAdded.addAll(pidsToInclude);
|
||||
nextRoundMatches = pidsToInclude;
|
||||
} while (includes.size() > 0 && nextRoundMatches.size() > 0 && addedSomeThisRound);
|
||||
|
||||
ourLog.info("Loaded {} {} in {} rounds and {} ms", new Object[] { allAdded.size(), theReverseMode ? "_revincludes" : "_includes", roundCounts, w.getMillisAndRestart() });
|
||||
|
||||
return allAdded;
|
||||
}
|
||||
|
||||
static Predicate[] toArray(List<Predicate> thePredicates) {
|
||||
return thePredicates.toArray(new Predicate[thePredicates.size()]);
|
||||
}
|
||||
|
@ -2021,117 +2019,4 @@ public class SearchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
public final static class BundleProviderPersisted implements IBundleProvider {
|
||||
|
||||
private String myUuid;
|
||||
private PlatformTransactionManager myPlatformTransactionManager;
|
||||
private ISearchResultDao mySearchResultDao;
|
||||
private Search mySearchEntity;
|
||||
private EntityManager myEntityManager;
|
||||
private FhirContext myContext;
|
||||
private IDao myDao;
|
||||
|
||||
public BundleProviderPersisted(String theSearchUuid, PlatformTransactionManager thePlatformTransactionManager, ISearchResultDao theSearchResultDao, EntityManager theEntityManager,
|
||||
FhirContext theContext, IDao theDao) {
|
||||
myUuid = theSearchUuid;
|
||||
myPlatformTransactionManager = thePlatformTransactionManager;
|
||||
mySearchResultDao = theSearchResultDao;
|
||||
myEntityManager = theEntityManager;
|
||||
myContext = theContext;
|
||||
myDao = theDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstantDt getPublished() {
|
||||
ensureSearchEntityLoaded();
|
||||
return new InstantDt(mySearchEntity.getCreated());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> getResources(final int theFromIndex, final int theToIndex) {
|
||||
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
|
||||
|
||||
return template.execute(new TransactionCallback<List<IBaseResource>>() {
|
||||
@Override
|
||||
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
|
||||
ensureSearchEntityLoaded();
|
||||
|
||||
int pageSize = theToIndex - theFromIndex;
|
||||
if (pageSize < 1) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
int pageIndex = theFromIndex / pageSize;
|
||||
|
||||
Pageable page = new PageRequest(pageIndex, pageSize);
|
||||
Page<SearchResult> search = mySearchResultDao.findWithSearchUuid(mySearchEntity, page);
|
||||
|
||||
List<Long> pidsSubList = new ArrayList<Long>();
|
||||
for (SearchResult next : search) {
|
||||
pidsSubList.add(next.getResourcePid());
|
||||
}
|
||||
|
||||
// Load includes
|
||||
pidsSubList = new ArrayList<Long>(pidsSubList);
|
||||
|
||||
Set<Long> revIncludedPids = new HashSet<Long>();
|
||||
if (mySearchEntity.getEverythingMode() == null) {
|
||||
revIncludedPids.addAll(loadReverseIncludes(myContext, myEntityManager, pidsSubList, mySearchEntity.toRevIncludesList(), true, mySearchEntity.getLastUpdated()));
|
||||
}
|
||||
revIncludedPids.addAll(loadReverseIncludes(myContext, myEntityManager, pidsSubList, mySearchEntity.toIncludesList(), false, mySearchEntity.getLastUpdated()));
|
||||
|
||||
// Execute the query and make sure we return distinct results
|
||||
List<IBaseResource> resources = new ArrayList<IBaseResource>();
|
||||
loadResourcesByPid(pidsSubList, resources, revIncludedPids, false, myEntityManager, myContext, myDao);
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer preferredPageSize() {
|
||||
ensureSearchEntityLoaded();
|
||||
return mySearchEntity.getPreferredPageSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
ensureSearchEntityLoaded();
|
||||
return mySearchEntity.getTotalCount();
|
||||
}
|
||||
|
||||
public String getSearchUuid() {
|
||||
return myUuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if the entity can't be found
|
||||
*/
|
||||
public boolean ensureSearchEntityLoaded() {
|
||||
if (mySearchEntity == null) {
|
||||
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
|
||||
template.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
||||
return template.execute(new TransactionCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInTransaction(TransactionStatus theStatus) {
|
||||
TypedQuery<Search> q = myEntityManager.createQuery("SELECT s FROM Search s WHERE s.myUuid = :uuid", Search.class);
|
||||
q.setParameter("uuid", myUuid);
|
||||
try {
|
||||
mySearchEntity = q.getSingleResult();
|
||||
|
||||
// Ensure includes are loaded
|
||||
mySearchEntity.getIncludes().size();
|
||||
return true;
|
||||
} catch (NoResultException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
package ca.uhn.fhir.jpa.dao.data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.jpa.repository.Temporal;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
||||
|
||||
public interface IResourceHistoryTableDao extends JpaRepository<ResourceHistoryTable, Long> {
|
||||
//@formatter:off
|
||||
|
||||
@Query("SELECT COUNT(*) FROM ResourceHistoryTable t WHERE t.myUpdated >= :cutoff")
|
||||
int countForAllResourceTypes(
|
||||
@Temporal(value=TemporalType.TIMESTAMP) @Param("cutoff") Date theCutoff
|
||||
);
|
||||
|
||||
@Query("SELECT COUNT(*) FROM ResourceHistoryTable t WHERE t.myResourceId = :id AND t.myUpdated >= :cutoff")
|
||||
int countForResourceInstance(
|
||||
@Param("id") Long theId,
|
||||
@Temporal(value=TemporalType.TIMESTAMP) @Param("cutoff") Date theCutoff
|
||||
);
|
||||
|
||||
@Query("SELECT COUNT(*) FROM ResourceHistoryTable t WHERE t.myResourceType = :type AND t.myUpdated >= :cutoff")
|
||||
int countForResourceType(
|
||||
@Param("type") String theType,
|
||||
@Temporal(value=TemporalType.TIMESTAMP) @Param("cutoff") Date theCutoff
|
||||
);
|
||||
|
||||
@Query("SELECT COUNT(*) FROM ResourceHistoryTable t")
|
||||
int countForAllResourceTypes(
|
||||
);
|
||||
|
||||
@Query("SELECT COUNT(*) FROM ResourceHistoryTable t WHERE t.myResourceId = :id")
|
||||
int countForResourceInstance(
|
||||
@Param("id") Long theId
|
||||
);
|
||||
|
||||
@Query("SELECT COUNT(*) FROM ResourceHistoryTable t WHERE t.myResourceType = :type")
|
||||
int countForResourceType(
|
||||
@Param("type") String theType
|
||||
);
|
||||
|
||||
@Query("SELECT t FROM ResourceHistoryTable t WHERE t.myUpdated >= :cutoff ORDER BY t.myUpdated DESC")
|
||||
List<ResourceHistoryTable> findForAllResourceTypes(
|
||||
@Temporal(value=TemporalType.TIMESTAMP) @Param("cutoff") Date theCutoff,
|
||||
Pageable thePageable);
|
||||
|
||||
@Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceId = :id AND t.myUpdated >= :cutoff ORDER BY t.myUpdated DESC")
|
||||
List<ResourceHistoryTable> findForResourceInstance(
|
||||
@Param("id") Long theId,
|
||||
@Temporal(value=TemporalType.TIMESTAMP) @Param("cutoff") Date theCutoff,
|
||||
Pageable thePageable);
|
||||
|
||||
@Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceType = :type AND t.myUpdated >= :cutoff ORDER BY t.myUpdated DESC")
|
||||
List<ResourceHistoryTable> findForResourceType(
|
||||
@Param("type") String theType,
|
||||
@Temporal(value=TemporalType.TIMESTAMP) @Param("cutoff") Date theCutoff,
|
||||
Pageable thePageable);
|
||||
|
||||
@Query("SELECT t FROM ResourceHistoryTable t ORDER BY t.myUpdated DESC")
|
||||
List<ResourceHistoryTable> findForAllResourceTypes(
|
||||
Pageable thePageable);
|
||||
|
||||
@Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceId = :id ORDER BY t.myUpdated DESC")
|
||||
List<ResourceHistoryTable> findForResourceInstance(
|
||||
@Param("id") Long theId,
|
||||
Pageable thePageable);
|
||||
|
||||
@Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceType = :type ORDER BY t.myUpdated DESC")
|
||||
List<ResourceHistoryTable> findForResourceType(
|
||||
@Param("type") String theType,
|
||||
Pageable thePageable);
|
||||
|
||||
@Query("SELECT t FROM ResourceHistoryTable t WHERE t.myResourceId = :id AND t.myResourceVersion = :version")
|
||||
ResourceHistoryTable findForIdAndVersion(@Param("id") long theId, @Param("version") long theVersion);
|
||||
|
||||
//@formatter:on
|
||||
}
|
|
@ -25,5 +25,5 @@ import org.springframework.data.jpa.repository.JpaRepository;
|
|||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
|
||||
public interface IResourceTableDao extends JpaRepository<ResourceTable, Long> {
|
||||
|
||||
// nothing yet
|
||||
}
|
||||
|
|
|
@ -29,9 +29,12 @@ import org.springframework.data.repository.query.Param;
|
|||
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
|
||||
public interface ISearchDao extends JpaRepository<Search, Long> {
|
||||
public interface ISearchDao extends JpaRepository<Search, Long> {
|
||||
|
||||
@Query("SELECT s FROM Search s WHERE s.myUuid = :uuid")
|
||||
public Search findByUuid(@Param("uuid") String theUuid);
|
||||
|
||||
@Query("SELECT s FROM Search s WHERE s.myCreated < :cutoff")
|
||||
public Collection<Search> findWhereCreatedBefore(@Param("cutoff") Date theCutoff);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -31,21 +31,25 @@ import javax.persistence.FetchType;
|
|||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.hibernate.annotations.Index;
|
||||
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
||||
//@formatter:off
|
||||
@Entity
|
||||
@Table(name = "HFJ_RES_VER", uniqueConstraints = { @UniqueConstraint(name = "IDX_RES_VER_ALL", columnNames = { "RES_ID", "RES_TYPE", "RES_VER" }) })
|
||||
@org.hibernate.annotations.Table(appliesTo = "HFJ_RES_VER", indexes = { @Index(name = "IDX_RES_VER_DATE", columnNames = { "RES_UPDATED" }) })
|
||||
@Table(name = "HFJ_RES_VER", uniqueConstraints = {
|
||||
@UniqueConstraint(name="IDX_RESVER_ID_VER", columnNames = { "RES_ID", "RES_VER" })
|
||||
}, indexes= {
|
||||
@Index(name="IDX_RESVER_TYPE_DATE", columnList="RES_TYPE,RES_UPDATED"),
|
||||
@Index(name="IDX_RESVER_ID_DATE", columnList="RES_ID,RES_UPDATED"),
|
||||
@Index(name="IDX_RESVER_DATE", columnList="RES_UPDATED")
|
||||
})
|
||||
//@formatter:on
|
||||
public class ResourceHistoryTable extends BaseHasResource implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
@ -68,13 +72,6 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
|
|||
@OneToMany(mappedBy = "myResourceHistory", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
|
||||
private Collection<ResourceHistoryTag> myTags;
|
||||
|
||||
/**
|
||||
* This field is only populated if this specific histor entry corresponds to
|
||||
* the most recent version of a resource
|
||||
*/
|
||||
@OneToOne(fetch=FetchType.LAZY, optional=true, mappedBy="myHistory")
|
||||
private ResourceTable myCorrespondsToVersion;
|
||||
|
||||
public void addTag(ResourceHistoryTag theTag) {
|
||||
for (ResourceHistoryTag next : getTags()) {
|
||||
if (next.getTag().equals(theTag)) {
|
||||
|
@ -89,7 +86,7 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
|
|||
tag.setResourceType(theTag.getResourceType());
|
||||
getTags().add(tag);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BaseTag addTag(TagDefinition theDef) {
|
||||
ResourceHistoryTag historyTag = new ResourceHistoryTag(this, theDef);
|
||||
|
@ -139,6 +136,10 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
|
|||
return false;
|
||||
}
|
||||
|
||||
public void setId(Long theId) {
|
||||
myId = theId;
|
||||
}
|
||||
|
||||
public void setResourceId(Long theResourceId) {
|
||||
myResourceId = theResourceId;
|
||||
}
|
||||
|
|
|
@ -35,9 +35,7 @@ import javax.persistence.GeneratedValue;
|
|||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
|
@ -152,10 +150,6 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
@Column(name = "SP_HAS_LINKS")
|
||||
private boolean myHasLinks;
|
||||
|
||||
@OneToOne(fetch=FetchType.LAZY, optional=true)
|
||||
@JoinColumn(name="HISTORY_VERSION_PID", referencedColumnName="PID", nullable=true)
|
||||
private ResourceHistoryTable myHistory;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@Column(name = "RES_ID")
|
||||
|
@ -191,10 +185,10 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
|
||||
@Column(name = "SP_DATE_PRESENT")
|
||||
private boolean myParamsDatePopulated;
|
||||
|
||||
|
||||
@OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
|
||||
private Collection<ResourceIndexedSearchParamNumber> myParamsNumber;
|
||||
|
||||
|
||||
@Column(name = "SP_NUMBER_PRESENT")
|
||||
private boolean myParamsNumberPopulated;
|
||||
|
||||
|
@ -534,8 +528,8 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
myVersion = theVersion;
|
||||
}
|
||||
|
||||
public ResourceHistoryTable toHistory() {
|
||||
ResourceHistoryTable retVal = new ResourceHistoryTable();
|
||||
public ResourceHistoryTable toHistory(ResourceHistoryTable theResourceHistoryTable) {
|
||||
ResourceHistoryTable retVal = theResourceHistoryTable != null ? theResourceHistoryTable : new ResourceHistoryTable();
|
||||
|
||||
retVal.setResourceId(myId);
|
||||
retVal.setResourceType(myResourceType);
|
||||
|
@ -550,6 +544,9 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
retVal.setDeleted(getDeleted());
|
||||
retVal.setForcedId(getForcedId());
|
||||
|
||||
retVal.getTags().clear();
|
||||
|
||||
retVal.setHasTags(isHasTags());
|
||||
if (isHasTags()) {
|
||||
for (ResourceTag next : getTags()) {
|
||||
retVal.addTag(next);
|
||||
|
|
|
@ -43,7 +43,6 @@ import javax.persistence.Temporal;
|
|||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
|
||||
|
@ -63,16 +62,12 @@ public class Search implements Serializable {
|
|||
@Column(name="CREATED", nullable=false, updatable=false)
|
||||
private Date myCreated;
|
||||
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Column(name="EVERYTHING_MODE", nullable=true)
|
||||
private EverythingModeEnum myEverythingMode;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator="SEQ_SEARCH")
|
||||
@SequenceGenerator(name="SEQ_SEARCH", sequenceName="SEQ_SEARCH")
|
||||
@Column(name = "PID")
|
||||
private Long myId;
|
||||
|
||||
|
||||
@OneToMany(mappedBy="mySearch")
|
||||
private Collection<SearchInclude> myIncludes;
|
||||
|
||||
|
@ -87,23 +82,29 @@ public class Search implements Serializable {
|
|||
@Column(name="PREFERRED_PAGE_SIZE", nullable=true)
|
||||
private Integer myPreferredPageSize;
|
||||
|
||||
@Column(name="RESOURCE_ID", nullable=true)
|
||||
private Long myResourceId;
|
||||
|
||||
@Column(name="RESOURCE_TYPE", length=200, nullable=true)
|
||||
private String myResourceType;
|
||||
|
||||
@OneToMany(mappedBy="mySearch")
|
||||
private Collection<SearchResult> myResults;
|
||||
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Column(name="SEARCH_TYPE", nullable=false)
|
||||
private SearchTypeEnum mySearchType;
|
||||
|
||||
@Column(name="TOTAL_COUNT")
|
||||
private int myTotalCount;
|
||||
|
||||
@Column(name="SEARCH_UUID", length=40, nullable=false, updatable=false)
|
||||
private String myUuid;
|
||||
|
||||
|
||||
public Date getCreated() {
|
||||
return myCreated;
|
||||
}
|
||||
|
||||
public EverythingModeEnum getEverythingMode() {
|
||||
return myEverythingMode;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return myId;
|
||||
}
|
||||
|
@ -114,6 +115,10 @@ public class Search implements Serializable {
|
|||
}
|
||||
return myIncludes;
|
||||
}
|
||||
|
||||
public Date getLastUpdatedHigh() {
|
||||
return myLastUpdatedHigh;
|
||||
}
|
||||
|
||||
public DateRangeParam getLastUpdated() {
|
||||
if (myLastUpdatedLow == null && myLastUpdatedHigh == null) {
|
||||
|
@ -127,6 +132,19 @@ public class Search implements Serializable {
|
|||
return myPreferredPageSize;
|
||||
}
|
||||
|
||||
public Long getResourceId() {
|
||||
return myResourceId;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return myResourceType;
|
||||
}
|
||||
|
||||
public SearchTypeEnum getSearchType() {
|
||||
return mySearchType;
|
||||
}
|
||||
|
||||
|
||||
public int getTotalCount() {
|
||||
return myTotalCount;
|
||||
}
|
||||
|
@ -134,15 +152,15 @@ public class Search implements Serializable {
|
|||
public String getUuid() {
|
||||
return myUuid;
|
||||
}
|
||||
|
||||
|
||||
public void setCreated(Date theCreated) {
|
||||
myCreated = theCreated;
|
||||
}
|
||||
|
||||
public void setEverythingMode(EverythingModeEnum theEverythingMode) {
|
||||
myEverythingMode = theEverythingMode;
|
||||
public void setLastUpdated(Date theLowerBound, Date theUpperBound) {
|
||||
myLastUpdatedLow = theLowerBound;
|
||||
myLastUpdatedHigh = theUpperBound;
|
||||
}
|
||||
|
||||
|
||||
public void setLastUpdated(DateRangeParam theLastUpdated) {
|
||||
if (theLastUpdated == null) {
|
||||
myLastUpdatedLow = null;
|
||||
|
@ -156,6 +174,19 @@ public class Search implements Serializable {
|
|||
public void setPreferredPageSize(Integer thePreferredPageSize) {
|
||||
myPreferredPageSize = thePreferredPageSize;
|
||||
}
|
||||
|
||||
public void setResourceId(Long theResourceId) {
|
||||
myResourceId = theResourceId;
|
||||
}
|
||||
|
||||
|
||||
public void setResourceType(String theResourceType) {
|
||||
myResourceType = theResourceType;
|
||||
}
|
||||
|
||||
public void setSearchType(SearchTypeEnum theSearchType) {
|
||||
mySearchType = theSearchType;
|
||||
}
|
||||
|
||||
public void setTotalCount(int theTotalCount) {
|
||||
myTotalCount = theTotalCount;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package ca.uhn.fhir.jpa.entity;
|
||||
|
||||
public enum SearchTypeEnum {
|
||||
|
||||
EVERYTHING,
|
||||
SEARCH,
|
||||
HISTORY,
|
||||
|
||||
}
|
|
@ -37,6 +37,8 @@ import javax.persistence.UniqueConstraint;
|
|||
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import ca.uhn.fhir.model.api.Tag;
|
||||
|
||||
|
@ -145,5 +147,17 @@ public class TagDefinition implements Serializable {
|
|||
b.append(myId);
|
||||
return b.toHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder retVal = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
retVal.append("id", myId);
|
||||
retVal.append("system", mySystem);
|
||||
retVal.append("code", myCode);
|
||||
retVal.append("display", myDisplay);
|
||||
return retVal.build();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@ import org.springframework.transaction.PlatformTransactionManager;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.IDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder.BundleProviderPersisted;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
|
@ -36,8 +34,7 @@ public class DatabaseBackedPagingProvider extends FifoMemoryPagingProvider {
|
|||
public synchronized IBundleProvider retrieveResultList(String theId) {
|
||||
IBundleProvider retVal = super.retrieveResultList(theId);
|
||||
if (retVal == null) {
|
||||
BundleProviderPersisted provider = new SearchBuilder.BundleProviderPersisted(theId, thePlatformTransactionManager, theSearchResultDao, theEntityManager,
|
||||
theContext, theDao);
|
||||
PersistedJpaBundleProvider provider = new PersistedJpaBundleProvider(theId, theDao);
|
||||
if (!provider.ensureSearchEntityLoaded()) {
|
||||
return null;
|
||||
}
|
||||
|
@ -48,8 +45,8 @@ public class DatabaseBackedPagingProvider extends FifoMemoryPagingProvider {
|
|||
|
||||
@Override
|
||||
public synchronized String storeResultList(IBundleProvider theList) {
|
||||
if (theList instanceof SearchBuilder.BundleProviderPersisted) {
|
||||
return ((BundleProviderPersisted)theList).getSearchUuid();
|
||||
if (theList instanceof PersistedJpaBundleProvider) {
|
||||
return ((PersistedJpaBundleProvider)theList).getSearchUuid();
|
||||
}
|
||||
return super.storeResultList(theList);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,246 @@
|
|||
package ca.uhn.fhir.jpa.search;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.IDao;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||
import ca.uhn.fhir.jpa.entity.BaseHasResource;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.entity.SearchResult;
|
||||
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
|
||||
public final class PersistedJpaBundleProvider implements IBundleProvider {
|
||||
|
||||
private FhirContext myContext;
|
||||
private IDao myDao;
|
||||
private EntityManager myEntityManager;
|
||||
private PlatformTransactionManager myPlatformTransactionManager;
|
||||
private IResourceHistoryTableDao myResourceHistoryTableDao;
|
||||
private ISearchDao mySearchDao;
|
||||
private Search mySearchEntity;
|
||||
private ISearchResultDao mySearchResultDao;
|
||||
private String myUuid;
|
||||
|
||||
public PersistedJpaBundleProvider(String theSearchUuid, IDao theDao) {
|
||||
myUuid = theSearchUuid;
|
||||
myDao = theDao;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private IResourceTableDao myResourceTableDao;
|
||||
|
||||
protected List<IBaseResource> doHistoryInTransaction(int theFromIndex, int theToIndex) {
|
||||
|
||||
Date cutoff = mySearchEntity.getLastUpdatedHigh();
|
||||
|
||||
Pageable pageable = toPage(theFromIndex, theToIndex);
|
||||
|
||||
List<ResourceHistoryTable> results;
|
||||
|
||||
if (cutoff != null) {
|
||||
if (mySearchEntity.getResourceType() == null) {
|
||||
results = myResourceHistoryTableDao.findForAllResourceTypes(cutoff, pageable);
|
||||
} else if (mySearchEntity.getResourceId() == null) {
|
||||
results = myResourceHistoryTableDao.findForResourceType(mySearchEntity.getResourceType(), cutoff, pageable);
|
||||
} else {
|
||||
results = myResourceHistoryTableDao.findForResourceInstance(mySearchEntity.getResourceId(), cutoff, pageable);
|
||||
}
|
||||
} else {
|
||||
if (mySearchEntity.getResourceType() == null) {
|
||||
results = myResourceHistoryTableDao.findForAllResourceTypes(pageable);
|
||||
} else if (mySearchEntity.getResourceId() == null) {
|
||||
results = myResourceHistoryTableDao.findForResourceType(mySearchEntity.getResourceType(), pageable);
|
||||
} else {
|
||||
results = myResourceHistoryTableDao.findForResourceInstance(mySearchEntity.getResourceId(), pageable);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||
for (ResourceHistoryTable next : results) {
|
||||
BaseHasResource resource;
|
||||
resource = next;
|
||||
|
||||
retVal.add(myDao.toResource(resource, true));
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
protected List<IBaseResource> doSearchOrEverythingInTransaction(final int theFromIndex, final int theToIndex) {
|
||||
|
||||
Pageable page = toPage(theFromIndex, theToIndex);
|
||||
if (page == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Page<SearchResult> search = mySearchResultDao.findWithSearchUuid(mySearchEntity, page);
|
||||
|
||||
List<Long> pidsSubList = new ArrayList<Long>();
|
||||
for (SearchResult next : search) {
|
||||
pidsSubList.add(next.getResourcePid());
|
||||
}
|
||||
|
||||
// Load includes
|
||||
pidsSubList = new ArrayList<Long>(pidsSubList);
|
||||
|
||||
Set<Long> revIncludedPids = new HashSet<Long>();
|
||||
if (mySearchEntity.getSearchType() == SearchTypeEnum.SEARCH) {
|
||||
revIncludedPids.addAll(SearchBuilder.loadReverseIncludes(myContext, myEntityManager, pidsSubList, mySearchEntity.toRevIncludesList(), true, mySearchEntity.getLastUpdated()));
|
||||
}
|
||||
revIncludedPids.addAll(SearchBuilder.loadReverseIncludes(myContext, myEntityManager, pidsSubList, mySearchEntity.toIncludesList(), false, mySearchEntity.getLastUpdated()));
|
||||
|
||||
// Execute the query and make sure we return distinct results
|
||||
List<IBaseResource> resources = new ArrayList<IBaseResource>();
|
||||
SearchBuilder.loadResourcesByPid(pidsSubList, resources, revIncludedPids, false, myEntityManager, myContext, myDao);
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false if the entity can't be found
|
||||
*/
|
||||
public boolean ensureSearchEntityLoaded() {
|
||||
if (mySearchEntity == null) {
|
||||
ensureDependenciesInjected();
|
||||
|
||||
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
|
||||
template.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
||||
return template.execute(new TransactionCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInTransaction(TransactionStatus theStatus) {
|
||||
try {
|
||||
mySearchEntity = mySearchDao.findByUuid(myUuid);
|
||||
|
||||
if (mySearchEntity == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the includes now so that they are available outside of this transaction
|
||||
mySearchEntity.getIncludes().size();
|
||||
|
||||
return true;
|
||||
} catch (NoResultException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ensureDependenciesInjected() {
|
||||
if (myPlatformTransactionManager == null) {
|
||||
myDao.injectDependenciesIntoBundleProvider(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstantDt getPublished() {
|
||||
ensureSearchEntityLoaded();
|
||||
return new InstantDt(mySearchEntity.getCreated());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> getResources(final int theFromIndex, final int theToIndex) {
|
||||
ensureDependenciesInjected();
|
||||
|
||||
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
|
||||
|
||||
return template.execute(new TransactionCallback<List<IBaseResource>>() {
|
||||
@Override
|
||||
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
|
||||
ensureSearchEntityLoaded();
|
||||
|
||||
switch (mySearchEntity.getSearchType()) {
|
||||
case HISTORY:
|
||||
return doHistoryInTransaction(theFromIndex, theToIndex);
|
||||
case SEARCH:
|
||||
case EVERYTHING:
|
||||
default:
|
||||
return doSearchOrEverythingInTransaction(theFromIndex, theToIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public String getSearchUuid() {
|
||||
return myUuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer preferredPageSize() {
|
||||
ensureSearchEntityLoaded();
|
||||
return mySearchEntity.getPreferredPageSize();
|
||||
}
|
||||
|
||||
public void setContext(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
public void setEntityManager(EntityManager theEntityManager) {
|
||||
myEntityManager = theEntityManager;
|
||||
}
|
||||
|
||||
|
||||
public void setPlatformTransactionManager(PlatformTransactionManager thePlatformTransactionManager) {
|
||||
myPlatformTransactionManager = thePlatformTransactionManager;
|
||||
}
|
||||
|
||||
public void setResourceHistoryTableDao(IResourceHistoryTableDao theResourceHistoryTableDao) {
|
||||
myResourceHistoryTableDao = theResourceHistoryTableDao;
|
||||
}
|
||||
|
||||
public void setSearchDao(ISearchDao theSearchDao) {
|
||||
mySearchDao = theSearchDao;
|
||||
}
|
||||
|
||||
public void setSearchResultDao(ISearchResultDao theSearchResultDao) {
|
||||
mySearchResultDao = theSearchResultDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
ensureSearchEntityLoaded();
|
||||
return mySearchEntity.getTotalCount();
|
||||
}
|
||||
|
||||
private Pageable toPage(int theFromIndex, int theToIndex) {
|
||||
int pageSize = theToIndex - theFromIndex;
|
||||
if (pageSize < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int pageIndex = theFromIndex / pageSize;
|
||||
|
||||
Pageable page = new PageRequest(pageIndex, pageSize);
|
||||
return page;
|
||||
}
|
||||
}
|
|
@ -55,7 +55,7 @@ public class TestDstu1Config extends BaseJavaConfigDstu1 {
|
|||
|
||||
private Properties jpaProperties() {
|
||||
Properties extraProperties = new Properties();
|
||||
extraProperties.put("hibernate.format_sql", "true");
|
||||
extraProperties.put("hibernate.format_sql", "false");
|
||||
extraProperties.put("hibernate.show_sql", "false");
|
||||
extraProperties.put("hibernate.hbm2ddl.auto", "update");
|
||||
return extraProperties;
|
||||
|
|
|
@ -19,6 +19,8 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.AfterClass;
|
||||
|
@ -26,6 +28,7 @@ import org.junit.Before;
|
|||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.config.TestDstu1Config;
|
||||
|
@ -59,6 +62,14 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
|
|||
private static IFhirResourceDao<Patient> ourPatientDao;
|
||||
private static IFhirSystemDao<List<IResource>, MetaDt> ourSystemDao;
|
||||
private RequestDetails myRequestDetails;
|
||||
private static EntityManager ourEntityManager;
|
||||
private static PlatformTransactionManager ourTxManager;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myRequestDetails = mock(RequestDetails.class);
|
||||
super.purgeDatabase(ourEntityManager, ourTxManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceCounts() {
|
||||
|
@ -133,7 +144,7 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
|
|||
assertEquals(1, values.size());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testPersistWithSimpleLink() {
|
||||
Patient patient = new Patient();
|
||||
|
@ -187,11 +198,6 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
|
|||
assertEquals(obsVersion2, "2");
|
||||
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myRequestDetails = mock(RequestDetails.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersistWithUnknownId() {
|
||||
|
@ -484,6 +490,8 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
|
|||
ourObservationDao = ourCtx.getBean("myObservationDaoDstu1", IFhirResourceDao.class);
|
||||
ourLocationDao = ourCtx.getBean("myLocationDaoDstu1", IFhirResourceDao.class);
|
||||
ourSystemDao = ourCtx.getBean("mySystemDaoDstu1", IFhirSystemDao.class);
|
||||
ourEntityManager = ourCtx.getBean(EntityManager.class);
|
||||
ourTxManager = ourCtx.getBean(PlatformTransactionManager.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -112,6 +112,28 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2Test.class);
|
||||
|
||||
private void assertGone(IIdType theId) {
|
||||
try {
|
||||
assertNotGone(theId);
|
||||
fail();
|
||||
} catch (ResourceGoneException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets called from assertGone too! Careful about exceptions...
|
||||
*/
|
||||
private void assertNotGone(IIdType theId) {
|
||||
if ("Patient".equals(theId.getResourceType())) {
|
||||
myPatientDao.read(theId, new ServletRequestDetails());
|
||||
} else if ("Organization".equals(theId.getResourceType())){
|
||||
myOrganizationDao.read(theId, new ServletRequestDetails());
|
||||
} else {
|
||||
fail("No type");
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> extractNames(IBundleProvider theSearch) {
|
||||
ArrayList<String> retVal = new ArrayList<String>();
|
||||
for (IBaseResource next : theSearch.getResources(0, theSearch.size())) {
|
||||
|
@ -121,6 +143,14 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private String log(IBundleProvider theHistory) {
|
||||
StringBuilder b =new StringBuilder(theHistory.size() + " results: ");
|
||||
for (IBaseResource next : theHistory.getResources(0, theHistory.size())) {
|
||||
b.append("\n ").append(next.getIdElement().toUnqualified().getValue());
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private void sort(TagList thePublished) {
|
||||
ArrayList<Tag> tags = new ArrayList<Tag>(thePublished);
|
||||
Collections.sort(tags, new Comparator<Tag>() {
|
||||
|
@ -743,7 +773,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
assertThat(found, empty());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeleteResource() {
|
||||
int initialHistory = myPatientDao.history(null, new ServletRequestDetails()).size();
|
||||
|
@ -806,6 +837,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
assertEquals(0, patients.size());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeleteThenUndelete() {
|
||||
|
@ -840,27 +872,44 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
assertEquals(id2, gotId);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeleteWithMatchUrlChainedString() {
|
||||
String methodName = "testDeleteWithMatchUrlChainedString";
|
||||
|
||||
Organization org = new Organization();
|
||||
org.setName(methodName);
|
||||
IIdType orgId = myOrganizationDao.create(org, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
public void testDeleteWithMatchUrl() {
|
||||
String methodName = "testDeleteWithMatchUrl";
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
p.getManagingOrganization().setReference(orgId);
|
||||
IIdType id = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
IIdType id = myPatientDao.create(p, new ServletRequestDetails()).getId();
|
||||
ourLog.info("Created patient, got it: {}", id);
|
||||
|
||||
myPatientDao.deleteByUrl("Patient?organization.name=" + methodName, new ServletRequestDetails());
|
||||
Bundle request = new Bundle();
|
||||
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.DELETE).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
|
||||
|
||||
myPatientDao.deleteByUrl("Patient?identifier=urn%3Asystem%7C" + methodName, new ServletRequestDetails());
|
||||
|
||||
try {
|
||||
myPatientDao.read(id.toVersionless(), new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (ResourceGoneException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
try {
|
||||
myPatientDao.read(new IdDt("Patient/" + methodName), new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
IBundleProvider history = myPatientDao.history(id, null, new ServletRequestDetails());
|
||||
assertEquals(2, history.size());
|
||||
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(0, 1).get(0)));
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(0, 1).get(0)).getValue());
|
||||
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(1, 2).get(0)));
|
||||
|
||||
assertGone(id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeleteWithMatchUrlChainedIdentifier() {
|
||||
String methodName = "testDeleteWithMatchUrlChainedIdentifer";
|
||||
|
@ -886,7 +935,74 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
assertGone(orgId);
|
||||
|
||||
}
|
||||
@Test
|
||||
public void testDeleteWithMatchUrlChainedProfile() {
|
||||
String methodName = "testDeleteWithMatchUrlChainedProfile";
|
||||
|
||||
List<IdDt> profileList = new ArrayList<IdDt>();
|
||||
profileList.add(new IdDt("http://foo"));
|
||||
|
||||
Organization org = new Organization();
|
||||
ResourceMetadataKeyEnum.PROFILES.put(org, profileList);
|
||||
org.setName(methodName);
|
||||
|
||||
IIdType orgId = myOrganizationDao.create(org, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
p.getManagingOrganization().setReference(orgId);
|
||||
IIdType id = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
ourLog.info("Created patient, got it: {}", id);
|
||||
|
||||
myPatientDao.deleteByUrl("Patient?organization._profile=http://foo", new ServletRequestDetails());
|
||||
assertGone(id);
|
||||
|
||||
myOrganizationDao.deleteByUrl("Organization?_profile=http://foo", new ServletRequestDetails());
|
||||
try {
|
||||
myOrganizationDao.read(orgId, new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (ResourceGoneException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
try {
|
||||
myPatientDao.deleteByUrl("Patient?organization._profile.identifier=http://foo", new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Invalid parameter chain: organization._profile.identifier", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
myOrganizationDao.deleteByUrl("Organization?_profile.identifier=http://foo", new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Invalid parameter chain: _profile.identifier", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeleteWithMatchUrlChainedString() {
|
||||
String methodName = "testDeleteWithMatchUrlChainedString";
|
||||
|
||||
Organization org = new Organization();
|
||||
org.setName(methodName);
|
||||
IIdType orgId = myOrganizationDao.create(org, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
p.getManagingOrganization().setReference(orgId);
|
||||
IIdType id = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
ourLog.info("Created patient, got it: {}", id);
|
||||
|
||||
myPatientDao.deleteByUrl("Patient?organization.name=" + methodName, new ServletRequestDetails());
|
||||
|
||||
assertGone(id);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWithMatchUrlChainedTag() {
|
||||
|
@ -935,7 +1051,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeleteWithMatchUrlQualifierMissing() {
|
||||
String methodName = "testDeleteWithMatchUrlChainedProfile";
|
||||
|
@ -997,113 +1112,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
assertGone(org2Id);
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets called from assertGone too! Careful about exceptions...
|
||||
*/
|
||||
private void assertNotGone(IIdType theId) {
|
||||
if ("Patient".equals(theId.getResourceType())) {
|
||||
myPatientDao.read(theId, new ServletRequestDetails());
|
||||
} else if ("Organization".equals(theId.getResourceType())){
|
||||
myOrganizationDao.read(theId, new ServletRequestDetails());
|
||||
} else {
|
||||
fail("No type");
|
||||
}
|
||||
}
|
||||
private void assertGone(IIdType theId) {
|
||||
try {
|
||||
assertNotGone(theId);
|
||||
fail();
|
||||
} catch (ResourceGoneException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeleteWithMatchUrlChainedProfile() {
|
||||
String methodName = "testDeleteWithMatchUrlChainedProfile";
|
||||
|
||||
List<IdDt> profileList = new ArrayList<IdDt>();
|
||||
profileList.add(new IdDt("http://foo"));
|
||||
|
||||
Organization org = new Organization();
|
||||
ResourceMetadataKeyEnum.PROFILES.put(org, profileList);
|
||||
org.setName(methodName);
|
||||
|
||||
IIdType orgId = myOrganizationDao.create(org, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
p.getManagingOrganization().setReference(orgId);
|
||||
IIdType id = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
ourLog.info("Created patient, got it: {}", id);
|
||||
|
||||
myPatientDao.deleteByUrl("Patient?organization._profile=http://foo", new ServletRequestDetails());
|
||||
assertGone(id);
|
||||
|
||||
myOrganizationDao.deleteByUrl("Organization?_profile=http://foo", new ServletRequestDetails());
|
||||
try {
|
||||
myOrganizationDao.read(orgId, new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (ResourceGoneException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
try {
|
||||
myPatientDao.deleteByUrl("Patient?organization._profile.identifier=http://foo", new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Invalid parameter chain: organization._profile.identifier", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
myOrganizationDao.deleteByUrl("Organization?_profile.identifier=http://foo", new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Invalid parameter chain: _profile.identifier", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWithMatchUrl() {
|
||||
String methodName = "testDeleteWithMatchUrl";
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
IIdType id = myPatientDao.create(p, new ServletRequestDetails()).getId();
|
||||
ourLog.info("Created patient, got it: {}", id);
|
||||
|
||||
Bundle request = new Bundle();
|
||||
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.DELETE).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
|
||||
|
||||
myPatientDao.deleteByUrl("Patient?identifier=urn%3Asystem%7C" + methodName, new ServletRequestDetails());
|
||||
|
||||
try {
|
||||
myPatientDao.read(id.toVersionless(), new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (ResourceGoneException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
try {
|
||||
myPatientDao.read(new IdDt("Patient/" + methodName), new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
IBundleProvider history = myPatientDao.history(id, null, new ServletRequestDetails());
|
||||
assertEquals(2, history.size());
|
||||
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(0, 0).get(0)));
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(0, 0).get(0)).getValue());
|
||||
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(1, 1).get(0)));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryByForcedId() {
|
||||
IIdType idv1;
|
||||
|
@ -1287,14 +1295,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
private String log(IBundleProvider theHistory) {
|
||||
StringBuilder b =new StringBuilder(theHistory.size() + " results: ");
|
||||
for (IBaseResource next : theHistory.getResources(0, theHistory.size())) {
|
||||
b.append("\n ").append(next.getIdElement().toUnqualified().getValue());
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryWithDeletedResource() throws Exception {
|
||||
String methodName = "testHistoryWithDeletedResource";
|
||||
|
@ -2263,79 +2263,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortByLastUpdated() {
|
||||
String methodName = "testSortByLastUpdated";
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system1").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id1 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system2").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id2 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system3").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id3 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system4").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id4 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameterMap pm;
|
||||
List<IIdType> actual;
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.DESC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id4, id3, id2, id1));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam(null, methodName));
|
||||
pm.setSort(new SortSpec(Patient.SP_NAME).setChain(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.DESC)));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id4, id3, id2, id1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortNoMatches() {
|
||||
String methodName = "testSortNoMatches";
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
IIdType id1 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameterMap map;
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.add(BaseResource.SP_RES_ID, new StringParam(id1.getIdPart()));
|
||||
map.setLastUpdated(new DateRangeParam("2001", "2003"));
|
||||
map.setSort(new SortSpec(Constants.PARAM_LASTUPDATED));
|
||||
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.add(BaseResource.SP_RES_ID, new StringParam(id1.getIdPart()));
|
||||
map.setLastUpdated(new DateRangeParam("2001", "2003"));
|
||||
map.setSort(new SortSpec(Patient.SP_NAME));
|
||||
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), empty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortById() {
|
||||
String methodName = "testSortBTyId";
|
||||
|
@ -2387,6 +2314,55 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
assertThat(actual, contains(id4, id3, id2, id1, idMethodName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortByLastUpdated() {
|
||||
String methodName = "testSortByLastUpdated";
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system1").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id1 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system2").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id2 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system3").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id3 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system4").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id4 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameterMap pm;
|
||||
List<IIdType> actual;
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.DESC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id4, id3, id2, id1));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam(null, methodName));
|
||||
pm.setSort(new SortSpec(Patient.SP_NAME).setChain(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.DESC)));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id4, id3, id2, id1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortByNumber() {
|
||||
String methodName = "testSortByNumber";
|
||||
|
@ -2714,6 +2690,30 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortNoMatches() {
|
||||
String methodName = "testSortNoMatches";
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
IIdType id1 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameterMap map;
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.add(BaseResource.SP_RES_ID, new StringParam(id1.getIdPart()));
|
||||
map.setLastUpdated(new DateRangeParam("2001", "2003"));
|
||||
map.setSort(new SortSpec(Constants.PARAM_LASTUPDATED));
|
||||
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.add(BaseResource.SP_RES_ID, new StringParam(id1.getIdPart()));
|
||||
map.setLastUpdated(new DateRangeParam("2001", "2003"));
|
||||
map.setSort(new SortSpec(Patient.SP_NAME));
|
||||
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), empty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoreUnversionedResources() {
|
||||
Organization o1 = new Organization();
|
||||
|
|
|
@ -745,9 +745,9 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
|
|||
IBundleProvider history = myPatientDao.history(id, null, new ServletRequestDetails());
|
||||
assertEquals(2, history.size());
|
||||
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(0, 0).get(0)));
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(0, 0).get(0)).getValue());
|
||||
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(1, 1).get(0)));
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(0, 1).get(0)));
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(0, 1).get(0)).getValue());
|
||||
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(1, 2).get(0)));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ import ca.uhn.fhir.model.api.Include;
|
|||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
|
@ -109,6 +110,28 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3Test.class);
|
||||
|
||||
private void assertGone(IIdType theId) {
|
||||
try {
|
||||
assertNotGone(theId);
|
||||
fail();
|
||||
} catch (ResourceGoneException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets called from assertGone too! Careful about exceptions...
|
||||
*/
|
||||
private void assertNotGone(IIdType theId) {
|
||||
if ("Patient".equals(theId.getResourceType())) {
|
||||
myPatientDao.read(theId, new ServletRequestDetails());
|
||||
} else if ("Organization".equals(theId.getResourceType())) {
|
||||
myOrganizationDao.read(theId, new ServletRequestDetails());
|
||||
} else {
|
||||
fail("No type");
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> extractNames(IBundleProvider theSearch) {
|
||||
ArrayList<String> retVal = new ArrayList<String>();
|
||||
for (IBaseResource next : theSearch.getResources(0, theSearch.size())) {
|
||||
|
@ -118,6 +141,12 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private CodeableConcept newCodeableConcept(String theSystem, String theCode) {
|
||||
CodeableConcept retVal = new CodeableConcept();
|
||||
retVal.addCoding().setSystem(theSystem).setCode(theCode);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void sort(ArrayList<Coding> thePublished) {
|
||||
ArrayList<Coding> tags = new ArrayList<Coding>(thePublished);
|
||||
Collections.sort(tags, new Comparator<Coding>() {
|
||||
|
@ -202,6 +231,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testChoiceParamDate() {
|
||||
Observation o2 = new Observation();
|
||||
|
@ -215,7 +245,8 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
assertEquals(id2, found.getResources(0, 1).get(0).getIdElement());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testChoiceParamDateAlt() {
|
||||
Observation o2 = new Observation();
|
||||
|
@ -272,47 +303,6 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testChoiceParamQuantityPrecision() {
|
||||
Observation o3 = new Observation();
|
||||
o3.getCode().addCoding().setSystem("foo").setCode("testChoiceParam03");
|
||||
o3.setValue(new Quantity(null, 123.01, "foo", "bar", "bar"));
|
||||
IIdType id3 = myObservationDao.create(o3, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
{
|
||||
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123", "foo", "bar"));
|
||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||
assertThat(list, containsInAnyOrder(id3));
|
||||
}
|
||||
{
|
||||
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.0", "foo", "bar"));
|
||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||
assertThat(list, containsInAnyOrder(id3));
|
||||
}
|
||||
{
|
||||
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.01", "foo", "bar"));
|
||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||
assertThat(list, containsInAnyOrder(id3));
|
||||
}
|
||||
{
|
||||
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.010", "foo", "bar"));
|
||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||
assertThat(list, containsInAnyOrder(id3));
|
||||
}
|
||||
{
|
||||
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.02", "foo", "bar"));
|
||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||
assertThat(list, containsInAnyOrder());
|
||||
}
|
||||
{
|
||||
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.001", "foo", "bar"));
|
||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||
assertThat(list, containsInAnyOrder());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testChoiceParamQuantity() {
|
||||
Observation o3 = new Observation();
|
||||
|
@ -372,6 +362,45 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChoiceParamQuantityPrecision() {
|
||||
Observation o3 = new Observation();
|
||||
o3.getCode().addCoding().setSystem("foo").setCode("testChoiceParam03");
|
||||
o3.setValue(new Quantity(null, 123.01, "foo", "bar", "bar"));
|
||||
IIdType id3 = myObservationDao.create(o3, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
{
|
||||
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123", "foo", "bar"));
|
||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||
assertThat(list, containsInAnyOrder(id3));
|
||||
}
|
||||
{
|
||||
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.0", "foo", "bar"));
|
||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||
assertThat(list, containsInAnyOrder(id3));
|
||||
}
|
||||
{
|
||||
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.01", "foo", "bar"));
|
||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||
assertThat(list, containsInAnyOrder(id3));
|
||||
}
|
||||
{
|
||||
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.010", "foo", "bar"));
|
||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||
assertThat(list, containsInAnyOrder(id3));
|
||||
}
|
||||
{
|
||||
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.02", "foo", "bar"));
|
||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||
assertThat(list, containsInAnyOrder());
|
||||
}
|
||||
{
|
||||
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.001", "foo", "bar"));
|
||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||
assertThat(list, containsInAnyOrder());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChoiceParamString() {
|
||||
|
||||
|
@ -387,58 +416,6 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateOperationOutcome() {
|
||||
/*
|
||||
* If any of this ever fails, it means that one of the OperationOutcome issue severity codes has changed code value across versions. We store the string as a constant, so something will need to
|
||||
* be fixed.
|
||||
*/
|
||||
assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.ERROR.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
|
||||
assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.ERROR.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
|
||||
assertEquals(org.hl7.fhir.dstu3.model.OperationOutcome.IssueSeverity.ERROR.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
|
||||
assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.INFORMATION.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_INFO);
|
||||
assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.INFORMATION.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_INFO);
|
||||
assertEquals(org.hl7.fhir.dstu3.model.OperationOutcome.IssueSeverity.INFORMATION.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_INFO);
|
||||
assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.WARNING.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
|
||||
assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.WARNING.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
|
||||
assertEquals(org.hl7.fhir.dstu3.model.OperationOutcome.IssueSeverity.WARNING.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateOperationOutcomeError() {
|
||||
FhirResourceDaoDstu3<Bundle> dao = new FhirResourceDaoDstu3<Bundle>();
|
||||
OperationOutcome oo = (OperationOutcome) dao.createErrorOperationOutcome("my message", "incomplete");
|
||||
assertEquals(IssueSeverity.ERROR.toCode(), oo.getIssue().get(0).getSeverity().toCode());
|
||||
assertEquals("my message", oo.getIssue().get(0).getDiagnostics());
|
||||
assertEquals(IssueType.INCOMPLETE, oo.getIssue().get(0).getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateOperationOutcomeInfo() {
|
||||
FhirResourceDaoDstu3<Bundle> dao = new FhirResourceDaoDstu3<Bundle>();
|
||||
OperationOutcome oo = (OperationOutcome) dao.createInfoOperationOutcome("my message");
|
||||
assertEquals(IssueSeverity.INFORMATION.toCode(), oo.getIssue().get(0).getSeverity().toCode());
|
||||
assertEquals("my message", oo.getIssue().get(0).getDiagnostics());
|
||||
assertEquals(IssueType.INFORMATIONAL, oo.getIssue().get(0).getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSummaryFails() {
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue("testCreateTextIdFails");
|
||||
p.addName().addFamily("Hello");
|
||||
|
||||
ArrayList<Coding> tl = new ArrayList<Coding>();
|
||||
p.getMeta().addTag().setSystem(Constants.TAG_SUBSETTED_SYSTEM).setCode(Constants.TAG_SUBSETTED_CODE);
|
||||
|
||||
try {
|
||||
myPatientDao.create(p, new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertThat(e.getMessage(), containsString("subsetted"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateLongString() {
|
||||
//@formatter:off
|
||||
|
@ -468,22 +445,57 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWrongType() {
|
||||
public void testCreateOperationOutcome() {
|
||||
/*
|
||||
* If any of this ever fails, it means that one of the OperationOutcome issue severity codes has changed code value across versions. We store the string as a constant, so something will need to
|
||||
* be fixed.
|
||||
*/
|
||||
assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.ERROR.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
|
||||
assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.ERROR.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
|
||||
assertEquals(org.hl7.fhir.dstu3.model.OperationOutcome.IssueSeverity.ERROR.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
|
||||
assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.INFORMATION.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_INFO);
|
||||
assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.INFORMATION.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_INFO);
|
||||
assertEquals(org.hl7.fhir.dstu3.model.OperationOutcome.IssueSeverity.INFORMATION.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_INFO);
|
||||
assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.WARNING.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
|
||||
assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.WARNING.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
|
||||
assertEquals(org.hl7.fhir.dstu3.model.OperationOutcome.IssueSeverity.WARNING.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
|
||||
}
|
||||
|
||||
// Lose typing so we can put the wrong type in
|
||||
@SuppressWarnings("rawtypes")
|
||||
IFhirResourceDao dao = myNamingSystemDao;
|
||||
|
||||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("My Name");
|
||||
try {
|
||||
dao.create(resource, new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Incorrect resource type detected for endpoint, found Patient but expected NamingSystem", e.getMessage());
|
||||
}
|
||||
@Test
|
||||
public void testCreateOperationOutcomeError() {
|
||||
FhirResourceDaoDstu3<Bundle> dao = new FhirResourceDaoDstu3<Bundle>();
|
||||
OperationOutcome oo = (OperationOutcome) dao.createErrorOperationOutcome("my message", "incomplete");
|
||||
assertEquals(IssueSeverity.ERROR.toCode(), oo.getIssue().get(0).getSeverity().toCode());
|
||||
assertEquals("my message", oo.getIssue().get(0).getDiagnostics());
|
||||
assertEquals(IssueType.INCOMPLETE, oo.getIssue().get(0).getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateOperationOutcomeInfo() {
|
||||
FhirResourceDaoDstu3<Bundle> dao = new FhirResourceDaoDstu3<Bundle>();
|
||||
OperationOutcome oo = (OperationOutcome) dao.createInfoOperationOutcome("my message");
|
||||
assertEquals(IssueSeverity.INFORMATION.toCode(), oo.getIssue().get(0).getSeverity().toCode());
|
||||
assertEquals("my message", oo.getIssue().get(0).getDiagnostics());
|
||||
assertEquals(IssueType.INFORMATIONAL, oo.getIssue().get(0).getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSummaryFails() {
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue("testCreateTextIdFails");
|
||||
p.addName().addFamily("Hello");
|
||||
|
||||
ArrayList<Coding> tl = new ArrayList<Coding>();
|
||||
p.getMeta().addTag().setSystem(Constants.TAG_SUBSETTED_SYSTEM).setCode(Constants.TAG_SUBSETTED_CODE);
|
||||
|
||||
try {
|
||||
myPatientDao.create(p, new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (UnprocessableEntityException e) {
|
||||
assertThat(e.getMessage(), containsString("subsetted"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTextIdFails() {
|
||||
Patient p = new Patient();
|
||||
|
@ -629,12 +641,6 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
private CodeableConcept newCodeableConcept(String theSystem, String theCode) {
|
||||
CodeableConcept retVal = new CodeableConcept();
|
||||
retVal.addCoding().setSystem(theSystem).setCode(theCode);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithInvalidReferenceFailsGracefully() {
|
||||
Patient patient = new Patient();
|
||||
|
@ -691,6 +697,23 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWrongType() {
|
||||
|
||||
// Lose typing so we can put the wrong type in
|
||||
@SuppressWarnings("rawtypes")
|
||||
IFhirResourceDao dao = myNamingSystemDao;
|
||||
|
||||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("My Name");
|
||||
try {
|
||||
dao.create(resource, new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Incorrect resource type detected for endpoint, found Patient but expected NamingSystem", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDatePeriodParamEndOnly() {
|
||||
{
|
||||
|
@ -969,23 +992,40 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWithMatchUrlChainedString() {
|
||||
String methodName = "testDeleteWithMatchUrlChainedString";
|
||||
|
||||
Organization org = new Organization();
|
||||
org.setName(methodName);
|
||||
IIdType orgId = myOrganizationDao.create(org, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
public void testDeleteWithMatchUrl() {
|
||||
String methodName = "testDeleteWithMatchUrl";
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
p.getManagingOrganization().setReferenceElement(orgId);
|
||||
IIdType id = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
IIdType id = myPatientDao.create(p, new ServletRequestDetails()).getId();
|
||||
ourLog.info("Created patient, got it: {}", id);
|
||||
|
||||
myPatientDao.deleteByUrl("Patient?organization.name=" + methodName, new ServletRequestDetails());
|
||||
Bundle request = new Bundle();
|
||||
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.DELETE).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
|
||||
|
||||
myPatientDao.deleteByUrl("Patient?identifier=urn%3Asystem%7C" + methodName, new ServletRequestDetails());
|
||||
|
||||
try {
|
||||
myPatientDao.read(id.toVersionless(), new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (ResourceGoneException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
try {
|
||||
myPatientDao.read(new IdType("Patient/" + methodName), new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
IBundleProvider history = myPatientDao.history(id, null, new ServletRequestDetails());
|
||||
assertEquals(2, history.size());
|
||||
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) history.getResources(0, 1).get(0)));
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) history.getResources(0, 1).get(0)).getValue());
|
||||
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) history.getResources(1, 2).get(0)));
|
||||
|
||||
assertGone(id);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1014,6 +1054,73 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWithMatchUrlChainedProfile() {
|
||||
String methodName = "testDeleteWithMatchUrlChainedProfile";
|
||||
|
||||
List<IdType> profileList = new ArrayList<IdType>();
|
||||
|
||||
Organization org = new Organization();
|
||||
|
||||
org.getMeta().getProfile().add(new IdType("http://foo"));
|
||||
org.setName(methodName);
|
||||
|
||||
IIdType orgId = myOrganizationDao.create(org, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
p.getManagingOrganization().setReferenceElement(orgId);
|
||||
IIdType id = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
ourLog.info("Created patient, got it: {}", id);
|
||||
|
||||
myPatientDao.deleteByUrl("Patient?organization._profile=http://foo", new ServletRequestDetails());
|
||||
assertGone(id);
|
||||
|
||||
myOrganizationDao.deleteByUrl("Organization?_profile=http://foo", new ServletRequestDetails());
|
||||
try {
|
||||
myOrganizationDao.read(orgId, new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (ResourceGoneException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
try {
|
||||
myPatientDao.deleteByUrl("Patient?organization._profile.identifier=http://foo", new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Invalid parameter chain: organization._profile.identifier", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
myOrganizationDao.deleteByUrl("Organization?_profile.identifier=http://foo", new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Invalid parameter chain: _profile.identifier", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWithMatchUrlChainedString() {
|
||||
String methodName = "testDeleteWithMatchUrlChainedString";
|
||||
|
||||
Organization org = new Organization();
|
||||
org.setName(methodName);
|
||||
IIdType orgId = myOrganizationDao.create(org, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
p.getManagingOrganization().setReferenceElement(orgId);
|
||||
IIdType id = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
ourLog.info("Created patient, got it: {}", id);
|
||||
|
||||
myPatientDao.deleteByUrl("Patient?organization.name=" + methodName, new ServletRequestDetails());
|
||||
|
||||
assertGone(id);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWithMatchUrlChainedTag() {
|
||||
String methodName = "testDeleteWithMatchUrlChainedString";
|
||||
|
@ -1120,112 +1227,6 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
assertGone(org2Id);
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets called from assertGone too! Careful about exceptions...
|
||||
*/
|
||||
private void assertNotGone(IIdType theId) {
|
||||
if ("Patient".equals(theId.getResourceType())) {
|
||||
myPatientDao.read(theId, new ServletRequestDetails());
|
||||
} else if ("Organization".equals(theId.getResourceType())) {
|
||||
myOrganizationDao.read(theId, new ServletRequestDetails());
|
||||
} else {
|
||||
fail("No type");
|
||||
}
|
||||
}
|
||||
|
||||
private void assertGone(IIdType theId) {
|
||||
try {
|
||||
assertNotGone(theId);
|
||||
fail();
|
||||
} catch (ResourceGoneException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWithMatchUrlChainedProfile() {
|
||||
String methodName = "testDeleteWithMatchUrlChainedProfile";
|
||||
|
||||
List<IdType> profileList = new ArrayList<IdType>();
|
||||
|
||||
Organization org = new Organization();
|
||||
|
||||
org.getMeta().getProfile().add(new IdType("http://foo"));
|
||||
org.setName(methodName);
|
||||
|
||||
IIdType orgId = myOrganizationDao.create(org, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
p.getManagingOrganization().setReferenceElement(orgId);
|
||||
IIdType id = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
ourLog.info("Created patient, got it: {}", id);
|
||||
|
||||
myPatientDao.deleteByUrl("Patient?organization._profile=http://foo", new ServletRequestDetails());
|
||||
assertGone(id);
|
||||
|
||||
myOrganizationDao.deleteByUrl("Organization?_profile=http://foo", new ServletRequestDetails());
|
||||
try {
|
||||
myOrganizationDao.read(orgId, new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (ResourceGoneException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
try {
|
||||
myPatientDao.deleteByUrl("Patient?organization._profile.identifier=http://foo", new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Invalid parameter chain: organization._profile.identifier", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
myOrganizationDao.deleteByUrl("Organization?_profile.identifier=http://foo", new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Invalid parameter chain: _profile.identifier", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteWithMatchUrl() {
|
||||
String methodName = "testDeleteWithMatchUrl";
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
IIdType id = myPatientDao.create(p, new ServletRequestDetails()).getId();
|
||||
ourLog.info("Created patient, got it: {}", id);
|
||||
|
||||
Bundle request = new Bundle();
|
||||
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.DELETE).setUrl("Patient?identifier=urn%3Asystem%7C" + methodName);
|
||||
|
||||
myPatientDao.deleteByUrl("Patient?identifier=urn%3Asystem%7C" + methodName, new ServletRequestDetails());
|
||||
|
||||
try {
|
||||
myPatientDao.read(id.toVersionless(), new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (ResourceGoneException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
try {
|
||||
myPatientDao.read(new IdType("Patient/" + methodName), new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
IBundleProvider history = myPatientDao.history(id, null, new ServletRequestDetails());
|
||||
assertEquals(2, history.size());
|
||||
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) history.getResources(0, 0).get(0)));
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) history.getResources(0, 0).get(0)).getValue());
|
||||
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) history.getResources(1, 1).get(0)));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryByForcedId() {
|
||||
IIdType idv1;
|
||||
|
@ -1262,6 +1263,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
int halfSize = 50;
|
||||
int fullSize = 100;
|
||||
for (int i = 0; i < fullSize; i++) {
|
||||
ourLog.info("Pass {}", i);
|
||||
if (i == halfSize) {
|
||||
Thread.sleep(fullSize);
|
||||
middleDate = new Date();
|
||||
|
@ -1398,6 +1400,57 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryReflectsMetaOperations() throws Exception {
|
||||
Patient inPatient = new Patient();
|
||||
inPatient.addName().addFamily("version1");
|
||||
inPatient.getMeta().addProfile("http://example.com/1");
|
||||
IIdType id = myPatientDao.create(inPatient, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
IBundleProvider history = myPatientDao.history(null, mySrd);
|
||||
assertEquals(1, history.size());
|
||||
Patient outPatient = (Patient) history.getResources(0, 1).get(0);
|
||||
assertEquals("version1", inPatient.getName().get(0).getFamilyAsSingleString());
|
||||
List<String> profiles = toStringList(outPatient.getMeta().getProfile());
|
||||
assertThat(profiles, contains("http://example.com/1"));
|
||||
|
||||
/*
|
||||
* Change metadata
|
||||
*/
|
||||
|
||||
inPatient.getMeta().addProfile("http://example.com/2");
|
||||
myPatientDao.metaAddOperation(id, inPatient.getMeta(), mySrd);
|
||||
|
||||
history = myPatientDao.history(null, mySrd);
|
||||
assertEquals(1, history.size());
|
||||
outPatient = (Patient) history.getResources(0, 1).get(0);
|
||||
assertEquals("version1", inPatient.getName().get(0).getFamilyAsSingleString());
|
||||
profiles = toStringList(outPatient.getMeta().getProfile());
|
||||
assertThat(profiles, containsInAnyOrder("http://example.com/1", "http://example.com/2"));
|
||||
|
||||
/*
|
||||
* Do an update
|
||||
*/
|
||||
|
||||
inPatient.setId(id);
|
||||
inPatient.getMeta().addProfile("http://example.com/3");
|
||||
inPatient.getName().get(0).addFamily("version2");
|
||||
myPatientDao.update(inPatient, mySrd);
|
||||
|
||||
history = myPatientDao.history(null, mySrd);
|
||||
assertEquals(2, history.size());
|
||||
outPatient = (Patient) history.getResources(0, 2).get(0);
|
||||
assertEquals("version1 version2", outPatient.getName().get(0).getFamilyAsSingleString());
|
||||
profiles = toStringList(outPatient.getMeta().getProfile());
|
||||
ourLog.info(profiles.toString());
|
||||
assertThat(profiles, containsInAnyOrder("http://example.com/1", "http://example.com/2", "http://example.com/3"));
|
||||
|
||||
outPatient = (Patient) history.getResources(0, 2).get(1);
|
||||
assertEquals("version1", outPatient.getName().get(0).getFamilyAsSingleString());
|
||||
profiles = toStringList(outPatient.getMeta().getProfile());
|
||||
assertThat(profiles, containsInAnyOrder("http://example.com/1", "http://example.com/2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryWithDeletedResource() throws Exception {
|
||||
|
@ -1412,11 +1465,11 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
myPatientDao.update(patient, new ServletRequestDetails());
|
||||
|
||||
IBundleProvider history = myPatientDao.history(id, null, new ServletRequestDetails());
|
||||
assertEquals(3, history.size());
|
||||
List<IBaseResource> entries = history.getResources(0, 3);
|
||||
ourLog.info("" + ((IAnyResource) entries.get(0)).getMeta().getLastUpdated());
|
||||
ourLog.info("" + ((IAnyResource) entries.get(1)).getMeta().getLastUpdated());
|
||||
ourLog.info("" + ((IAnyResource) entries.get(2)).getMeta().getLastUpdated());
|
||||
ourLog.info(((IAnyResource) entries.get(0)).getIdElement() + " - " + ((IAnyResource) entries.get(0)).getMeta().getLastUpdated());
|
||||
ourLog.info(((IAnyResource) entries.get(1)).getIdElement() + " - " + ((IAnyResource) entries.get(1)).getMeta().getLastUpdated());
|
||||
ourLog.info(((IAnyResource) entries.get(2)).getIdElement() + " - " + ((IAnyResource) entries.get(2)).getMeta().getLastUpdated());
|
||||
assertEquals(3, history.size());
|
||||
|
||||
assertEquals(id.withVersion("3"), entries.get(0).getIdElement());
|
||||
assertEquals(id.withVersion("2"), entries.get(1).getIdElement());
|
||||
|
@ -1432,6 +1485,16 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
assertEquals(BundleEntryTransactionMethodEnum.POST.getCode(), ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get((IAnyResource) entries.get(2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryWithInvalidId() throws Exception {
|
||||
try {
|
||||
myPatientDao.history(new IdDt("Patient/FOOFOOFOO"), null, new ServletRequestDetails());
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertEquals("Resource Patient/FOOFOOFOO is not known", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIdParam() {
|
||||
Patient patient = new Patient();
|
||||
|
@ -2327,79 +2390,6 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortByLastUpdated() {
|
||||
String methodName = "testSortByLastUpdated";
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system1").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id1 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system2").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id2 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system3").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id3 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system4").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id4 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameterMap pm;
|
||||
List<IIdType> actual;
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.DESC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id4, id3, id2, id1));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam(null, methodName));
|
||||
pm.setSort(new SortSpec(Patient.SP_NAME).setChain(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.DESC)));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id4, id3, id2, id1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortNoMatches() {
|
||||
String methodName = "testSortNoMatches";
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
IIdType id1 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameterMap map;
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.add(BaseResource.SP_RES_ID, new StringParam(id1.getIdPart()));
|
||||
map.setLastUpdated(new DateRangeParam("2001", "2003"));
|
||||
map.setSort(new SortSpec(Constants.PARAM_LASTUPDATED));
|
||||
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.add(BaseResource.SP_RES_ID, new StringParam(id1.getIdPart()));
|
||||
map.setLastUpdated(new DateRangeParam("2001", "2003"));
|
||||
map.setSort(new SortSpec(Patient.SP_NAME));
|
||||
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), empty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortById() {
|
||||
String methodName = "testSortBTyId";
|
||||
|
@ -2451,6 +2441,55 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
assertThat(actual, contains(id4, id3, id2, id1, idMethodName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortByLastUpdated() {
|
||||
String methodName = "testSortByLastUpdated";
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system1").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id1 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system2").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id2 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system3").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id3 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system4").setValue(methodName);
|
||||
p.addName().addFamily(methodName);
|
||||
IIdType id4 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameterMap pm;
|
||||
List<IIdType> actual;
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.DESC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id4, id3, id2, id1));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam(null, methodName));
|
||||
pm.setSort(new SortSpec(Patient.SP_NAME).setChain(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.DESC)));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id4, id3, id2, id1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortByNumber() {
|
||||
String methodName = "testSortByNumber";
|
||||
|
@ -2778,6 +2817,30 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortNoMatches() {
|
||||
String methodName = "testSortNoMatches";
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
IIdType id1 = myPatientDao.create(p, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameterMap map;
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.add(BaseResource.SP_RES_ID, new StringParam(id1.getIdPart()));
|
||||
map.setLastUpdated(new DateRangeParam("2001", "2003"));
|
||||
map.setSort(new SortSpec(Constants.PARAM_LASTUPDATED));
|
||||
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.add(BaseResource.SP_RES_ID, new StringParam(id1.getIdPart()));
|
||||
map.setLastUpdated(new DateRangeParam("2001", "2003"));
|
||||
map.setSort(new SortSpec(Patient.SP_NAME));
|
||||
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), empty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoreUnversionedResources() {
|
||||
Organization o1 = new Organization();
|
||||
|
@ -2994,4 +3057,12 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
private static List<String> toStringList(List<UriType> theUriType) {
|
||||
ArrayList<String> retVal = new ArrayList<String>();
|
||||
for (UriType next : theUriType) {
|
||||
retVal.add(next.getValue());
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -852,9 +852,9 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
|||
IBundleProvider history = myPatientDao.history(id, null, new ServletRequestDetails());
|
||||
assertEquals(2, history.size());
|
||||
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) history.getResources(0, 0).get(0)));
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) history.getResources(0, 0).get(0)).getValue());
|
||||
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) history.getResources(1, 1).get(0)));
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) history.getResources(0, 1).get(0)));
|
||||
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) history.getResources(0, 1).get(0)).getValue());
|
||||
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) history.getResources(1, 2).get(0)));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src/main/resources"/>
|
||||
<classpathentry kind="src" path="src/main/java"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
|
||||
<attributes>
|
||||
<attribute name="owner.project.facets" value="java"/>
|
||||
|
|
|
@ -191,6 +191,17 @@
|
|||
Old searches are deleted after an hour by default, but this can be changed
|
||||
via a setting in the DaoConfig.
|
||||
</action>
|
||||
<action type="add">
|
||||
JPA servers' resource version history mechanism
|
||||
has been adjusted so that the history table
|
||||
keeps a record of all versions including the
|
||||
current version. This has the very helpful
|
||||
side effect that history no longer needs to be
|
||||
paged into memory as a complete set. Previously
|
||||
history had a hard limit of only being able to
|
||||
page the most recent 20000 entries. Now it has
|
||||
no limit.
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.4" date="2016-02-04">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue