Merge branch 'jpa_perf_enhancements'

This commit is contained in:
James 2017-04-21 06:30:22 -04:00
commit 716fa56b8f
98 changed files with 6115 additions and 3187 deletions

View File

@ -3,19 +3,30 @@ root = true
[*]
end_of_line = lf
insert_final_newline = true
tab_width = 3
indent_size = 3
[*.java]
charset = utf-8
indent_style = tab
tab_width = 3
indent_size = 3
[*.xml]
charset = utf-8
indent_style = tab
tab_width = 3
indent_size = 3
[*.json]
charset = utf-8
indent_style = tab
tab_width = 3
indent_size = 3
[*.vm]
charset = utf-8
indent_style = tab
tab_width = 3
indent_size = 3

View File

@ -39,7 +39,7 @@ public class PagingPatientProvider implements IResourceProvider {
return new IBundleProvider() {
@Override
public int size() {
public Integer size() {
return matchingResourceIds.size();
}

View File

@ -130,6 +130,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Server -->
<dependency>

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.api;
import java.io.Serializable;
/*
* #%L
* HAPI FHIR - Core Library
@ -26,7 +28,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public interface IQueryParameterAnd<T extends IQueryParameterOr<?>> {
public interface IQueryParameterAnd<T extends IQueryParameterOr<?>> extends Serializable {
/**
*

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.api;
import java.io.Serializable;
/*
* #%L
* HAPI FHIR - Core Library
@ -25,7 +27,7 @@ import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.method.QualifiedParamList;
public interface IQueryParameterOr<T extends IQueryParameterType> {
public interface IQueryParameterOr<T extends IQueryParameterType> extends Serializable {
public void setValuesAsQueryTokens(FhirContext theContext, String theParamName, QualifiedParamList theParameters);

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.api;
import java.io.Serializable;
import ca.uhn.fhir.context.FhirContext;
/*
@ -22,7 +24,7 @@ import ca.uhn.fhir.context.FhirContext;
* #L%
*/
public interface IQueryParameterType {
public interface IQueryParameterType extends Serializable {
/**
* This method is generally only called by HAPI itself, and should not need to be called from user code.

View File

@ -3,6 +3,8 @@ package ca.uhn.fhir.model.api;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.Serializable;
import org.apache.commons.lang3.builder.ToStringBuilder;
/*
@ -33,8 +35,10 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
* upgrading servers.
* </p>
*/
public class Include {
public class Include implements Serializable {
private static final long serialVersionUID = 1L;
private final boolean myImmutable;
private boolean myRecurse;
private String myValue;

View File

@ -104,9 +104,9 @@ public class TagList implements Set<Tag>, Serializable, IBase {
* Add a new tag instance
*
* @param theScheme
* The tag scheme
* The tag scheme (the system)
* @param theTerm
* The tag term
* The tag term (the code)
* @return Returns the newly created tag instance. Note that the tag is added to the list by this method, so you
* generally do not need to interact directly with the added tag.
*/

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.api;
import java.io.Serializable;
/*
* #%L
* HAPI FHIR - Core Library
@ -24,8 +26,10 @@ package ca.uhn.fhir.rest.api;
* Represents values for <a href="http://hl7.org/implement/standards/fhir/search.html#sort">sorting</a> resources
* returned by a server.
*/
public class SortSpec {
public class SortSpec implements Serializable {
private static final long serialVersionUID = 2866833099879713467L;
private SortSpec myChain;
private String myParamName;
private SortOrderEnum myOrder;

View File

@ -375,8 +375,15 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
if (offsetI == null || offsetI < 0) {
offsetI = 0;
}
int start = Math.max(0, Math.min(offsetI, result.size() - 1));
Integer resultSize = result.size();
int start;
if (resultSize != null) {
start = Math.max(0, Math.min(offsetI, resultSize - 1));
} else {
start = offsetI;
}
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory();
ResponseEncoding responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest, theServer.getDefaultResponseEncoding());

View File

@ -196,7 +196,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
public int size() {
public Integer size() {
return resources.size();
}

View File

@ -93,7 +93,8 @@ public class PageMethodBinding extends BaseResourceReturningMethodBinding {
IBundleProvider resultList = pagingProvider.retrieveResultList(thePagingAction);
if (resultList == null) {
ourLog.info("Client requested unknown paging ID[{}]", thePagingAction);
throw new ResourceGoneException("Search ID[" + thePagingAction + "] does not exist and may have expired.");
String msg = getContext().getLocalizer().getMessage(PageMethodBinding.class, "unknownSearchId", thePagingAction);
throw new ResourceGoneException(msg);
}
Integer count = RestfulServerUtils.extractCountParameter(theRequest);
@ -108,8 +109,12 @@ public class PageMethodBinding extends BaseResourceReturningMethodBinding {
offsetI = 0;
}
int start = Math.min(offsetI, resultList.size() - 1);
Integer totalNum = resultList.size();
int start = offsetI;
if (totalNum != null) {
start = Math.min(start, totalNum - 1);
}
ResponseEncoding responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest, theServer.getDefaultResponseEncoding());
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);

View File

@ -63,6 +63,7 @@ public abstract class RequestDetails {
private IRestfulResponse myResponse;
private RestOperationTypeEnum myRestOperationType;
private String mySecondaryOperation;
private boolean mySubRequest;
private Map<String, List<String>> myUnqualifiedToQualifiedNames;
private Map<Object, Object> myUserData;
protected abstract byte[] getByteStreamRequestContents();
@ -71,6 +72,7 @@ public abstract class RequestDetails {
* Return the charset as defined by the header contenttype. Return null if it is not set.
*/
public abstract Charset getCharset();
public String getCompartmentName() {
return myCompartmentName;
}
@ -78,7 +80,6 @@ public abstract class RequestDetails {
public String getCompleteUrl() {
return myCompleteUrl;
}
/**
* Returns the <b>conditional URL</b> if this request has one, or <code>null</code> otherwise. For an
* update or delete method, this is the part of the URL after the <code>?</code>. For a create, this
@ -248,6 +249,19 @@ public abstract class RequestDetails {
return myRespondGzip;
}
/**
* Is this request a sub-request (i.e. a request within a batch or transaction)? This
* flag is used internally by hapi-fhir-jpaserver-base, but not used in the plain server
* library. You may use it in your client code as a hint when implementing transaction logic in the plain
* server.
* <p>
* Defaults to {@literal false}
* </p>
*/
public boolean isSubRequest() {
return mySubRequest;
}
public final byte[] loadRequestContents() {
if (myRequestContents == null) {
myRequestContents = getByteStreamRequestContents();
@ -327,10 +341,23 @@ public abstract class RequestDetails {
public void setRestOperationType(RestOperationTypeEnum theRestOperationType) {
myRestOperationType = theRestOperationType;
}
public void setSecondaryOperation(String theSecondaryOperation) {
mySecondaryOperation = theSecondaryOperation;
}
/**
* Is this request a sub-request (i.e. a request within a batch or transaction)? This
* flag is used internally by hapi-fhir-jpaserver-base, but not used in the plain server
* library. You may use it in your client code as a hint when implementing transaction logic in the plain
* server.
* <p>
* Defaults to {@literal false}
* </p>
*/
public void setSubRequest(boolean theSubRequest) {
mySubRequest = theSubRequest;
}
private class RequestOperationCallback implements IRequestOperationCallback {

View File

@ -36,6 +36,8 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class DateRangeParam implements IQueryParameterAnd<DateParam> {
private static final long serialVersionUID = 1L;
private DateParam myLowerBound;
private DateParam myUpperBound;

View File

@ -51,7 +51,7 @@ public class BundleProviders {
}
@Override
public int size() {
public Integer size() {
return 0;
}

View File

@ -58,9 +58,10 @@ public interface IBundleProvider {
/**
* Returns the total number of results which match the given query (exclusive of any
* _include's or OperationOutcome)
* _include's or OperationOutcome). May return {@literal null} if the total size is not
* known or would be too expensive to calculate.
*/
int size();
Integer size();
/**
* Returns the instant as of which this result was valid

View File

@ -52,7 +52,7 @@ public class SimpleBundleProvider implements IBundleProvider {
}
@Override
public int size() {
public Integer size() {
return myList.size();
}

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.rest.server.servlet;
* 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
* 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,
@ -94,7 +94,7 @@ public class ServletRequestDetails extends RequestDetails {
try {
InputStream inputStream = reader.getInputStream(this);
requestContents = IOUtils.toByteArray(inputStream);
if (myServer.isUncompressIncomingContents()) {
String contentEncoding = myServletRequest.getHeader(Constants.HEADER_CONTENT_ENCODING);
if ("gzip".equals(contentEncoding)) {
@ -105,7 +105,7 @@ public class ServletRequestDetails extends RequestDetails {
}
}
}
//FIXME resource leak
// FIXME resource leak
return requestContents;
} catch (IOException e) {
ourLog.error("Could not load request resource", e);
@ -164,7 +164,6 @@ public class ServletRequestDetails extends RequestDetails {
this.myServletResponse = myServletResponse;
}
@Override
public Charset getCharset() {
String ct = getHeader(Constants.HEADER_CONTENT_TYPE);

View File

@ -30,6 +30,8 @@ ca.uhn.fhir.rest.method.OperationParameter.urlParamNotPrimitive=Can not invoke o
ca.uhn.fhir.rest.method.IncludeParameter.invalidIncludeNameInRequest=Invalid {2} parameter value: "{0}". Valid values are: {1}
ca.uhn.fhir.rest.method.IncludeParameter.orIncludeInRequest='OR' query parameters (values containing ',') are not supported in _include parameters
ca.uhn.fhir.rest.method.PageMethodBinding.unknownSearchId=Search ID "{0}" does not exist and may have expired
ca.uhn.fhir.rest.method.SearchMethodBinding.invalidSpecialParamName=Method [{0}] in provider [{1}] contains search parameter annotated to use name [{2}] - This name is reserved according to the FHIR specification and can not be used as a search parameter name.
ca.uhn.fhir.rest.method.SearchMethodBinding.idWithoutCompartment=Method [{0}] in provider [{1}] has an @IdParam parameter. This is only allowable for compartment search (e.g. @Search(compartment="foo") )
ca.uhn.fhir.rest.method.SearchMethodBinding.idNullForCompartmentSearch=ID parameter can not be null or empty for compartment search

View File

@ -37,16 +37,15 @@ import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
import org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
import ca.uhn.fhir.jpa.search.*;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl;
@Configuration
@EnableScheduling
@EnableJpaRepositories(basePackages = "ca.uhn.fhir.jpa.dao.data")
public class BaseConfig implements SchedulingConfigurer {
@Resource
private ApplicationContext myAppCtx;
@ -64,11 +63,6 @@ public class BaseConfig implements SchedulingConfigurer {
return retVal;
}
@Bean(autowire=Autowire.BY_TYPE)
public IStaleSearchDeletingSvc staleSearchDeletingSvc() {
return new StaleSearchDeletingSvcImpl();
}
@Bean()
public ScheduledExecutorFactoryBean scheduledExecutorService() {
ScheduledExecutorFactoryBean b = new ScheduledExecutorFactoryBean();
@ -76,6 +70,21 @@ public class BaseConfig implements SchedulingConfigurer {
return b;
}
@Bean(autowire=Autowire.BY_TYPE)
public ISearchCoordinatorSvc searchCoordinatorSvc() {
return new SearchCoordinatorSvcImpl();
}
@Bean
public ISearchParamPresenceSvc searchParamPresenceSvc() {
return new SearchParamPresenceSvcImpl();
}
@Bean(autowire=Autowire.BY_TYPE)
public IStaleSearchDeletingSvc staleSearchDeletingSvc() {
return new StaleSearchDeletingSvcImpl();
}
@Bean
public TaskScheduler taskScheduler() {
ConcurrentTaskScheduler retVal = new ConcurrentTaskScheduler();

View File

@ -35,6 +35,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
@ -75,32 +76,12 @@ import ca.uhn.fhir.context.FhirVersionEnum;
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.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.entity.BaseTag;
import ca.uhn.fhir.jpa.entity.ForcedId;
import ca.uhn.fhir.jpa.entity.ResourceEncodingEnum;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTag;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
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.dao.data.*;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.util.DeleteConflict;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterType;
@ -155,7 +136,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
public static final String OO_SEVERITY_WARN = "warning";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirDao.class);
private static final Map<FhirVersionEnum, FhirContext> ourRetrievalContexts = new HashMap<FhirVersionEnum, FhirContext>();
private static final String PROCESSING_SUB_REQUEST = "BaseHapiFhirDao.processingSubRequest";
/**
* These are parameters which are supported by {@link BaseHapiFhirResourceDao#searchForIds(Map)}
@ -183,17 +163,16 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
RESOURCE_META_PARAMS = Collections.unmodifiableMap(resourceMetaParams);
RESOURCE_META_AND_PARAMS = Collections.unmodifiableMap(resourceMetaAndParams);
}
@Autowired(required = true)
private DaoConfig myConfig;
private FhirContext myContext;
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
protected EntityManager myEntityManager;
@Autowired
protected IForcedIdDao myForcedIdDao;
@Autowired(required = false)
protected IFulltextSearchSvc myFulltextSearchSvc;
@Autowired
private PlatformTransactionManager myPlatformTransactionManager;
@ -203,20 +182,35 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
@Autowired
private IResourceHistoryTableDao myResourceHistoryTableDao;
@Autowired()
protected IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
private Map<Class<? extends IBaseResource>, IFhirResourceDao<?>> myResourceTypeToDao;
@Autowired
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
@Autowired
private ISearchDao mySearchDao;
@Autowired
private ISearchParamExtractor mySearchParamExtractor;
@Autowired
private ISearchParamPresenceSvc mySearchParamPresenceSvc;
@Autowired
private ISearchParamRegistry mySearchParamRegistry;
@Autowired
private ISearchResultDao mySearchResultDao;
@Autowired
protected ISearchParamRegistry mySerarchParamRegistry;
@Autowired()
protected IHapiTerminologySvc myTerminologySvc;
protected void clearRequestAsProcessingSubRequest(ServletRequestDetails theRequestDetails) {
if (theRequestDetails != null) {
theRequestDetails.getUserData().remove(PROCESSING_SUB_REQUEST);
@ -243,14 +237,19 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return InstantDt.withCurrentTime();
}
/**
* @return Returns a set containing all of the parameter names that
* were found to have a value
*/
@SuppressWarnings("unchecked")
protected void extractResourceLinks(ResourceTable theEntity, IBaseResource theResource, Set<ResourceLink> theLinks, Date theUpdateTime) {
protected Set<String> extractResourceLinks(ResourceTable theEntity, IBaseResource theResource, Set<ResourceLink> theLinks, Date theUpdateTime) {
HashSet<String> retVal = new HashSet<String>();
/*
* For now we don't try to load any of the links in a bundle if it's the actual bundle we're storing..
*/
if (theResource instanceof IBaseBundle) {
return;
return Collections.emptySet();
}
Map<String, RuntimeSearchParam> searchParams = mySearchParamRegistry.getActiveSearchParams(toResourceName(theResource.getClass()));
@ -311,6 +310,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
}
retVal.add(nextSpDef.getName());
if (isLogicalReference(nextId)) {
ResourceLink resourceLink = new ResourceLink(nextPathAndRef.getPath(), theEntity, nextId, theUpdateTime);
if (theLinks.add(resourceLink)) {
@ -394,26 +395,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
theEntity.setHasLinks(theLinks.size() > 0);
}
protected boolean isLogicalReference(IIdType theId) {
Set<String> treatReferencesAsLogical = myConfig.getTreatReferencesAsLogical();
if (treatReferencesAsLogical != null) {
for (String nextLogicalRef : treatReferencesAsLogical) {
nextLogicalRef = trim(nextLogicalRef);
if (nextLogicalRef.charAt(nextLogicalRef.length() - 1) == '*') {
if (theId.getValue().startsWith(nextLogicalRef.substring(0, nextLogicalRef.length() - 1))) {
return true;
}
} else {
if (theId.getValue().equals(nextLogicalRef)) {
return true;
}
}
}
}
return false;
return retVal;
}
protected Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
@ -531,6 +513,56 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
}
@SuppressWarnings("unchecked")
private <T extends BaseResourceIndexedSearchParam> void findMissingSearchParams(ResourceTable theEntity, Set<Entry<String, RuntimeSearchParam>> activeSearchParams, RestSearchParameterTypeEnum type,
Set<T> paramCollection) {
for (Entry<String, RuntimeSearchParam> nextEntry : activeSearchParams) {
String nextParamName = nextEntry.getKey();
if (nextEntry.getValue().getParamType() == type) {
boolean haveParam = false;
for (BaseResourceIndexedSearchParam nextParam : paramCollection) {
if (nextParam.getParamName().equals(nextParamName)) {
haveParam = true;
break;
}
}
if (!haveParam) {
BaseResourceIndexedSearchParam param;
switch (type) {
case DATE:
param = new ResourceIndexedSearchParamDate();
break;
case NUMBER:
param = new ResourceIndexedSearchParamNumber();
break;
case QUANTITY:
param = new ResourceIndexedSearchParamQuantity();
break;
case STRING:
param = new ResourceIndexedSearchParamString();
break;
case TOKEN:
param = new ResourceIndexedSearchParamToken();
break;
case URI:
param = new ResourceIndexedSearchParamUri();
break;
case COMPOSITE:
case HAS:
case REFERENCE:
default:
continue;
}
param.setResource(theEntity);
param.setMissing(true);
param.setParamName(nextParamName);
paramCollection.add((T) param);
}
}
}
}
protected DaoConfig getConfig() {
return myConfig;
}
@ -613,7 +645,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return retVal;
}
}
protected TagList getTags(Class<? extends IBaseResource> theResourceType, IIdType theResourceId) {
String resourceName = null;
if (theResourceType != null) {
@ -652,7 +684,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return retVal;
}
}
protected IBundleProvider history(String theResourceName, Long theId, Date theSince, Date theUntil) {
String resourceName = defaultIfBlank(theResourceName, null);
@ -664,7 +696,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
search.setResourceType(resourceName);
search.setResourceId(theId);
search.setSearchType(SearchTypeEnum.HISTORY);
search.setStatus(SearchStatusEnum.FINISHED);
if (theSince != null) {
if (resourceName == null) {
search.setTotalCount(myResourceHistoryTableDao.countForAllResourceTypes(theSince));
@ -694,7 +727,27 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
theProvider.setEntityManager(myEntityManager);
theProvider.setPlatformTransactionManager(myPlatformTransactionManager);
theProvider.setSearchDao(mySearchDao);
theProvider.setSearchResultDao(mySearchResultDao);
theProvider.setSearchCoordinatorSvc(mySearchCoordinatorSvc);
}
protected boolean isLogicalReference(IIdType theId) {
Set<String> treatReferencesAsLogical = myConfig.getTreatReferencesAsLogical();
if (treatReferencesAsLogical != null) {
for (String nextLogicalRef : treatReferencesAsLogical) {
nextLogicalRef = trim(nextLogicalRef);
if (nextLogicalRef.charAt(nextLogicalRef.length() - 1) == '*') {
if (theId.getValue().startsWith(nextLogicalRef.substring(0, nextLogicalRef.length() - 1))) {
return true;
}
} else {
if (theId.getValue().equals(nextLogicalRef)) {
return true;
}
}
}
}
return false;
}
protected void markRequestAsProcessingSubRequest(ServletRequestDetails theRequestDetails) {
@ -703,6 +756,14 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
}
@Override
public SearchBuilder newSearchBuilder() {
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, myFulltextSearchSvc, mySearchResultDao, this, myResourceIndexedSearchParamUriDao,
myForcedIdDao,
myTerminologySvc, mySerarchParamRegistry);
return builder;
}
protected void notifyInterceptors(RestOperationTypeEnum theOperationType, ActionRequestDetails theRequestDetails) {
if (theRequestDetails.getId() != null && theRequestDetails.getId().hasResourceType() && isNotBlank(theRequestDetails.getResourceType())) {
if (theRequestDetails.getId().getResourceType().equals(theRequestDetails.getResourceType()) == false) {
@ -966,7 +1027,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
* @param theTag
* The tag
* @return Returns <code>true</code> if the tag should be removed
*/
protected void postPersist(ResourceTable theEntity, T theResource) {
// nothing
@ -1023,6 +1083,12 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
myPlatformTransactionManager = thePlatformTransactionManager;
}
private void setUpdatedTime(Collection<? extends BaseResourceIndexedSearchParam> theParams, Date theUpdateTime) {
for (BaseResourceIndexedSearchParam nextSearchParam : theParams) {
nextSearchParam.setUpdated(theUpdateTime);
}
}
/**
* 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>
@ -1224,6 +1290,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
Set<ResourceIndexedSearchParamCoords> coordsParams = null;
Set<ResourceLink> links = null;
Set<String> populatedResourceLinkParameters = Collections.emptySet();
if (theDeletedTimestampOrNull != null) {
stringParams = Collections.emptySet();
@ -1264,6 +1331,14 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
}
Set<Entry<String, RuntimeSearchParam>> activeSearchParams = mySearchParamRegistry.getActiveSearchParams(theEntity.getResourceType()).entrySet();
findMissingSearchParams(theEntity, activeSearchParams, RestSearchParameterTypeEnum.STRING, stringParams);
findMissingSearchParams(theEntity, activeSearchParams, RestSearchParameterTypeEnum.NUMBER, numberParams);
findMissingSearchParams(theEntity, activeSearchParams, RestSearchParameterTypeEnum.QUANTITY, quantityParams);
findMissingSearchParams(theEntity, activeSearchParams, RestSearchParameterTypeEnum.DATE, dateParams);
findMissingSearchParams(theEntity, activeSearchParams, RestSearchParameterTypeEnum.URI, uriParams);
findMissingSearchParams(theEntity, activeSearchParams, RestSearchParameterTypeEnum.TOKEN, tokenParams);
setUpdatedTime(stringParams, theUpdateTime);
setUpdatedTime(numberParams, theUpdateTime);
setUpdatedTime(quantityParams, theUpdateTime);
@ -1323,7 +1398,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
links = new HashSet<ResourceLink>();
extractResourceLinks(theEntity, theResource, links, theUpdateTime);
populatedResourceLinkParameters = extractResourceLinks(theEntity, theResource, links, theUpdateTime);
/*
* If the existing resource already has links and those match links we still want, use them instead of removing them and re adding them
@ -1392,6 +1467,26 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
postUpdate(theEntity, (T) theResource);
}
/*
* Update the "search param present" table which is used for the
* ?foo:missing=true queries
*/
if (thePerformIndexing) {
Map<String, Boolean> presentSearchParams = new HashMap<String, Boolean>();
for (String nextKey : populatedResourceLinkParameters) {
presentSearchParams.put(nextKey, Boolean.TRUE);
}
Set<Entry<String, RuntimeSearchParam>> activeSearchParams = mySearchParamRegistry.getActiveSearchParams(theEntity.getResourceType()).entrySet();
for (Entry<String, RuntimeSearchParam> nextSpEntry : activeSearchParams) {
if (nextSpEntry.getValue().getParamType() == RestSearchParameterTypeEnum.REFERENCE) {
if (!presentSearchParams.containsKey(nextSpEntry.getKey())) {
presentSearchParams.put(nextSpEntry.getKey(), Boolean.FALSE);
}
}
}
mySearchParamPresenceSvc.updatePresence(theEntity, presentSearchParams);
}
/*
* Create history entry
*/
@ -1482,16 +1577,16 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return theEntity;
}
private void setUpdatedTime(Collection<? extends BaseResourceIndexedSearchParam> theParams, Date theUpdateTime) {
for (BaseResourceIndexedSearchParam nextSearchParam : theParams) {
nextSearchParam.setUpdated(theUpdateTime);
}
}
protected ResourceTable updateEntity(IBaseResource theResource, ResourceTable entity, Date theDeletedTimestampOrNull, Date theUpdateTime) {
return updateEntity(theResource, entity, theDeletedTimestampOrNull, true, true, theUpdateTime);
}
private void updateSearchParamPresent(Map<String, Boolean> presentSearchParams, Set<? extends BaseResourceIndexedSearchParam> params) {
for (BaseResourceIndexedSearchParam nextSearchParam : params) {
presentSearchParams.put(nextSearchParam.getParamName(), Boolean.TRUE);
}
}
private void validateChildReferences(IBase theElement, String thePath) {
if (theElement == null) {
return;

View File

@ -78,8 +78,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
protected PlatformTransactionManager myPlatformTransactionManager;
@Autowired
private IResourceHistoryTableDao myResourceHistoryTableDao;
@Autowired()
protected IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
private String myResourceName;
@Autowired
protected IResourceTableDao myResourceTableDao;
@ -89,10 +87,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Autowired()
protected ISearchResultDao mySearchResultDao;
private String mySecondaryPrimaryKeyParamName;
@Autowired
private ISearchParamRegistry mySerarchParamRegistry;
@Autowired()
protected IHapiTerminologySvc myTerminologySvc;
@Override
public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
@ -599,87 +593,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return retVal;
}
// @Override
// public IBundleProvider everything(IIdType theId) {
// Search search = new Search();
// search.setUuid(UUID.randomUUID().toString());
// search.setCreated(new Date());
// myEntityManager.persist(search);
//
// List<SearchResult> results = new ArrayList<SearchResult>();
// if (theId != null) {
// Long pid = translateForcedIdToPid(theId);
// ResourceTable entity = myEntityManager.find(ResourceTable.class, pid);
// validateGivenIdIsAppropriateToRetrieveResource(theId, entity);
// SearchResult res = new SearchResult(search);
// res.setResourcePid(pid);
// results.add(res);
// } else {
// TypedQuery<Tuple> query = createSearchAllByTypeQuery();
// for (Tuple next : query.getResultList()) {
// SearchResult res = new SearchResult(search);
// res.setResourcePid(next.get(0, Long.class));
// results.add(res);
// }
// }
//
// int totalCount = results.size();
// mySearchResultDao.save(results);
// mySearchResultDao.flush();
//
// CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
//
// // Load _revincludes
// CriteriaQuery<Long> cq = builder.createQuery(Long.class);
// Root<ResourceLink> from = cq.from(ResourceLink.class);
// cq.select(from.get("mySourceResourcePid").as(Long.class));
//
// Subquery<Long> pidsSubquery = cq.subquery(Long.class);
// Root<SearchResult> pidsSubqueryFrom = pidsSubquery.from(SearchResult.class);
// pidsSubquery.select(pidsSubqueryFrom.get("myResourcePid").as(Long.class));
// pidsSubquery.where(pidsSubqueryFrom.get("mySearch").in(search));
//
// cq.where(from.get("myTargetResourceId").in(pidsSubquery));
// TypedQuery<Long> query = myEntityManager.createQuery(cq);
//
// results = new ArrayList<SearchResult>();
// for (Long next : query.getResultList()) {
// SearchResult res = new SearchResult(search);
// res.setResourcePid(next);
// results.add(res);
// }
//
// // Save _revincludes
// totalCount += results.size();
// mySearchResultDao.save(results);
// mySearchResultDao.flush();
//
// final int finalTotalCount = totalCount;
// return new IBundleProvider() {
//
// @Override
// public int size() {
// return finalTotalCount;
// }
//
// @Override
// public Integer preferredPageSize() {
// return null;
// }
//
// @Override
// public List<IBaseResource> getResources(int theFromIndex, int theToIndex) {
// // TODO Auto-generated method stub
// return null;
// }
//
// @Override
// public InstantDt getPublished() {
// // TODO Auto-generated method stub
// return null;
// }
// };
// }
@Override
public <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType, IIdType theId, RequestDetails theRequestDetails) {
@ -761,7 +674,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
* Subclasses may override to provide behaviour. Invoked within a delete
* transaction with the resource that is about to be deleted.
*/
@SuppressWarnings("unused")
protected void preDelete(T theResourceToDelete, ResourceTable theEntityToDelete) {
// nothing by default
}
@ -948,32 +860,29 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
ourLog.info("Processed remove tag {}/{} on {} in {}ms", new Object[] { theScheme, theTerm, theId.getValue(), w.getMillisAndRestart() });
}
@Override
public IBundleProvider search(Map<String, IQueryParameterType> theParams) {
SearchParameterMap map = new SearchParameterMap();
for (Entry<String, IQueryParameterType> nextEntry : theParams.entrySet()) {
map.add(nextEntry.getKey(), (nextEntry.getValue()));
}
return search(map);
}
@Override
public IBundleProvider search(final SearchParameterMap theParams) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theParams.getRequestDetails(), getContext(), getResourceName(), null);
notifyInterceptors(RestOperationTypeEnum.SEARCH_TYPE, requestDetails);
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao,
myTerminologySvc, mySerarchParamRegistry);
builder.setType(getResourceType(), getResourceName());
return builder.search(theParams);
return search(theParams, null);
}
@Override
public IBundleProvider search(String theParameterName, IQueryParameterType theValue) {
return search(Collections.singletonMap(theParameterName, theValue));
public IBundleProvider search(final SearchParameterMap theParams, RequestDetails theRequestDetails) {
// Notify interceptors
if (theRequestDetails != null) {
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), getResourceName(), null);
notifyInterceptors(RestOperationTypeEnum.SEARCH_TYPE, requestDetails);
if (theRequestDetails.isSubRequest()) {
theParams.setLoadSynchronous(true);
theParams.setLoadSynchronousUpTo(myDaoConfig.getMaximumSearchResultCountInTransaction());
}
}
return mySearchCoordinatorSvc.registerSearch(this, theParams, getResourceName());
}
@Override
public Set<Long> searchForIds(Map<String, IQueryParameterType> theParams) {
SearchParameterMap map = new SearchParameterMap();
@ -990,13 +899,20 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override
public Set<Long> searchForIdsWithAndOr(SearchParameterMap theParams) {
theParams.setPersistResults(false);
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao,
myTerminologySvc, mySerarchParamRegistry);
SearchBuilder builder = newSearchBuilder();
builder.setType(getResourceType(), getResourceName());
builder.search(theParams);
return builder.doGetPids();
// FIXME: fail if too many results
HashSet<Long> retVal = new HashSet<Long>();
Iterator<Long> iter = builder.createQuery(theParams);
while (iter.hasNext()) {
retVal.add(iter.next());
}
return retVal;
}
@SuppressWarnings("unchecked")

View File

@ -48,6 +48,13 @@ public class DaoConfig {
"http://hl7.org/fhir/codesystem-*",
"http://hl7.org/fhir/StructureDefinition/*")));
/**
* Default value for {@link #setMaximumSearchResultCountInTransaction(int)}
*
* @see #setMaximumSearchResultCountInTransaction(int)
*/
private static final int DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION = 500;
// ***
// update setter javadoc if default changes
// ***
@ -86,12 +93,14 @@ public class DaoConfig {
// update setter javadoc if default changes
// ***
private int myMaximumExpansionSize = 5000;
private int myMaximumSearchResultCountInTransaction = DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION;
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
private boolean mySchedulingDisabled;
private boolean mySubscriptionEnabled;
private long mySubscriptionPollDelay = 1000;
private Long mySubscriptionPurgeInactiveAfterMillis;
private Set<String> myTreatBaseUrlsAsLocal = new HashSet<String>();
private Set<String> myTreatReferencesAsLogical = new HashSet<String>(DEFAULT_LOGICAL_BASE_URLS);
/**
@ -173,6 +182,18 @@ public class DaoConfig {
return myMaximumExpansionSize;
}
/**
* Provides the maximum number of results which may be returned by a search within a FHIR <code>transaction</code>
* operation. For example, if this value is set to <code>100</code> and a FHIR transaction is processed with a sub-request
* for <code>Patient?gender=male</code>, the server will throw an error (and the transaction will fail) if there are more than
* 100 resources on the server which match this query.
*
* @see #DEFAULT_LOGICAL_BASE_URLS The default value for this setting
*/
public int getMaximumSearchResultCountInTransaction() {
return myMaximumSearchResultCountInTransaction;
}
public ResourceEncodingEnum getResourceEncoding() {
return myResourceEncoding;
}
@ -499,6 +520,18 @@ public class DaoConfig {
myMaximumExpansionSize = theMaximumExpansionSize;
}
/**
* Provides the maximum number of results which may be returned by a search within a FHIR <code>transaction</code>
* operation. For example, if this value is set to <code>100</code> and a FHIR transaction is processed with a sub-request
* for <code>Patient?gender=male</code>, the server will throw an error (and the transaction will fail) if there are more than
* 100 resources on the server which match this query.
*
* @see #DEFAULT_LOGICAL_BASE_URLS The default value for this setting
*/
public void setMaximumSearchResultCountInTransaction(int theMaximumSearchResultCountInTransaction) {
myMaximumSearchResultCountInTransaction = theMaximumSearchResultCountInTransaction;
}
public void setResourceEncoding(ResourceEncodingEnum theResourceEncoding) {
myResourceEncoding = theResourceEncoding;
}

View File

@ -26,7 +26,6 @@ import javax.servlet.http.HttpServletRequest;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.model.api.IResource;
@ -47,9 +46,6 @@ public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2<Patient>im
super();
}
@Autowired
private ISearchParamRegistry mySerarchParamRegistry;
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
@ -69,9 +65,7 @@ public class FhirResourceDaoPatientDstu2 extends FhirResourceDaoDstu2<Patient>im
paramMap.add("_id", new StringParam(theId.getIdPart()));
}
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao, myTerminologySvc, mySerarchParamRegistry);
builder.setType(getResourceType(), getResourceName());
return builder.search(paramMap);
return mySearchCoordinatorSvc.registerSearch(this, paramMap, getResourceName());
}
@Override

View File

@ -110,7 +110,10 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
if (defaultValueSet != null) {
source = getContext().newJsonParser().parseResource(ValueSet.class, myRiCtx.newJsonParser().encodeResourceToString(defaultValueSet));
} else {
IBundleProvider ids = search(ValueSet.SP_URL, new UriParam(theUri));
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(ValueSet.SP_URL, new UriParam(theUri));
IBundleProvider ids = search(params);
if (ids.size() == 0) {
throw new InvalidRequestException("Unknown ValueSet URI: " + theUri);
}

View File

@ -66,6 +66,13 @@ public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implem
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
private EntityManager myEntityManager;
/**
* Constructor
*/
public FulltextSearchSvcImpl() {
super();
}
private void addTextSearch(QueryBuilder theQueryBuilder, BooleanJunction<?> theBoolean, List<List<? extends IQueryParameterType>> theTerms, String theFieldName, String theFieldNameEdgeNGram, String theFieldNameNGram) {
if (theTerms == null) {
return;

View File

@ -64,21 +64,23 @@ public interface IDao {
FhirContext getContext();
RuntimeSearchParam getSearchParamByName(RuntimeResourceDefinition theResourceDef, String theParamName);
Collection<RuntimeSearchParam> getSearchParamsByResourceType(RuntimeResourceDefinition theResourceDef);
/**
* Populate all of the runtime dependencies that a bundle provider requires in order to work
*/
void injectDependenciesIntoBundleProvider(PersistedJpaBundleProvider theProvider);
ISearchBuilder newSearchBuilder();
void populateFullTextFields(IBaseResource theResource, ResourceTable theEntity);
<R extends IBaseResource> Set<Long> processMatchUrl(String theMatchUrl, Class<R> theResourceType);
IBaseResource toResource(BaseHasResource theEntity, boolean theForHistoryOperation);
<R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation);
void populateFullTextFields(IBaseResource theResource, ResourceTable theEntity);
RuntimeSearchParam getSearchParamByName(RuntimeResourceDefinition theResourceDef, String theParamName);
Collection<RuntimeSearchParam> getSearchParamsByResourceType(RuntimeResourceDefinition theResourceDef);
}

View File

@ -174,11 +174,9 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
void removeTag(IIdType theId, TagTypeEnum theTagType, String theSystem, String theCode);
IBundleProvider search(Map<String, IQueryParameterType> theParams);
IBundleProvider search(SearchParameterMap theParams);
IBundleProvider search(SearchParameterMap theMap);
IBundleProvider search(String theParameterName, IQueryParameterType theValue);
IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails);
Set<Long> searchForIds(Map<String, IQueryParameterType> theParams);

View File

@ -0,0 +1,28 @@
package ca.uhn.fhir.jpa.dao;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.persistence.EntityManager;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.param.DateRangeParam;
public interface ISearchBuilder {
Iterator<Long> createQuery(SearchParameterMap theParams);
void setType(Class<? extends IBaseResource> theResourceType, String theResourceName);
void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation, EntityManager theEntityManager,
FhirContext theContext, IDao theDao);
Set<Long> loadReverseIncludes(IDao theCallingDao, FhirContext theContext, EntityManager theEntityManager, Collection<Long> theMatches, Set<Include> theRevIncludes, boolean theReverseMode,
DateRangeParam theLastUpdated);
}

View File

@ -66,9 +66,11 @@ public class JpaValidationSupportDstu2 implements IJpaValidationSupportDstu2 {
String resourceName = myRiCtx.getResourceDefinition(theClass).getName();
IBundleProvider search;
if ("ValueSet".equals(resourceName)) {
search = myValueSetDao.search(ca.uhn.fhir.model.dstu2.resource.ValueSet.SP_URL, new UriParam(theUri));
SearchParameterMap params = new SearchParameterMap(ca.uhn.fhir.model.dstu2.resource.ValueSet.SP_URL, new UriParam(theUri));
params.setLoadSynchronousUpTo(10);
search = myValueSetDao.search(params);
} else if ("StructureDefinition".equals(resourceName)) {
search = myStructureDefinitionDao.search(ca.uhn.fhir.model.dstu2.resource.StructureDefinition.SP_URL, new UriParam(theUri));
search = myStructureDefinitionDao.search(new SearchParameterMap().setLoadSynchronous(true).add(ca.uhn.fhir.model.dstu2.resource.StructureDefinition.SP_URL, new UriParam(theUri)));
} else {
throw new IllegalArgumentException("Can't fetch resource type: " + resourceName);
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.jpa.dao;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/*
* #%L
* HAPI FHIR JPA Server
@ -10,7 +12,7 @@ package ca.uhn.fhir.jpa.dao;
* 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
* 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,
@ -19,24 +21,26 @@ package ca.uhn.fhir.jpa.dao;
* limitations under the License.
* #L%
*/
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.ObjectUtil;
import ca.uhn.fhir.util.UrlUtil;
public class SearchParameterMap extends LinkedHashMap<String, List<List<? extends IQueryParameterType>>> {
@ -46,11 +50,30 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
private EverythingModeEnum myEverythingMode = null;
private Set<Include> myIncludes;
private DateRangeParam myLastUpdated;
private boolean myPersistResults = true;
private RequestDetails myRequestDetails;
private boolean myLoadSynchronous;
private Integer myLoadSynchronousUpTo;
private Set<Include> myRevIncludes;
private SortSpec mySort;
/**
* Constructor
*/
public SearchParameterMap() {
// nothing
}
/**
* Constructor
*/
public SearchParameterMap(String theName, IQueryParameterType theParam) {
add(theName, theParam);
}
public SearchParameterMap add(String theName, DateParam theDateParam) {
add(theName, (IQueryParameterOr<?>) theDateParam);
return this;
}
public void add(String theName, IQueryParameterAnd<?> theAnd) {
if (theAnd == null) {
return;
@ -78,11 +101,11 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
get(theName).add(theOr.getValuesAsQueryTokens());
}
public void add(String theName, IQueryParameterType theParam) {
assert!Constants.PARAM_LASTUPDATED.equals(theName); // this has it's own field in the map
public SearchParameterMap add(String theName, IQueryParameterType theParam) {
assert !Constants.PARAM_LASTUPDATED.equals(theName); // this has it's own field in the map
if (theParam == null) {
return;
return this;
}
if (!containsKey(theName)) {
put(theName, new ArrayList<List<? extends IQueryParameterType>>());
@ -90,6 +113,8 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
ArrayList<IQueryParameterType> list = new ArrayList<IQueryParameterType>();
list.add(theParam);
get(theName).add(list);
return this;
}
public void addInclude(Include theInclude) {
@ -137,8 +162,12 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
return retVal;
}
public RequestDetails getRequestDetails() {
return myRequestDetails;
/**
* If set, tells the server to load these results synchronously, and not to load
* more than X results
*/
public Integer getLoadSynchronousUpTo() {
return myLoadSynchronousUpTo;
}
public Set<Include> getRevIncludes() {
@ -152,8 +181,12 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
return mySort;
}
public boolean isPersistResults() {
return myPersistResults;
/**
* If set, tells the server to load these results synchronously, and not to load
* more than X results
*/
public boolean isLoadSynchronous() {
return myLoadSynchronous;
}
public void setCount(Integer theCount) {
@ -173,14 +206,33 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
}
/**
* Should results be persisted into a table for paging
* If set, tells the server to load these results synchronously, and not to load
* more than X results
*/
public void setPersistResults(boolean thePersistResults) {
myPersistResults = thePersistResults;
public SearchParameterMap setLoadSynchronous(boolean theLoadSynchronous) {
myLoadSynchronous = theLoadSynchronous;
return this;
}
public void setRequestDetails(RequestDetails theRequestDetails) {
myRequestDetails = theRequestDetails;
/**
* If set, tells the server to load these results synchronously, and not to load
* more than X results. Note that setting this to a value will also set
* {@link #setLoadSynchronous(boolean)} to true
*/
public SearchParameterMap setLoadSynchronousUpTo(Integer theLoadSynchronousUpTo) {
myLoadSynchronousUpTo = theLoadSynchronousUpTo;
if (myLoadSynchronousUpTo != null) {
setLoadSynchronous(true);
}
return this;
}
/**
* @deprecated As of HAPI FHIR 2.4 this method no longer does anything
*/
@Deprecated
public void setPersistResults(boolean thePersistResults) {
// does nothing as of HAPI FHIR 2.4
}
public void setRevIncludes(Set<Include> theRevIncludes) {
@ -191,6 +243,140 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
mySort = theSort;
}
public String toNormalizedQueryString(FhirContext theCtx) {
StringBuilder b = new StringBuilder();
ArrayList<String> keys = new ArrayList<String>(keySet());
Collections.sort(keys);
for (String nextKey : keys) {
List<List<? extends IQueryParameterType>> nextValuesAndsIn = get(nextKey);
List<List<IQueryParameterType>> nextValuesAndsOut = new ArrayList<List<IQueryParameterType>>();
for (List<? extends IQueryParameterType> nextValuesAndIn : nextValuesAndsIn) {
List<IQueryParameterType> nextValuesOrsOut = new ArrayList<IQueryParameterType>();
for (IQueryParameterType nextValueOrIn : nextValuesAndIn) {
if (nextValueOrIn.getMissing() != null || isNotBlank(nextValueOrIn.getValueAsQueryToken(theCtx))) {
nextValuesOrsOut.add(nextValueOrIn);
}
}
Collections.sort(nextValuesOrsOut, new QueryParameterTypeComparator(theCtx));
if (nextValuesOrsOut.size() > 0) {
nextValuesAndsOut.add(nextValuesOrsOut);
}
} // for AND
Collections.sort(nextValuesAndsOut, new QueryParameterOrComparator(theCtx));
for (List<IQueryParameterType> nextValuesAnd : nextValuesAndsOut) {
addUrlParamSeparator(b);
IQueryParameterType firstValue = nextValuesAnd.get(0);
b.append(UrlUtil.escape(nextKey));
if (firstValue.getMissing() != null) {
b.append(Constants.PARAMQUALIFIER_MISSING);
b.append('=');
if (firstValue.getMissing()) {
b.append(Constants.PARAMQUALIFIER_MISSING_TRUE);
}else {
b.append(Constants.PARAMQUALIFIER_MISSING_FALSE);
}
continue;
}
if (isNotBlank(firstValue.getQueryParameterQualifier())){
b.append(firstValue.getQueryParameterQualifier());
}
b.append('=');
for (int i = 0; i < nextValuesAnd.size(); i++) {
IQueryParameterType nextValueOr = nextValuesAnd.get(i);
if (i > 0) {
b.append(',');
}
b.append(ParameterUtil.escapeAndUrlEncode(nextValueOr.getValueAsQueryToken(theCtx)));
}
}
} // for keys
SortSpec sort = getSort();
boolean first = true;
while (sort != null) {
if (isNotBlank(sort.getParamName())) {
if (first) {
b.append(Constants.PARAM_SORT);
b.append('=');
first = false;
} else {
b.append(',');
}
if (sort.getOrder() == SortOrderEnum.DESC) {
b.append('-');
}
b.append(sort.getParamName());
}
Validate.isTrue(sort != sort.getChain()); // just in case, shouldn't happen
sort = sort.getChain();
}
addUrlIncludeParams(b, Constants.PARAM_INCLUDE, getIncludes());
addUrlIncludeParams(b, Constants.PARAM_REVINCLUDE, getRevIncludes());
if (getLastUpdated() != null) {
DateParam lb = getLastUpdated().getLowerBound();
addLastUpdateParam(b, lb);
DateParam ub = getLastUpdated().getUpperBound();
addLastUpdateParam(b, ub);
}
if (getCount() != null) {
b.append(Constants.PARAM_COUNT);
b.append('=');
b.append(getCount());
}
return b.toString();
}
private void addLastUpdateParam(StringBuilder b, DateParam date) {
if (isNotBlank(date.getValueAsString())) {
b.append(Constants.PARAM_LASTUPDATED);
b.append('=');
b.append(date.getValueAsString());
}
}
private void addUrlIncludeParams(StringBuilder b, String paramName, Set<Include> list) {
for (Include nextInclude : list) {
b.append(paramName);
b.append('=');
b.append(UrlUtil.escape(nextInclude.getParamType()));
b.append(':');
b.append(UrlUtil.escape(nextInclude.getParamName()));
if (isNotBlank(nextInclude.getParamTargetType())) {
b.append(':');
b.append(nextInclude.getParamTargetType());
}
}
}
private void addUrlParamSeparator(StringBuilder theB) {
if (theB.length() == 0) {
theB.append('?');
} else {
theB.append('&');
}
}
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
@ -203,14 +389,43 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
return b.toString();
}
static int compare(FhirContext theCtx, IQueryParameterType theO1, IQueryParameterType theO2) {
int retVal;
if (theO1.getMissing() == null && theO2.getMissing() == null) {
retVal = 0;
} else if (theO1.getMissing() == null) {
retVal = -1;
} else if (theO2.getMissing() == null) {
retVal = 1;
} else if (ObjectUtil.equals(theO1.getMissing(), theO2.getMissing())) {
retVal = 0;
} else {
if (theO1.getMissing().booleanValue()) {
retVal = 1;
} else {
retVal = -1;
}
}
if (retVal == 0) {
String q1 = theO1.getQueryParameterQualifier();
String q2 = theO2.getQueryParameterQualifier();
retVal = StringUtils.compare(q1, q2);
}
if (retVal == 0) {
String v1 = theO1.getValueAsQueryToken(theCtx);
String v2 = theO2.getValueAsQueryToken(theCtx);
retVal = StringUtils.compare(v1, v2);
}
return retVal;
}
public enum EverythingModeEnum {
/*
* Don't reorder! We rely on the ordinals
*/
ENCOUNTER_INSTANCE(false, true, true),
ENCOUNTER_TYPE(false, true, false),
PATIENT_INSTANCE(true, false, true),
PATIENT_TYPE(true, false, false);
ENCOUNTER_INSTANCE(false, true, true), ENCOUNTER_TYPE(false, true, false), PATIENT_INSTANCE(true, false, true), PATIENT_TYPE(true, false, false);
private final boolean myEncounter;
@ -228,6 +443,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
public boolean isEncounter() {
return myEncounter;
}
public boolean isInstance() {
return myInstance;
}
@ -237,4 +453,34 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
}
}
public class QueryParameterOrComparator implements Comparator<List<IQueryParameterType>> {
private final FhirContext myCtx;
public QueryParameterOrComparator(FhirContext theCtx) {
myCtx = theCtx;
}
@Override
public int compare(List<IQueryParameterType> theO1, List<IQueryParameterType> theO2) {
// These lists will never be empty
return SearchParameterMap.compare(myCtx, theO1.get(0), theO2.get(0));
}
}
public class QueryParameterTypeComparator implements Comparator<IQueryParameterType> {
private final FhirContext myCtx;
public QueryParameterTypeComparator(FhirContext theCtx) {
myCtx = theCtx;
}
@Override
public int compare(IQueryParameterType theO1, IQueryParameterType theO2) {
return SearchParameterMap.compare(myCtx, theO1, theO2);
}
}
}

View File

@ -0,0 +1,34 @@
package ca.uhn.fhir.jpa.dao.data;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2017 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.repository.query.Param;
import ca.uhn.fhir.jpa.entity.SearchParam;
public interface ISearchParamDao extends JpaRepository<SearchParam, Long> {
@Query("SELECT s FROM SearchParam s WHERE s.myResourceName = :resname AND s.myParamName = :parmname")
public SearchParam findForResource(@Param("resname") String theResourceType, @Param("parmname") String theParamName);
}

View File

@ -0,0 +1,38 @@
package ca.uhn.fhir.jpa.dao.data;
import java.util.Collection;
import java.util.Date;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2017 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.repository.query.Param;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.SearchParamPresent;
public interface ISearchParamPresentDao extends JpaRepository<SearchParamPresent, Long> {
@Query("SELECT s FROM SearchParamPresent s WHERE s.myResource = :res")
public Collection<SearchParamPresent> findAllForResource(@Param("res") ResourceTable theResource);
}

View File

@ -69,9 +69,7 @@ public class FhirResourceDaoPatientDstu3 extends FhirResourceDaoDstu3<Patient>im
paramMap.add("_id", new StringParam(theId.getIdPart()));
}
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myPlatformTransactionManager, mySearchDao, mySearchResultDao, this, myResourceIndexedSearchParamUriDao, myForcedIdDao, myTerminologySvc, mySerarchParamRegistry);
builder.setType(getResourceType(), getResourceName());
return builder.search(paramMap);
return mySearchCoordinatorSvc.registerSearch(this, paramMap, getResourceName());
}
@Override

View File

@ -28,6 +28,11 @@ import org.hl7.fhir.dstu3.model.Meta;
import org.hl7.fhir.dstu3.model.SearchParameter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import ca.uhn.fhir.jpa.dao.BaseSearchParamExtractor;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter;
@ -51,9 +56,18 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
protected void markAffectedResources(SearchParameter theResource) {
if (theResource != null) {
String expression = theResource.getExpression();
String resourceType = expression.substring(0, expression.indexOf('.'));
final String resourceType = expression.substring(0, expression.indexOf('.'));
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", expression);
int updatedCount = myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
int updatedCount = txTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus theStatus) {
return myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
}
});
ourLog.info("Marked {} resources for reindexing", updatedCount);
}

View File

@ -38,6 +38,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.IBundleProvider;
@ -92,19 +93,40 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3 {
IBundleProvider search;
if ("ValueSet".equals(resourceName)) {
if (localReference) {
search = myValueSetDao.search(IAnyResource.SP_RES_ID, new StringParam(theUri));
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(IAnyResource.SP_RES_ID, new StringParam(theUri));
search = myValueSetDao.search(params);
if (search.size() == 0) {
params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(ValueSet.SP_URL, new UriParam(theUri));
search = myValueSetDao.search(params);
}
} else {
search = myValueSetDao.search(ValueSet.SP_URL, new UriParam(theUri));
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(ValueSet.SP_URL, new UriParam(theUri));
search = myValueSetDao.search(params);
}
} else if ("StructureDefinition".equals(resourceName)) {
if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
return null;
}
search = myStructureDefinitionDao.search(StructureDefinition.SP_URL, new UriParam(theUri));
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(StructureDefinition.SP_URL, new UriParam(theUri));
search = myStructureDefinitionDao.search(params);
} else if ("Questionnaire".equals(resourceName)) {
search = myQuestionnaireDao.search(IAnyResource.SP_RES_ID, new StringParam(id.getIdPart()));
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(IAnyResource.SP_RES_ID, new StringParam(id.getIdPart()));
search = myQuestionnaireDao.search(params);
} else if ("CodeSystem".equals(resourceName)) {
search = myCodeSystemDao.search(CodeSystem.SP_URL, new UriParam(theUri));
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(1);
params.add(CodeSystem.SP_URL, new UriParam(theUri));
search = myCodeSystemDao.search(params);
} else {
throw new IllegalArgumentException("Can't fetch resource type: " + resourceName);
}

View File

@ -482,6 +482,11 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
// needContactPointSystem = "email";
// }
if (nextPath.contains(".where(system='email')")) {
ourLog.info("Email"); // FIXME: remove
}
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject instanceof Extension) {

View File

@ -100,7 +100,10 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
}
}
IBundleProvider allSearchParamsBp = mySpDao.search(new SearchParameterMap());
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronous(true);
IBundleProvider allSearchParamsBp = mySpDao.search(params);
int size = allSearchParamsBp.size();
// Just in case..

View File

@ -30,6 +30,8 @@ import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.Columns;
import org.hibernate.search.annotations.ContainedIn;
import org.hibernate.search.annotations.Field;
@ -40,6 +42,11 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
private static final long serialVersionUID = 1L;
@Field()
@Column(name = "SP_MISSING", nullable = true)
@ColumnDefault("false")
private boolean myMissing;
@Field
@Column(name = "SP_NAME", length = MAX_SP_NAME, nullable = false)
private String myParamName;
@ -83,6 +90,14 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
return myUpdated;
}
public boolean isMissing() {
return myMissing;
}
public void setMissing(boolean theMissing) {
myMissing = theMissing;
}
public void setParamName(String theName) {
myParamName = theName;
}

View File

@ -42,7 +42,6 @@ import org.hibernate.search.annotations.Field;
import ca.uhn.fhir.model.primitive.InstantDt;
//@formatter:off
@Embeddable
@Entity
@Table(name = "HFJ_SPIDX_DATE", indexes= {
@ -50,7 +49,6 @@ import ca.uhn.fhir.model.primitive.InstantDt;
@Index(name = "IDX_SP_DATE_UPDATED", columnList = "SP_UPDATED"),
@Index(name = "IDX_SP_DATE_RESID", columnList = "RES_ID")
})
//@formatter:on
public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchParam {
private static final long serialVersionUID = 1L;

View File

@ -236,6 +236,9 @@ public class ResourceTable extends BaseHasResource implements Serializable {
@OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private Set<ResourceTag> myTags;
@OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private Collection<SearchParamPresent> mySearchParamPresents;
@Column(name = "RES_VER")
private long myVersion;

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.jpa.entity;
import static org.apache.commons.lang3.StringUtils.left;
/*
* #%L
* HAPI FHIR JPA Server
@ -28,20 +30,7 @@ import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
import javax.persistence.*;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.param.DateRangeParam;
@ -51,17 +40,26 @@ import ca.uhn.fhir.rest.param.DateRangeParam;
@Table(name = "HFJ_SEARCH", uniqueConstraints= {
@UniqueConstraint(name="IDX_SEARCH_UUID", columnNames="SEARCH_UUID")
}, indexes= {
@Index(name="JDX_SEARCH_CREATED", columnList="CREATED")
@Index(name="JDX_SEARCH_CREATED", columnList="CREATED"),
@Index(name="JDX_SEARCH_STRING", columnList="SEARCH_STRING")
})
//@formatter:on
public class Search implements Serializable {
private static final int FAILURE_MESSAGE_LENGTH = 500;
private static final long serialVersionUID = 1L;
@Temporal(TemporalType.TIMESTAMP)
@Column(name="CREATED", nullable=false, updatable=false)
private Date myCreated;
@Column(name="FAILURE_CODE", nullable=true)
private Integer myFailureCode;
@Column(name="FAILURE_MESSAGE", length=FAILURE_MESSAGE_LENGTH, nullable=true)
private String myFailureMessage;
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator="SEQ_SEARCH")
@SequenceGenerator(name="SEQ_SEARCH", sequenceName="SEQ_SEARCH")
@ -78,24 +76,34 @@ public class Search implements Serializable {
@Temporal(TemporalType.TIMESTAMP)
@Column(name="LAST_UPDATED_LOW", nullable=true, insertable=true, updatable=false)
private Date myLastUpdatedLow;
@Column(name="NUM_FOUND", nullable=false)
private int myNumFound;
@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;
@Column(name="SEARCH_STRING", length=1000, nullable=true)
private String mySearchString;
@Enumerated(EnumType.ORDINAL)
@Column(name="SEARCH_TYPE", nullable=false)
private SearchTypeEnum mySearchType;
@Column(name="TOTAL_COUNT", nullable=false)
@Enumerated(EnumType.STRING)
@Column(name="SEARCH_STATUS", nullable=false, length=10)
private SearchStatusEnum myStatus;
@Column(name="TOTAL_COUNT", nullable=true)
private Integer myTotalCount;
@Column(name="SEARCH_UUID", length=40, nullable=false, updatable=false)
@ -105,6 +113,14 @@ public class Search implements Serializable {
return myCreated;
}
public Integer getFailureCode() {
return myFailureCode;
}
public String getFailureMessage() {
return myFailureMessage;
}
public Long getId() {
return myId;
}
@ -115,14 +131,6 @@ public class Search implements Serializable {
}
return myIncludes;
}
public Date getLastUpdatedHigh() {
return myLastUpdatedHigh;
}
public Date getLastUpdatedLow() {
return myLastUpdatedLow;
}
public DateRangeParam getLastUpdated() {
if (myLastUpdatedLow == null && myLastUpdatedHigh == null) {
@ -132,6 +140,18 @@ public class Search implements Serializable {
}
}
public Date getLastUpdatedHigh() {
return myLastUpdatedHigh;
}
public Date getLastUpdatedLow() {
return myLastUpdatedLow;
}
public int getNumFound() {
return myNumFound;
}
public Integer getPreferredPageSize() {
return myPreferredPageSize;
}
@ -139,15 +159,18 @@ public class Search implements Serializable {
public Long getResourceId() {
return myResourceId;
}
public String getResourceType() {
return myResourceType;
}
public SearchTypeEnum getSearchType() {
return mySearchType;
}
public SearchStatusEnum getStatus() {
return myStatus;
}
public Integer getTotalCount() {
return myTotalCount;
@ -160,11 +183,21 @@ public class Search implements Serializable {
public void setCreated(Date theCreated) {
myCreated = theCreated;
}
public void setFailureCode(Integer theFailureCode) {
myFailureCode = theFailureCode;
}
public void setFailureMessage(String theFailureMessage) {
myFailureMessage = left(theFailureMessage, FAILURE_MESSAGE_LENGTH);
}
public void setLastUpdated(Date theLowerBound, Date theUpperBound) {
myLastUpdatedLow = theLowerBound;
myLastUpdatedHigh = theUpperBound;
}
public void setLastUpdated(DateRangeParam theLastUpdated) {
if (theLastUpdated == null) {
myLastUpdatedLow = null;
@ -174,24 +207,31 @@ public class Search implements Serializable {
myLastUpdatedHigh = theLastUpdated.getUpperBoundAsInstant();
}
}
public void setNumFound(int theNumFound) {
myNumFound = theNumFound;
}
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 setStatus(SearchStatusEnum theStatus) {
myStatus = theStatus;
}
public void setTotalCount(Integer theTotalCount) {
myTotalCount = theTotalCount;
}

View File

@ -0,0 +1,36 @@
package ca.uhn.fhir.jpa.entity;
import javax.persistence.*;
@Entity
@Table(name = "HFJ_SEARCH_PARM", uniqueConstraints= {
@UniqueConstraint(name="IDX_SEARCHPARM_RESTYPE_SPNAME", columnNames= {"RES_TYPE", "PARAM_NAME"})
})
public class SearchParam {
@Id
@SequenceGenerator(name = "SEQ_SEARCHPARM_ID", sequenceName = "SEQ_SEARCHPARM_ID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SEARCHPARM_ID")
@Column(name = "PID")
private Long myId;
@Column(name="PARAM_NAME", length=BaseResourceIndexedSearchParam.MAX_SP_NAME, nullable=false, updatable=false)
private String myParamName;
@Column(name="RES_TYPE", length=ResourceTable.RESTYPE_LEN, nullable=false, updatable=false)
private String myResourceName;
public String getParamName() {
return myParamName;
}
public void setParamName(String theParamName) {
myParamName = theParamName;
}
public void setResourceName(String theResourceName) {
myResourceName = theResourceName;
}
}

View File

@ -0,0 +1,71 @@
package ca.uhn.fhir.jpa.entity;
import java.io.Serializable;
import javax.persistence.*;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@Entity
@Table(name = "HFJ_RES_PARAM_PRESENT", indexes = {
@Index(name = "IDX_RESPARMPRESENT_RESID", columnList = "RES_ID")
}, uniqueConstraints = {
@UniqueConstraint(name = "IDX_RESPARMPRESENT_SPID_RESID", columnNames = { "SP_ID", "RES_ID" })
})
public class SearchParamPresent implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@SequenceGenerator(name = "SEQ_RESPARMPRESENT_ID", sequenceName = "SEQ_RESPARMPRESENT_ID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESPARMPRESENT_ID")
@Column(name = "PID")
private Long myId;
@Column(name = "SP_PRESENT", nullable = false)
private boolean myPresent;
@ManyToOne()
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, foreignKey = @ForeignKey(name = "FK_RESPARMPRES_RESID"))
private ResourceTable myResource;
@ManyToOne()
@JoinColumn(name = "SP_ID", referencedColumnName = "PID", nullable = false, foreignKey = @ForeignKey(name = "FK_RESPARMPRES_SPID"))
private SearchParam mySearchParam;
public ResourceTable getResource() {
return myResource;
}
public SearchParam getSearchParam() {
return mySearchParam;
}
public boolean isPresent() {
return myPresent;
}
public void setPresent(boolean thePresent) {
myPresent = thePresent;
}
public void setResource(ResourceTable theResourceTable) {
myResource = theResourceTable;
}
public void setSearchParam(SearchParam theSearchParam) {
mySearchParam = theSearchParam;
}
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
b.append("res_pid", myResource.getIdDt().toUnqualifiedVersionless().getValue());
b.append("param", mySearchParam.getParamName());
b.append("present", myPresent);
return b.build();
}
}

View File

@ -106,7 +106,8 @@ public class SearchResult implements Serializable {
myOrder = theOrder;
}
public void setResourcePid(Long theResourcePid) {
public SearchResult setResourcePid(Long theResourcePid) {
myResourcePid = theResourcePid;
return this;
}
}

View File

@ -0,0 +1,9 @@
package ca.uhn.fhir.jpa.entity;
public enum SearchStatusEnum {
LOADING,
FINISHED,
FAILED
}

View File

@ -30,6 +30,16 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
public class ServletSubRequestDetails extends ServletRequestDetails {
private Map<String, ArrayList<String>> myHeaders = new HashMap<String, ArrayList<String>>();
public void addHeader(String theName, String theValue) {
String lowerCase = theName.toLowerCase();
ArrayList<String> list = myHeaders.get(lowerCase);
if (list == null) {
list = new ArrayList<String>();
myHeaders.put(lowerCase, list);
}
list.add(theValue);
}
@Override
public String getHeader(String theName) {
@ -49,14 +59,9 @@ public class ServletSubRequestDetails extends ServletRequestDetails {
return list;
}
public void addHeader(String theName, String theValue) {
String lowerCase = theName.toLowerCase();
ArrayList<String> list = myHeaders.get(lowerCase);
if (list == null) {
list = new ArrayList<String>();
myHeaders.put(lowerCase, list);
}
list.add(theValue);
@Override
public boolean isSubRequest() {
return true;
}
}

View File

@ -39,15 +39,22 @@ import ca.uhn.fhir.rest.server.IPagingProvider;
public class DatabaseBackedPagingProvider extends BasePagingProvider implements IPagingProvider {
@Autowired
private PlatformTransactionManager thePlatformTransactionManager;
private FhirContext myContext;
@Autowired
private ISearchResultDao theSearchResultDao;
private IFhirSystemDao<?, ?> myDao;
@Autowired
private EntityManager theEntityManager;
private EntityManager myEntityManager;
@Autowired
private FhirContext theContext;
private PlatformTransactionManager myPlatformTransactionManager;
@Autowired
private IFhirSystemDao<?, ?> theDao;
private ISearchResultDao mySearchResultDao;
/**
* Constructor
*/
public DatabaseBackedPagingProvider() {
super();
}
/**
* Constructor
@ -58,16 +65,9 @@ public class DatabaseBackedPagingProvider extends BasePagingProvider implements
this();
}
/**
* Constructor
*/
public DatabaseBackedPagingProvider() {
super();
}
@Override
public synchronized IBundleProvider retrieveResultList(String theId) {
PersistedJpaBundleProvider provider = new PersistedJpaBundleProvider(theId, theDao);
PersistedJpaBundleProvider provider = new PersistedJpaBundleProvider(theId, myDao);
if (!provider.ensureSearchEntityLoaded()) {
return null;
}

View File

@ -0,0 +1,17 @@
package ca.uhn.fhir.jpa.search;
import java.util.List;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.jpa.dao.IDao;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.server.IBundleProvider;
public interface ISearchCoordinatorSvc {
List<Long> getResources(String theUuid, int theFrom, int theTo);
IBundleProvider registerSearch(IDao theCallingDao, SearchParameterMap theParams, String theResourceType);
}

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.jpa.search;
* 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
* 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,
@ -30,7 +30,6 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.PlatformTransactionManager;
@ -40,22 +39,24 @@ 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.ISearchBuilder;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.entity.*;
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.SearchTypeEnum;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.server.IBundleProvider;
public final class PersistedJpaBundleProvider implements IBundleProvider {
public class PersistedJpaBundleProvider implements IBundleProvider {
private FhirContext myContext;
private IDao myDao;
private EntityManager myEntityManager;
private PlatformTransactionManager myPlatformTransactionManager;
private ISearchCoordinatorSvc mySearchCoordinatorSvc;
private ISearchDao mySearchDao;
private Search mySearchEntity;
private ISearchResultDao mySearchResultDao;
private String myUuid;
public PersistedJpaBundleProvider(String theSearchUuid, IDao theDao) {
@ -70,7 +71,7 @@ public final class PersistedJpaBundleProvider implements IBundleProvider {
CriteriaQuery<ResourceHistoryTable> q = cb.createQuery(ResourceHistoryTable.class);
Root<ResourceHistoryTable> from = q.from(ResourceHistoryTable.class);
List<Predicate> predicates = new ArrayList<Predicate>();
if (mySearchEntity.getResourceType() == null) {
// All resource types
} else if (mySearchEntity.getResourceId() == null) {
@ -85,22 +86,22 @@ public final class PersistedJpaBundleProvider implements IBundleProvider {
if (mySearchEntity.getLastUpdatedHigh() != null) {
predicates.add(cb.lessThanOrEqualTo(from.get("myUpdated").as(Date.class), mySearchEntity.getLastUpdatedHigh()));
}
if (predicates.size() > 0) {
q.where(predicates.toArray(new Predicate[predicates.size()]));
}
q.orderBy(cb.desc(from.get("myUpdated")));
TypedQuery<ResourceHistoryTable> query = myEntityManager.createQuery(q);
if (theToIndex - theFromIndex > 0) {
query.setFirstResult(theFromIndex);
query.setMaxResults(theToIndex - theFromIndex);
}
results = query.getResultList();
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
for (ResourceHistoryTable next : results) {
BaseHasResource resource;
@ -113,33 +114,21 @@ public final class PersistedJpaBundleProvider implements IBundleProvider {
}
protected List<IBaseResource> doSearchOrEverythingInTransaction(final int theFromIndex, final int theToIndex) {
ISearchBuilder sb = myDao.newSearchBuilder();
Pageable page = toPage(theFromIndex, theToIndex);
if (page == null) {
return Collections.emptyList();
String resourceName = mySearchEntity.getResourceType();
Class<? extends IBaseResource> resourceType = myContext.getResourceDefinition(resourceName).getImplementingClass();
sb.setType(resourceType, resourceName);
List<Long> pidsSubList = mySearchCoordinatorSvc.getResources(myUuid, theFromIndex, theToIndex);
return toResourceList(sb, pidsSubList);
}
private void ensureDependenciesInjected() {
if (myPlatformTransactionManager == null) {
myDao.injectDependenciesIntoBundleProvider(this);
}
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(myDao, myContext, myEntityManager, pidsSubList, mySearchEntity.toRevIncludesList(), true, mySearchEntity.getLastUpdated()));
}
revIncludedPids.addAll(SearchBuilder.loadReverseIncludes(myDao, 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;
}
/**
@ -155,7 +144,7 @@ public final class PersistedJpaBundleProvider implements IBundleProvider {
@Override
public Boolean doInTransaction(TransactionStatus theStatus) {
try {
mySearchEntity = mySearchDao.findByUuid(myUuid);
setSearchEntity(mySearchDao.findByUuid(myUuid));
if (mySearchEntity == null) {
return false;
@ -174,12 +163,6 @@ public final class PersistedJpaBundleProvider implements IBundleProvider {
return true;
}
private void ensureDependenciesInjected() {
if (myPlatformTransactionManager == null) {
myDao.injectDependenciesIntoBundleProvider(this);
}
}
@Override
public InstantDt getPublished() {
ensureSearchEntityLoaded();
@ -232,36 +215,40 @@ public final class PersistedJpaBundleProvider implements IBundleProvider {
myPlatformTransactionManager = thePlatformTransactionManager;
}
public void setSearchCoordinatorSvc(ISearchCoordinatorSvc theSearchCoordinatorSvc) {
mySearchCoordinatorSvc = theSearchCoordinatorSvc;
}
public void setSearchDao(ISearchDao theSearchDao) {
mySearchDao = theSearchDao;
}
public void setSearchResultDao(ISearchResultDao theSearchResultDao) {
mySearchResultDao = theSearchResultDao;
protected void setSearchEntity(Search theSearchEntity) {
mySearchEntity = theSearchEntity;
}
@Override
public int size() {
public Integer size() {
ensureSearchEntityLoaded();
return Math.max(0, mySearchEntity.getTotalCount());
}
public static Pageable toPage(final int theFromIndex, int theToIndex) {
int pageSize = theToIndex - theFromIndex;
if (pageSize < 1) {
Integer size = mySearchEntity.getTotalCount();
if (size == null) {
return null;
}
int pageIndex = theFromIndex / pageSize;
Pageable page = new PageRequest(pageIndex, pageSize) {
private static final long serialVersionUID = 1L;
@Override
public int getOffset() {
return theFromIndex;
}};
return page;
return Math.max(0, size);
}
protected List<IBaseResource> toResourceList(ISearchBuilder sb, List<Long> pidsSubList) {
Set<Long> includedPids = new HashSet<Long>();
if (mySearchEntity.getSearchType() == SearchTypeEnum.SEARCH) {
includedPids.addAll(sb.loadReverseIncludes(myDao, myContext, myEntityManager, pidsSubList, mySearchEntity.toRevIncludesList(), true, mySearchEntity.getLastUpdated()));
}
includedPids.addAll(sb.loadReverseIncludes(myDao, myContext, myEntityManager, pidsSubList, mySearchEntity.toIncludesList(), false, mySearchEntity.getLastUpdated()));
// Execute the query and make sure we return distinct results
List<IBaseResource> resources = new ArrayList<IBaseResource>();
sb.loadResourcesByPid(pidsSubList, resources, includedPids, false, myEntityManager, myContext, myDao);
return resources;
}
}

View File

@ -0,0 +1,55 @@
package ca.uhn.fhir.jpa.search;
import java.util.List;
import org.hl7.fhir.instance.model.api.IBaseResource;
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.jpa.dao.IDao;
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.entity.SearchStatusEnum;
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl.SearchTask;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundleProvider {
private SearchTask mySearchTask;
private ISearchBuilder mySearchBuilder;
private Search mySearch;
private PlatformTransactionManager myTxManager;
public PersistedJpaSearchFirstPageBundleProvider(Search theSearch, IDao theDao, SearchTask theSearchTask, ISearchBuilder theSearchBuilder, PlatformTransactionManager theTxManager) {
super(theSearch.getUuid(), theDao);
setSearchEntity(theSearch);
mySearchTask = theSearchTask;
mySearchBuilder = theSearchBuilder;
mySearch = theSearch;
myTxManager = theTxManager;
}
@Override
public List<IBaseResource> getResources(int theFromIndex, int theToIndex) {
SearchCoordinatorSvcImpl.verifySearchHasntFailedOrThrowInternalErrorException(mySearch);
final List<Long> pids = mySearchTask.getResourcePids(theFromIndex, theToIndex);
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
return txTemplate.execute(new TransactionCallback<List<IBaseResource>>() {
@Override
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
return toResourceList(mySearchBuilder, pids);
}});
}
@Override
public Integer size() {
mySearchTask.awaitInitialSync();
SearchCoordinatorSvcImpl.verifySearchHasntFailedOrThrowInternalErrorException(mySearch);
return super.size();
}
}

View File

@ -0,0 +1,521 @@
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.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import javax.transaction.Transactional.TxType;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.time.DateUtils;
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.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IDao;
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
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.SearchStatusEnum;
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.method.PageMethodBinding;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
static final int DEFAULT_SYNC_SIZE = 250;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchCoordinatorSvcImpl.class);
@Autowired
private FhirContext myContext;
@Autowired
private EntityManager myEntityManager;
private ExecutorService myExecutor;
private final ConcurrentHashMap<String, SearchTask> myIdToSearchTask = new ConcurrentHashMap<String, SearchTask>();
private Integer myLoadingThrottleForUnitTests = null;
private long myMaxMillisToWaitForRemoteResults = DateUtils.MILLIS_PER_MINUTE;
private boolean myNeverUseLocalSearchForUnitTests;
@Autowired
private ISearchDao mySearchDao;
@Autowired
private ISearchIncludeDao mySearchIncludeDao;
@Autowired
private ISearchResultDao mySearchResultDao;
private int mySyncSize = DEFAULT_SYNC_SIZE;
@Autowired
private PlatformTransactionManager myTxManager;
/**
* Constructor
*/
public SearchCoordinatorSvcImpl() {
CustomizableThreadFactory threadFactory = new CustomizableThreadFactory("search_coord_");
myExecutor = Executors.newCachedThreadPool(threadFactory);
}
@Override
@Transactional(value = TxType.NOT_SUPPORTED)
public List<Long> getResources(final String theUuid, int theFrom, int theTo) {
if (myNeverUseLocalSearchForUnitTests == false) {
SearchTask task = myIdToSearchTask.get(theUuid);
if (task != null) {
return task.getResourcePids(theFrom, theTo);
}
}
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
Search search;
StopWatch sw = new StopWatch();
while (true) {
search = txTemplate.execute(new TransactionCallback<Search>() {
@Override
public Search doInTransaction(TransactionStatus theStatus) {
return mySearchDao.findByUuid(theUuid);
}
});
if (search == null) {
ourLog.info("Client requested unknown paging ID[{}]", theUuid);
String msg = myContext.getLocalizer().getMessage(PageMethodBinding.class, "unknownSearchId", theUuid);
throw new ResourceGoneException(msg);
}
verifySearchHasntFailedOrThrowInternalErrorException(search);
if (search.getStatus() == SearchStatusEnum.FINISHED) {
ourLog.info("Search entity marked as finished");
break;
}
if (search.getNumFound() >= theTo) {
ourLog.info("Search entity has {} results so far", search.getNumFound());
break;
}
if (sw.getMillis() > myMaxMillisToWaitForRemoteResults) {
throw new InternalErrorException("Request timed out after " + sw.getMillis() + "ms");
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// ignore
}
}
final Pageable page = toPage(theFrom, theTo);
if (page == null) {
return Collections.emptyList();
}
final Search foundSearch = search;
List<Long> retVal = txTemplate.execute(new TransactionCallback<List<Long>>() {
@Override
public List<Long> doInTransaction(TransactionStatus theStatus) {
final List<Long> resultPids = new ArrayList<Long>();
Page<SearchResult> searchResults = mySearchResultDao.findWithSearchUuid(foundSearch, page);
for (SearchResult next : searchResults) {
resultPids.add(next.getResourcePid());
}
return resultPids; }
});
return retVal;
}
@Override
public IBundleProvider registerSearch(IDao theCallingDao, SearchParameterMap theParams, String theResourceType) {
StopWatch w = new StopWatch();
Class<? extends IBaseResource> resourceTypeClass = myContext.getResourceDefinition(theResourceType).getImplementingClass();
ISearchBuilder sb = theCallingDao.newSearchBuilder();
sb.setType(resourceTypeClass, theResourceType);
if (theParams.isLoadSynchronous()) {
// Load the results synchronously
List<Long> pids = new ArrayList<Long>();
Iterator<Long> resultIter = sb.createQuery(theParams);
while (resultIter.hasNext()) {
pids.add(resultIter.next());
if (theParams.getLoadSynchronousUpTo() != null && pids.size() >= theParams.getLoadSynchronousUpTo()) {
break;
}
}
/*
* For synchronous queries, we load all the includes right away
* since we're returning a static bundle with all the results
* pre-loaded. This is ok because syncronous requests are not
* expected to be paged
*
* On the other hand for async queries we load includes/revincludes
* individually for pages as we return them to clients
*/
Set<Long> includedPids = new HashSet<Long>();
includedPids.addAll(sb.loadReverseIncludes(theCallingDao, myContext, myEntityManager, pids, theParams.getRevIncludes(), true, theParams.getLastUpdated()));
includedPids.addAll(sb.loadReverseIncludes(theCallingDao, myContext, myEntityManager, pids, theParams.getIncludes(), false, theParams.getLastUpdated()));
// Execute the query and make sure we return distinct results
List<IBaseResource> resources = new ArrayList<IBaseResource>();
sb.loadResourcesByPid(pids, resources, includedPids, false, myEntityManager, myContext, theCallingDao);
return new SimpleBundleProvider(resources);
}
Search search = new Search();
search.setUuid(UUID.randomUUID().toString());
search.setCreated(new Date());
search.setTotalCount(null);
search.setNumFound(0);
search.setPreferredPageSize(theParams.getCount());
search.setSearchType(theParams.getEverythingMode() != null ? SearchTypeEnum.EVERYTHING : SearchTypeEnum.SEARCH);
search.setLastUpdated(theParams.getLastUpdated());
search.setResourceType(theResourceType);
search.setStatus(SearchStatusEnum.LOADING);
for (Include next : theParams.getIncludes()) {
search.getIncludes().add(new SearchInclude(search, next.getValue(), false, next.isRecurse()));
}
for (Include next : theParams.getRevIncludes()) {
search.getIncludes().add(new SearchInclude(search, next.getValue(), true, next.isRecurse()));
}
SearchTask task = new SearchTask(search, theCallingDao, theParams, theResourceType);
myIdToSearchTask.put(search.getUuid(), task);
myExecutor.submit(task);
PersistedJpaSearchFirstPageBundleProvider retVal = new PersistedJpaSearchFirstPageBundleProvider(search, theCallingDao, task, sb, myTxManager);
retVal.setContext(myContext);
retVal.setEntityManager(myEntityManager);
retVal.setPlatformTransactionManager(myTxManager);
retVal.setSearchDao(mySearchDao);
retVal.setSearchCoordinatorSvc(this);
ourLog.info("Search initial phase completed in {}ms", w);
return retVal;
}
@VisibleForTesting
void setContextForUnitTest(FhirContext theCtx) {
myContext = theCtx;
}
@VisibleForTesting
void setEntityManagerForUnitTest(EntityManager theEntityManager) {
myEntityManager = theEntityManager;
}
@VisibleForTesting
void setLoadingThrottleForUnitTests(Integer theLoadingThrottleForUnitTests) {
myLoadingThrottleForUnitTests = theLoadingThrottleForUnitTests;
}
@VisibleForTesting
void setMaxMillisToWaitForRemoteResultsForUnitTest(long theMaxMillisToWaitForRemoteResults) {
myMaxMillisToWaitForRemoteResults = theMaxMillisToWaitForRemoteResults;
}
void setNeverUseLocalSearchForUnitTests(boolean theNeverUseLocalSearchForUnitTests) {
myNeverUseLocalSearchForUnitTests = theNeverUseLocalSearchForUnitTests;
}
@VisibleForTesting
void setSearchDaoForUnitTest(ISearchDao theSearchDao) {
mySearchDao = theSearchDao;
}
@VisibleForTesting
void setSearchDaoIncludeForUnitTest(ISearchIncludeDao theSearchIncludeDao) {
mySearchIncludeDao = theSearchIncludeDao;
}
@VisibleForTesting
void setSearchDaoResultForUnitTest(ISearchResultDao theSearchResultDao) {
mySearchResultDao = theSearchResultDao;
}
@VisibleForTesting
void setSyncSizeForUnitTests(int theSyncSize) {
mySyncSize = theSyncSize;
}
@VisibleForTesting
void setTransactionManagerForUnitTest(PlatformTransactionManager theTxManager) {
myTxManager = theTxManager;
}
static Pageable toPage(final int theFromIndex, int theToIndex) {
int pageSize = theToIndex - theFromIndex;
if (pageSize < 1) {
return null;
}
int pageIndex = theFromIndex / pageSize;
Pageable page = new PageRequest(pageIndex, pageSize) {
private static final long serialVersionUID = 1L;
@Override
public int getOffset() {
return theFromIndex;
}
};
return page;
}
static void verifySearchHasntFailedOrThrowInternalErrorException(Search theSearch) {
if (theSearch.getStatus() == SearchStatusEnum.FAILED) {
Integer status = theSearch.getFailureCode();
status = ObjectUtils.defaultIfNull(status, 500);
String message = theSearch.getFailureMessage();
throw BaseServerResponseException.newInstance(status, message);
}
}
public class SearchTask implements Callable<Void> {
private final IDao myCallingDao;
private int myCountSaved = 0;
private final CountDownLatch myInitialCollectionLatch = new CountDownLatch(1);
private SearchParameterMap myParams;
private String myResourceType;
private final Search mySearch;
private final ArrayList<Long> mySyncedPids = new ArrayList<Long>();
private final ArrayList<Long> myUnsyncedPids = new ArrayList<Long>();
public SearchTask(Search theSearch, IDao theCallingDao, SearchParameterMap theParams, String theResourceType) {
mySearch = theSearch;
myCallingDao = theCallingDao;
myParams = theParams;
myResourceType = theResourceType;
}
public void awaitInitialSync() {
ourLog.trace("Awaiting initial sync");
do {
try {
if (myInitialCollectionLatch.await(250, TimeUnit.MILLISECONDS)) {
break;
}
} catch (InterruptedException e) {
throw new InternalErrorException(e);
}
} while (mySearch.getStatus() == SearchStatusEnum.LOADING);
ourLog.trace("Initial sync completed");
}
@Override
public Void call() throws Exception {
StopWatch sw = new StopWatch();
try {
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
doSearch();
}
});
ourLog.info("Completed search for {} resources in {}ms", mySyncedPids.size(), sw.getMillis());
} catch (Throwable t) {
ourLog.error("Failed during search loading after {}ms", sw.getMillis(), t);
myUnsyncedPids.clear();
Throwable rootCause = ExceptionUtils.getRootCause(t);
rootCause = ObjectUtils.defaultIfNull(rootCause, t);
String failureMessage = rootCause.getMessage();
int failureCode = InternalErrorException.STATUS_CODE;
if (t instanceof BaseServerResponseException) {
failureCode = ((BaseServerResponseException) t).getStatusCode();
}
mySearch.setFailureMessage(failureMessage);
mySearch.setFailureCode(failureCode);
mySearch.setStatus(SearchStatusEnum.FAILED);
saveSearch();
}
myIdToSearchTask.remove(mySearch.getUuid());
return null;
}
private void doSaveSearch() {
if (mySearch.getId() == null) {
mySearchDao.save(mySearch);
for (SearchInclude next : mySearch.getIncludes()) {
mySearchIncludeDao.save(next);
}
} else {
mySearchDao.save(mySearch);
}
}
private void doSearch() {
Class<? extends IBaseResource> resourceTypeClass = myContext.getResourceDefinition(myResourceType).getImplementingClass();
ISearchBuilder sb = myCallingDao.newSearchBuilder();
sb.setType(resourceTypeClass, myResourceType);
Iterator<Long> theResultIter = sb.createQuery(myParams);
while (theResultIter.hasNext()) {
myUnsyncedPids.add(theResultIter.next());
if (myUnsyncedPids.size() >= mySyncSize) {
saveUnsynced(theResultIter);
}
if (myLoadingThrottleForUnitTests != null) {
try {
Thread.sleep(myLoadingThrottleForUnitTests);
} catch (InterruptedException e) {
// ignore
}
}
}
saveUnsynced(theResultIter);
}
public List<Long> getResourcePids(int theFromIndex, int theToIndex) {
ourLog.info("Requesting search PIDs from {}-{}", theFromIndex, theToIndex);
CountDownLatch latch = null;
synchronized (mySyncedPids) {
if (mySyncedPids.size() < theToIndex && mySearch.getStatus() == SearchStatusEnum.LOADING) {
int latchSize = theToIndex - mySyncedPids.size();
ourLog.trace("Registering latch to await {} results (want {} total)", latchSize, theToIndex);
latch = new CountDownLatch(latchSize);
}
}
if (latch != null) {
while (latch.getCount() > 0 && mySearch.getStatus() == SearchStatusEnum.LOADING) {
try {
ourLog.trace("Awaiting latch with {}", latch.getCount());
latch.await(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
// ok
}
}
}
ArrayList<Long> retVal = new ArrayList<Long>();
synchronized (mySyncedPids) {
verifySearchHasntFailedOrThrowInternalErrorException(mySearch);
int toIndex = theToIndex;
if (mySyncedPids.size() < toIndex) {
toIndex = mySyncedPids.size();
}
for (int i = theFromIndex; i < toIndex; i++) {
retVal.add(mySyncedPids.get(i));
}
}
return retVal;
}
private void saveSearch() {
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
doSaveSearch();
}
});
}
private void saveUnsynced(final Iterator<Long> theResultIter) {
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
if (mySearch.getId() == null) {
doSaveSearch();
}
List<SearchResult> resultsToSave = Lists.newArrayList();
for (Long nextPid : myUnsyncedPids) {
SearchResult nextResult = new SearchResult(mySearch);
nextResult.setResourcePid(nextPid);
nextResult.setOrder(myCountSaved++);
resultsToSave.add(nextResult);
}
mySearchResultDao.save(resultsToSave);
synchronized (mySyncedPids) {
int numSyncedThisPass = myUnsyncedPids.size();
ourLog.trace("Syncing {} search results", numSyncedThisPass);
mySyncedPids.addAll(myUnsyncedPids);
myUnsyncedPids.clear();
if (theResultIter.hasNext() == false) {
mySearch.setStatus(SearchStatusEnum.FINISHED);
mySearch.setTotalCount(myCountSaved);
}
}
mySearch.setNumFound(myCountSaved);
doSaveSearch();
}
});
myInitialCollectionLatch.countDown();
}
}
}

View File

@ -0,0 +1,13 @@
package ca.uhn.fhir.jpa.sp;
import java.util.Map;
import ca.uhn.fhir.jpa.entity.ResourceTable;
public interface ISearchParamPresenceSvc {
void updatePresence(ResourceTable theResource, Map<String, Boolean> theParamNameToPresence);
void flushCachesForUnitTest();
}

View File

@ -0,0 +1,88 @@
package ca.uhn.fhir.jpa.sp;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.jpa.dao.data.ISearchParamDao;
import ca.uhn.fhir.jpa.dao.data.ISearchParamPresentDao;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.SearchParam;
import ca.uhn.fhir.jpa.entity.SearchParamPresent;
public class SearchParamPresenceSvcImpl implements ISearchParamPresenceSvc {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamPresenceSvcImpl.class);
private Map<Pair<String, String>, SearchParam> myResourceTypeToSearchParamToEntity = new ConcurrentHashMap<Pair<String, String>, SearchParam>();
@Autowired
private ISearchParamDao mySearchParamDao;
@Autowired
private ISearchParamPresentDao mySearchParamPresentDao;
@Override
public void updatePresence(ResourceTable theResource, Map<String, Boolean> theParamNameToPresence) {
Map<String, Boolean> presenceMap = new HashMap<String, Boolean>(theParamNameToPresence);
List<SearchParamPresent> entitiesToSave = new ArrayList<SearchParamPresent>();
List<SearchParamPresent> entitiesToDelete = new ArrayList<SearchParamPresent>();
Collection<SearchParamPresent> existing;
existing = mySearchParamPresentDao.findAllForResource(theResource);
for (SearchParamPresent nextExistingEntity : existing) {
String nextSearchParamName = nextExistingEntity.getSearchParam().getParamName();
Boolean existingValue = presenceMap.remove(nextSearchParamName);
if (existingValue == null) {
entitiesToDelete.add(nextExistingEntity);
} else if (existingValue.booleanValue() == nextExistingEntity.isPresent()) {
ourLog.trace("No change for search param {}", nextSearchParamName);
} else {
nextExistingEntity.setPresent(existingValue);
entitiesToSave.add(nextExistingEntity);
}
}
for (Entry<String, Boolean> next : presenceMap.entrySet()) {
String resourceType = theResource.getResourceType();
String paramName = next.getKey();
Pair<String, String> key = Pair.of(resourceType, paramName);
SearchParam searchParam = myResourceTypeToSearchParamToEntity.get(key);
if (searchParam == null) {
searchParam = mySearchParamDao.findForResource(resourceType, paramName);
if (searchParam != null) {
myResourceTypeToSearchParamToEntity.put(key, searchParam);
} else {
searchParam = new SearchParam();
searchParam.setResourceName(resourceType);
searchParam.setParamName(paramName);
mySearchParamDao.save(searchParam);
// Don't add the newly saved entity to the map in case the save fails
}
}
SearchParamPresent present = new SearchParamPresent();
present.setResource(theResource);
present.setSearchParam(searchParam);
present.setPresent(next.getValue());
entitiesToSave.add(present);
}
mySearchParamPresentDao.deleteInBatch(entitiesToDelete);
mySearchParamPresentDao.save(entitiesToSave);
}
@Override
public void flushCachesForUnitTest() {
myResourceTypeToSearchParamToEntity.clear();
}
}

View File

@ -55,7 +55,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
private Properties jpaProperties() {
Properties extraProperties = new Properties();
extraProperties.put("hibernate.format_sql", "false");
extraProperties.put("hibernate.format_sql", "true");
extraProperties.put("hibernate.show_sql", "false");
extraProperties.put("hibernate.hbm2ddl.auto", "update");
extraProperties.put("hibernate.dialect", "org.hibernate.dialect.DerbyTenSevenDialect");

View File

@ -58,7 +58,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
private Properties jpaProperties() {
Properties extraProperties = new Properties();
extraProperties.put("hibernate.jdbc.batch_size", "50");
extraProperties.put("hibernate.format_sql", "false");
extraProperties.put("hibernate.format_sql", "true");
extraProperties.put("hibernate.show_sql", "false");
extraProperties.put("hibernate.hbm2ddl.auto", "update");
extraProperties.put("hibernate.dialect", "org.hibernate.dialect.DerbyTenSevenDialect");

View File

@ -3,15 +3,26 @@ package ca.uhn.fhir.jpa.config;
import java.util.Properties;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
@Configuration
@EnableTransactionManagement()
public class TestDstu3WithoutLuceneConfig extends TestDstu3Config {
/**
* Disable fulltext searching
*/
public IFulltextSearchSvc searchDaoDstu3() {
return null;
}
@Override
@Bean()
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

View File

@ -30,29 +30,9 @@ import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.ForcedId;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTag;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
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.SearchInclude;
import ca.uhn.fhir.jpa.entity.SearchResult;
import ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource;
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.jpa.entity.TagDefinition;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
@ -211,7 +191,7 @@ public abstract class BaseJpaTest {
return bundleStr;
}
public static void purgeDatabase(final EntityManager entityManager, PlatformTransactionManager theTxManager) {
public static void purgeDatabase(final EntityManager entityManager, PlatformTransactionManager theTxManager, ISearchParamPresenceSvc theSearchParamPresenceSvc) {
TransactionTemplate txTemplate = new TransactionTemplate(theTxManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
txTemplate.execute(new TransactionCallback<Void>() {
@ -225,6 +205,9 @@ public abstract class BaseJpaTest {
txTemplate.execute(new TransactionCallback<Void>() {
@Override
public Void doInTransaction(TransactionStatus theStatus) {
entityManager.createQuery("DELETE from " + SearchParamPresent.class.getSimpleName() + " d").executeUpdate();
entityManager.createQuery("DELETE from " + SearchParam.class.getSimpleName() + " d").executeUpdate();
entityManager.createQuery("DELETE from " + SubscriptionFlaggedResource.class.getSimpleName() + " d").executeUpdate();
entityManager.createQuery("DELETE from " + SubscriptionFlaggedResource.class.getSimpleName() + " d").executeUpdate();
entityManager.createQuery("DELETE from " + ForcedId.class.getSimpleName() + " d").executeUpdate();
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamDate.class.getSimpleName() + " d").executeUpdate();
@ -273,6 +256,7 @@ public abstract class BaseJpaTest {
return null;
}
});
theSearchParamPresenceSvc.flushCachesForUnitTest();
}
public static Set<String> toCodes(Set<TermConcept> theConcepts) {

View File

@ -26,12 +26,14 @@ import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.transaction.PlatformTransactionManager;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.config.TestDstu1Config;
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
@ -61,10 +63,11 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
private static IFhirResourceDao<Patient> ourPatientDao;
private static IFhirSystemDao<List<IResource>, MetaDt> ourSystemDao;
private static PlatformTransactionManager ourTxManager;
private static ISearchParamPresenceSvc ourSearchParamPresenceSvc;
@Before
public void before() {
super.purgeDatabase(ourEntityManager, ourTxManager);
super.purgeDatabase(ourEntityManager, ourTxManager, ourSearchParamPresenceSvc);
}
@Override
@ -120,7 +123,7 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
IIdType newpid3 = ourPatientDao.update(patient, mySrd).getId();
IBundleProvider values = ourSystemDao.history(start, null, mySrd);
assertEquals(4, values.size());
assertEquals(4, values.size().intValue());
List<IBaseResource> res = values.getResources(0, 4);
assertEquals(newpid3, res.get(0).getIdElement());
@ -139,10 +142,10 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
Thread.sleep(2000);
values = ourLocationDao.history(start, null, mySrd);
assertEquals(2, values.size());
assertEquals(2, values.size().intValue());
values = ourLocationDao.history(lid.toUnqualifiedVersionless(), start, null, mySrd);
assertEquals(1, values.size());
assertEquals(1, values.size().intValue());
}
@ -169,11 +172,11 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
// Try to search
IBundleProvider obsResults = ourObservationDao.search(Observation.SP_NAME, new IdentifierDt("urn:system", "testPersistWithSimpleLinkO01"));
assertEquals(1, obsResults.size());
IBundleProvider obsResults = ourObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_NAME, new IdentifierDt("urn:system", "testPersistWithSimpleLinkO01")));
assertEquals(1, obsResults.size().intValue());
IBundleProvider patResults = ourPatientDao.search(Patient.SP_IDENTIFIER, new IdentifierDt("urn:system", "testPersistWithSimpleLinkP01"));
assertEquals(1, obsResults.size());
IBundleProvider patResults = ourPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_IDENTIFIER, new IdentifierDt("urn:system", "testPersistWithSimpleLinkP01")));
assertEquals(1, obsResults.size().intValue());
IIdType foundPatientId = patResults.getResources(0, 1).get(0).getIdElement();
ResourceReferenceDt subject = obs.getSubject();
@ -443,8 +446,8 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
* Verify
*/
IBundleProvider results = ourPatientDao.search(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testTransactionWithDelete"));
assertEquals(3, results.size());
IBundleProvider results = ourPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testTransactionWithDelete")));
assertEquals(3, results.size().intValue());
/*
* Now delete 2
@ -469,8 +472,8 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
* Verify
*/
IBundleProvider results2 = ourPatientDao.search(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testTransactionWithDelete"));
assertEquals(1, results2.size());
IBundleProvider results2 = ourPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testTransactionWithDelete")));
assertEquals(1, results2.size().intValue());
List<IBaseResource> existing2 = results2.getResources(0, 1);
assertEquals(existing2.get(0).getIdElement(), existing.get(2).getIdElement());
@ -493,6 +496,7 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
ourSystemDao = ourCtx.getBean("mySystemDaoDstu1", IFhirSystemDao.class);
ourEntityManager = ourCtx.getBean(EntityManager.class);
ourTxManager = ourCtx.getBean(PlatformTransactionManager.class);
ourSearchParamPresenceSvc = ourCtx.getBean(ISearchParamPresenceSvc.class);
}
}

View File

@ -37,6 +37,7 @@ import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
@ -147,6 +148,9 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
@Autowired
@Qualifier("myValueSetDaoDstu2")
protected IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt> myValueSetDao;
@Autowired
protected ISearchParamPresenceSvc mySearchParamPresenceSvc;
@Before
public void beforeCreateInterceptor() {
myInterceptor = mock(IServerInterceptor.class);
@ -167,7 +171,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
@Transactional()
public void beforePurgeDatabase() {
final EntityManager entityManager = this.myEntityManager;
purgeDatabase(entityManager, myTxManager);
purgeDatabase(entityManager, myTxManager, mySearchParamPresenceSvc);
}
@Before

View File

@ -16,6 +16,9 @@ import javax.servlet.http.HttpServletRequest;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
import org.junit.Test;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
@ -150,13 +153,18 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
@Test
public void testSearchAndReindex() {
Patient patient;
SearchParameterMap map;
patient = new Patient();
patient.getText().setDiv("<div>DIVAAA</div>");
patient.addName().addGiven("NAMEAAA");
IIdType pId1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
final IIdType pId1= newTxTemplate().execute(new TransactionCallback<IIdType>() {
@Override
public IIdType doInTransaction(TransactionStatus theStatus) {
// TODO Auto-generated method stub
Patient patient = new Patient();
patient.getText().setDiv("<div>DIVAAA</div>");
patient.addName().addGiven("NAMEAAA");
return myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
});
map = new SearchParameterMap();
map.add(Constants.PARAM_CONTENT, new StringParam("NAMEAAA"));
@ -169,12 +177,21 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
/*
* Reindex
*/
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
Patient patient = new Patient();
patient.setId(pId1);
patient.getText().setDiv("<div>DIVBBB</div>");
patient.addName().addGiven("NAMEBBB");
myPatientDao.update(patient, mySrd);
}
});
patient = new Patient();
patient.setId(pId1);
patient.getText().setDiv("<div>DIVBBB</div>");
patient.addName().addGiven("NAMEBBB");
myPatientDao.update(patient, mySrd);
map = new SearchParameterMap();
map.add(Patient.SP_NAME, new StringParam("NAMEAAA"));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), empty());
map = new SearchParameterMap();
map.add(Constants.PARAM_CONTENT, new StringParam("NAMEAAA"));

View File

@ -180,21 +180,21 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
SearchParameterMap map = new SearchParameterMap();
map.add("_language", new StringParam("EN_ca"));
assertEquals(1, myOrganizationDao.search(map).size());
assertEquals(1, myOrganizationDao.search(map).size().intValue());
map = new SearchParameterMap();
map.add("_tag", new TokenParam(methodName, methodName));
assertEquals(1, myOrganizationDao.search(map).size());
assertEquals(1, myOrganizationDao.search(map).size().intValue());
myOrganizationDao.delete(orgId, mySrd);
map = new SearchParameterMap();
map.add("_language", new StringParam("EN_ca"));
assertEquals(0, myOrganizationDao.search(map).size());
assertEquals(0, myOrganizationDao.search(map).size().intValue());
map = new SearchParameterMap();
map.add("_tag", new TokenParam(methodName, methodName));
assertEquals(0, myOrganizationDao.search(map).size());
assertEquals(0, myOrganizationDao.search(map).size().intValue());
}
@Test
@ -205,8 +205,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
IIdType id1 = myObservationDao.create(o1, mySrd).getId();
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_CONCEPT, new TokenParam("testChoiceParam01CCS", "testChoiceParam01CCV"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_CONCEPT, new TokenParam("testChoiceParam01CCS", "testChoiceParam01CCV")));
assertEquals(1, found.size().intValue());
assertEquals(id1, found.getResources(0, 1).get(0).getIdElement());
}
}
@ -219,8 +219,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
IIdType id2 = myObservationDao.create(o2, mySrd).getId();
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_DATE, new DateParam("2001"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_DATE, new DateParam("2001")));
assertEquals(1, found.size().intValue());
assertEquals(id2, found.getResources(0, 1).get(0).getIdElement());
}
}
@ -230,11 +230,11 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
Observation o2 = new Observation();
o2.getCode().addCoding().setSystem("foo").setCode("testChoiceParamDateAlt02");
o2.setEffective(new DateTimeDt("2015-03-08T11:11:11"));
IIdType id2 = myObservationDao.create(o2, mySrd).getId();
IIdType id2 = myObservationDao.create(o2, mySrd).getId().toUnqualifiedVersionless();
{
Set<Long> found = myObservationDao.searchForIds(Observation.SP_DATE, new DateParam(">2001-01-02"));
assertThat(found, hasItem(id2.getIdPartAsLong()));
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_DATE, new DateParam(">2001-01-02")));
assertThat(toUnqualifiedVersionlessIdValues(found), hasItem(id2.getValue()));
}
{
Set<Long> found = myObservationDao.searchForIds(Observation.SP_DATE, new DateParam(">2016-01-02"));
@ -250,54 +250,54 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
IIdType id3 = myObservationDao.create(o3, mySrd).getId();
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam(">100", "foo", "bar"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_QUANTITY, new QuantityParam(">100", "foo", "bar")));
assertEquals(1, found.size().intValue());
assertEquals(id3, found.getResources(0, 1).get(0).getIdElement());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("gt100", "foo", "bar"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_QUANTITY, new QuantityParam("gt100", "foo", "bar")));
assertEquals(1, found.size().intValue());
assertEquals(id3, found.getResources(0, 1).get(0).getIdElement());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("<100", "foo", "bar"));
assertEquals(0, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_QUANTITY, new QuantityParam("<100", "foo", "bar")));
assertEquals(0, found.size().intValue());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("lt100", "foo", "bar"));
assertEquals(0, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_QUANTITY, new QuantityParam("lt100", "foo", "bar")));
assertEquals(0, found.size().intValue());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.0001", "foo", "bar"));
assertEquals(0, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.0001", "foo", "bar")));
assertEquals(0, found.size().intValue());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("~120", "foo", "bar"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_QUANTITY, new QuantityParam("~120", "foo", "bar")));
assertEquals(1, found.size().intValue());
assertEquals(id3, found.getResources(0, 1).get(0).getIdElement());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("ap120", "foo", "bar"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_QUANTITY, new QuantityParam("ap120", "foo", "bar")));
assertEquals(1, found.size().intValue());
assertEquals(id3, found.getResources(0, 1).get(0).getIdElement());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("eq123", "foo", "bar"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_QUANTITY, new QuantityParam("eq123", "foo", "bar")));
assertEquals(1, found.size().intValue());
assertEquals(id3, found.getResources(0, 1).get(0).getIdElement());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("eq120", "foo", "bar"));
assertEquals(0, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_QUANTITY, new QuantityParam("eq120", "foo", "bar")));
assertEquals(0, found.size().intValue());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("ne120", "foo", "bar"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_QUANTITY, new QuantityParam("ne120", "foo", "bar")));
assertEquals(1, found.size().intValue());
assertEquals(id3, found.getResources(0, 1).get(0).getIdElement());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("ne123", "foo", "bar"));
assertEquals(0, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_QUANTITY, new QuantityParam("ne123", "foo", "bar")));
assertEquals(0, found.size().intValue());
}
}
@ -310,8 +310,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
IIdType id4 = myObservationDao.create(o4, mySrd).getId();
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_STRING, new StringParam("testChoiceParam04Str"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_STRING, new StringParam("testChoiceParam04Str")));
assertEquals(1, found.size().intValue());
assertEquals(id4, found.getResources(0, 1).get(0).getIdElement());
}
}
@ -549,8 +549,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
IIdType id1 = myObservationDao.create(o1, mySrd).getId();
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_CONCEPT, new TokenParam("testChoiceParam01CCS", "testChoiceParam01CCV"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_VALUE_CONCEPT, new TokenParam("testChoiceParam01CCS", "testChoiceParam01CCV")));
assertEquals(1, found.size().intValue());
assertEquals(id1, found.getResources(0, 1).get(0).getIdElement());
}
}
@ -819,8 +819,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
}
ourLog.info("ID1:{} ID2:{} ID2b:{}", new Object[] { id1, id2, id2b });
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
params.put(Patient.SP_FAMILY, new StringDt("Tester_testDeleteResource"));
SearchParameterMap params = new SearchParameterMap();
params.add(Patient.SP_FAMILY, new StringDt("Tester_testDeleteResource"));
List<Patient> patients = toList(myPatientDao.search(params));
assertEquals(2, patients.size());
@ -838,7 +838,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
}
IBundleProvider history = myPatientDao.history(null, null, mySrd);
assertEquals(4 + initialHistory, history.size());
assertEquals(4 + initialHistory, history.size().intValue());
List<IBaseResource> resources = history.getResources(0, 4);
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) resources.get(0)));
@ -918,7 +918,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
}
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
assertEquals(2, history.size());
assertEquals(2, history.size().intValue());
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());
@ -1183,7 +1183,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
// By instance
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
assertEquals(fullSize + 1, history.size());
assertEquals(fullSize + 1, history.size().intValue());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1192,7 +1192,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
// By type
history = myPatientDao.history(null, null, mySrd);
assertEquals(fullSize + 1, history.size());
assertEquals(fullSize + 1, history.size().intValue());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1201,7 +1201,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
// By server
history = mySystemDao.history(null, null, mySrd);
assertEquals(fullSize + 1, history.size());
assertEquals(fullSize + 1, history.size().intValue());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1214,7 +1214,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
// By instance
history = myPatientDao.history(id, middleDate, null, mySrd);
assertEquals(halfSize, history.size());
assertEquals(halfSize, history.size().intValue());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1223,7 +1223,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
// By type
history = myPatientDao.history(middleDate, null, mySrd);
assertEquals(halfSize, history.size());
assertEquals(halfSize, history.size().intValue());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1232,7 +1232,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
// By server
history = mySystemDao.history(middleDate, null, mySrd);
assertEquals(halfSize, history.size());
assertEquals(halfSize, history.size().intValue());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1255,7 +1255,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
assertEquals(expected, actual);
}
assertEquals(log(history), fullSize + 1, history.size());
assertEquals(log(history), fullSize + 1, history.size().intValue());
// By type
history = myPatientDao.history(null, null, mySrd);
@ -1265,8 +1265,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
assertEquals(expected, actual);
}
ourLog.info(log(history));
ourLog.info("Want {} but got {}", fullSize + 1, history.size());
assertEquals(log(history), fullSize + 1, history.size()); // fails?
ourLog.info("Want {} but got {}", fullSize + 1, history.size().intValue());
assertEquals(log(history), fullSize + 1, history.size().intValue()); // fails?
// By server
history = mySystemDao.history(null, null, mySrd);
@ -1275,7 +1275,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
assertEquals(expected, actual);
}
assertEquals(log(history), fullSize + 1, history.size());
assertEquals(log(history), fullSize + 1, history.size().intValue());
/*
* With since date
@ -1288,7 +1288,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
assertEquals(expected, actual);
}
assertEquals(halfSize, history.size());
assertEquals(halfSize, history.size().intValue());
// By type
history = myPatientDao.history(middleDate, null, mySrd);
@ -1297,7 +1297,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
assertEquals(expected, actual);
}
assertEquals(halfSize, history.size());
assertEquals(halfSize, history.size().intValue());
// By server
history = mySystemDao.history(middleDate, null, mySrd);
@ -1306,7 +1306,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
assertEquals(expected, actual);
}
assertEquals(halfSize, history.size());
assertEquals(halfSize, history.size().intValue());
}
@ -1323,7 +1323,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
myPatientDao.update(patient, mySrd);
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
assertEquals(3, history.size());
assertEquals(3, history.size().intValue());
List<IBaseResource> entries = history.getResources(0, 3);
ourLog.info("" + ResourceMetadataKeyEnum.UPDATED.get((IResource) entries.get(0)));
ourLog.info("" + ResourceMetadataKeyEnum.UPDATED.get((IResource) entries.get(1)));
@ -1560,17 +1560,17 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
// OK
param = new ReferenceParam("999999999999");
param.setChain("organization");
myLocationDao.search("partof", param);
myLocationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("partof", param));
// OK
param = new ReferenceParam("999999999999");
param.setChain("organization.name");
myLocationDao.search("partof", param);
myLocationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("partof", param));
try {
param = new ReferenceParam("999999999999");
param.setChain("foo");
myLocationDao.search("partof", param);
myLocationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("partof", param));
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Invalid parameter chain: partof." + param.getChain()));
@ -1579,7 +1579,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
try {
param = new ReferenceParam("999999999999");
param.setChain("organization.foo");
myLocationDao.search("partof", param);
myLocationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("partof", param));
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Invalid parameter chain: " + param.getChain()));
@ -1588,7 +1588,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
try {
param = new ReferenceParam("999999999999");
param.setChain("organization.name.foo");
myLocationDao.search("partof", param);
myLocationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("partof", param));
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Invalid parameter chain: " + param.getChain()));
@ -1637,7 +1637,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
@Test
public void testPersistContactPoint() {
List<IResource> found = toList(myPatientDao.search(Patient.SP_TELECOM, new TokenParam(null, "555-123-4567")));
List<IResource> found = toList(myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_TELECOM, new TokenParam(null, "555-123-4567"))));
int initialSize2000 = found.size();
Patient patient = new Patient();
@ -1645,7 +1645,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
patient.addTelecom().setValue("555-123-4567");
myPatientDao.create(patient, mySrd);
found = toList(myPatientDao.search(Patient.SP_TELECOM, new TokenParam(null, "555-123-4567")));
found = toList(myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_TELECOM, new TokenParam(null, "555-123-4567"))));
assertEquals(1 + initialSize2000, found.size());
}
@ -1677,25 +1677,25 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
ourLog.info("P1[{}] P2[{}] O1[{}] O2[{}] D1[{}]", new Object[] { patientId01, patientId02, obsId01, obsId02, drId01 });
List<Observation> result = toList(myObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(patientId01.getIdPart())));
List<Observation> result = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam(patientId01.getIdPart()))));
assertEquals(1, result.size());
assertEquals(obsId01.getIdPart(), result.get(0).getId().getIdPart());
result = toList(myObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(patientId02.getIdPart())));
result = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam(patientId02.getIdPart()))));
assertEquals(1, result.size());
assertEquals(obsId02.getIdPart(), result.get(0).getId().getIdPart());
result = toList(myObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam("999999999999")));
result = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam("999999999999"))));
assertEquals(0, result.size());
}
@Test
public void testPersistSearchParamDate() {
List<Patient> found = toList(myPatientDao.search(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01")));
List<Patient> found = toList(myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01"))));
int initialSize2000 = found.size();
found = toList(myPatientDao.search(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2002-01-01")));
found = toList(myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2002-01-01"))));
int initialSize2002 = found.size();
Patient patient = new Patient();
@ -1704,15 +1704,19 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
myPatientDao.create(patient, mySrd);
found = toList(myPatientDao.search(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01")));
found = toList(myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01"))));
assertEquals(1 + initialSize2000, found.size());
found = toList(myPatientDao.search(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2002-01-01")));
found = toList(myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2002-01-01"))));
assertEquals(initialSize2002, found.size());
// If this throws an exception, that would be an acceptable outcome as well..
found = toList(myPatientDao.search(Patient.SP_BIRTHDATE + "AAAA", new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01")));
assertEquals(0, found.size());
try {
found = toList(myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_BIRTHDATE + "AAAA", new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01"))));
assertEquals(0, found.size());
} catch (InvalidRequestException e) {
assertEquals("Unknown search parameter birthdateAAAA for resource type Patient", e.getMessage());
}
}
@ -1724,10 +1728,10 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
myObservationDao.create(obs, mySrd);
List<Observation> found = toList(myObservationDao.search("value-string", new StringDt("AAAABBBB")));
List<Observation> found = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("value-string", new StringDt("AAAABBBB"))));
assertEquals(1, found.size());
found = toList(myObservationDao.search("value-string", new StringDt("AAAABBBBCCC")));
found = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("value-string", new StringDt("AAAABBBBCCC"))));
assertEquals(0, found.size());
}
@ -1740,13 +1744,13 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
myObservationDao.create(obs, mySrd);
List<Observation> found = toList(myObservationDao.search("value-quantity", new QuantityDt(111)));
List<Observation> found = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("value-quantity", new QuantityDt(111))));
assertEquals(1, found.size());
found = toList(myObservationDao.search("value-quantity", new QuantityDt(112)));
found = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("value-quantity", new QuantityDt(112))));
assertEquals(0, found.size());
found = toList(myObservationDao.search("value-quantity", new QuantityDt(212)));
found = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("value-quantity", new QuantityDt(212))));
assertEquals(0, found.size());
}
@ -1765,7 +1769,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
long id = outcome.getId().getIdPartAsLong();
IdentifierDt value = new IdentifierDt("urn:system", "001testPersistSearchParams");
List<Patient> found = toList(myPatientDao.search(Patient.SP_IDENTIFIER, value));
List<Patient> found = toList(myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_IDENTIFIER, value)));
assertEquals(1, found.size());
assertEquals(id, found.get(0).getId().getIdPartAsLong().longValue());
@ -1808,8 +1812,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
q.getGroup().setTitle("testQuestionnaireTitleGetsIndexedQ_NOTITLE");
IIdType qid2 = myQuestionnaireDao.create(q, mySrd).getId().toUnqualifiedVersionless();
IBundleProvider results = myQuestionnaireDao.search("title", new StringParam("testQuestionnaireTitleGetsIndexedQ_TITLE"));
assertEquals(1, results.size());
IBundleProvider results = myQuestionnaireDao.search(new SearchParameterMap().setLoadSynchronous(true).add("title", new StringParam("testQuestionnaireTitleGetsIndexedQ_TITLE")));
assertEquals(1, results.size().intValue());
assertEquals(qid1, results.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
assertNotEquals(qid2, results.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
@ -2202,7 +2206,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
map.add(Organization.SP_NAME, new StringParam("X" + methodName + "X"));
map.setRevIncludes(Collections.singleton(Patient.INCLUDE_ORGANIZATION));
IBundleProvider resultsP = myOrganizationDao.search(map);
assertEquals(1, resultsP.size());
assertEquals(1, resultsP.size().intValue());
List<IBaseResource> results = resultsP.getResources(0, resultsP.size());
assertEquals(2, results.size());
@ -2221,7 +2225,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
SearchParameterMap pm = new SearchParameterMap();
pm.setSort(new SortSpec(Observation.SP_CODE_VALUE_CONCEPT));
try {
myObservationDao.search(pm);
myObservationDao.search(pm).size();
fail();
} catch (InvalidRequestException e) {
assertEquals("This server does not support _sort specifications of type COMPOSITE - Can't serve _sort=code-value-concept", e.getMessage());
@ -2275,7 +2279,9 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
pm.setSort(new SortSpec(Patient.SP_BIRTHDATE).setOrder(SortOrderEnum.DESC));
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
assertEquals(4, actual.size());
assertThat(actual, contains(id3, id2, id1, id4));
// The first would be better, but JPA doesn't do NULLS LAST
// assertThat(actual, contains(id3, id2, id1, id4));
assertThat(actual, contains(id4, id3, id2, id1));
}
@ -2557,7 +2563,9 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
pm.setSort(new SortSpec(Patient.SP_FAMILY).setOrder(SortOrderEnum.DESC));
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
assertEquals(4, actual.size());
assertThat(actual, contains(id3, id2, id1, id4));
// The first would be better, but JPA doesn't do NULLS LAST
// assertThat(actual, contains(id3, id2, id1, id4));
assertThat(actual, contains(id4, id3, id2, id1));
}
/**
@ -2847,7 +2855,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
assertEquals("http://profile/1", profiles.get(0).getValue());
assertEquals("http://profile/2", profiles.get(1).getValue());
List<Patient> search = toList(myPatientDao.search(Patient.SP_IDENTIFIER, patient.getIdentifierFirstRep()));
List<Patient> search = toList(myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_IDENTIFIER, patient.getIdentifierFirstRep())));
assertEquals(1, search.size());
retrieved = search.get(0);

View File

@ -113,7 +113,7 @@ public class FhirResourceDaoDstu2UpdateTest extends BaseJpaDstu2Test {
IBundleProvider historyBundle = myPatientDao.history(outcome.getId(), null, null, mySrd);
assertEquals(2, historyBundle.size());
assertEquals(2, historyBundle.size().intValue());
List<IBaseResource> history = historyBundle.getResources(0, 2);
assertEquals("1", history.get(1).getIdElement().getVersionIdPart());

View File

@ -875,7 +875,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
}
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
assertEquals(2, history.size());
assertEquals(2, history.size().intValue());
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());

View File

@ -37,6 +37,7 @@ import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
import ca.uhn.fhir.parser.IParser;
@ -113,6 +114,9 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
@Autowired
@Qualifier("myImmunizationDaoDstu3")
protected IFhirResourceDao<Immunization> myImmunizationDao;
@Autowired
@Qualifier("myImmunizationRecommendationDaoDstu3")
protected IFhirResourceDao<ImmunizationRecommendation> myImmunizationRecommendationDao;
protected IServerInterceptor myInterceptor;
@Autowired
private JpaValidationSupportChainDstu3 myJpaValidationSupportChainDstu3;
@ -144,6 +148,9 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
@Qualifier("myOrganizationDaoDstu3")
protected IFhirResourceDao<Organization> myOrganizationDao;
@Autowired
@Qualifier("myTaskDaoDstu3")
protected IFhirResourceDao<Task> myTaskDao;
@Autowired
@Qualifier("myPatientDaoDstu3")
protected IFhirResourceDaoPatient<Patient> myPatientDao;
@Autowired
@ -191,6 +198,8 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
protected IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> myValueSetDao;
@Autowired
protected PlatformTransactionManager myTransactionMgr;
@Autowired
protected ISearchParamPresenceSvc mySearchParamPresenceSvc;
@After()
public void afterGrabCaches() {
ourValueSetDao = myValueSetDao;
@ -218,7 +227,7 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
@Transactional()
public void beforePurgeDatabase() {
final EntityManager entityManager = this.myEntityManager;
purgeDatabase(entityManager, myTxManager);
purgeDatabase(entityManager, myTxManager, mySearchParamPresenceSvc);
}
@Before

View File

@ -32,7 +32,7 @@ public class FhirResourceDaoCustomTypeDstu3Test extends BaseJpaDstu3Test {
assertEquals("blue", read.getEyeColour().getValue());
IBundleProvider found = myObservationDao.search(new SearchParameterMap());
assertEquals(1, found.size());
assertEquals(1, found.size().intValue());
CustomObservationDstu3 search = (CustomObservationDstu3) found.getResources(0, 1).get(0);
assertEquals("blue", search.getEyeColour().getValue());

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
@ -242,8 +243,12 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
// Try with custom gender SP
map = new SearchParameterMap();
map.add("foo", new TokenParam(null, "male"));
IBundleProvider res = myPatientDao.search(map);
assertEquals(0, res.size());
try {
myPatientDao.search(map).size();
fail();
} catch (InvalidRequestException e) {
assertEquals("Unknown search parameter foo for resource type Patient", e.getMessage());
}
}
@Test
@ -276,9 +281,12 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
// Try with custom gender SP (should find nothing)
map = new SearchParameterMap();
map.add("foo", new TokenParam(null, "male"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, empty());
try {
myPatientDao.search(map).size();
fail();
} catch (InvalidRequestException e) {
assertEquals("Unknown search parameter foo for resource type Patient", e.getMessage());
}
// Try with normal gender SP
map = new SearchParameterMap();

View File

@ -23,6 +23,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.config.TestDstu3WithoutLuceneConfig;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -139,6 +140,8 @@ public class FhirResourceDaoDstu3SearchWithLuceneDisabledTest extends BaseJpaTes
@Autowired
protected PlatformTransactionManager myTxManager;
@Autowired
protected ISearchParamPresenceSvc mySearchParamPresenceSvc;
@Autowired
@Qualifier("myJpaValidationSupportChainDstu3")
@ -148,7 +151,7 @@ public class FhirResourceDaoDstu3SearchWithLuceneDisabledTest extends BaseJpaTes
@Transactional()
public void beforePurgeDatabase() {
final EntityManager entityManager = this.myEntityManager;
purgeDatabase(entityManager, myTxManager);
purgeDatabase(entityManager, myTxManager, mySearchParamPresenceSvc);
}
@Before
@ -188,7 +191,7 @@ public class FhirResourceDaoDstu3SearchWithLuceneDisabledTest extends BaseJpaTes
SearchParameterMap map = new SearchParameterMap();
map.add(Constants.PARAM_CONTENT, new StringParam(methodName));
try {
myOrganizationDao.search(map);
myOrganizationDao.search(map).size();
fail();
} catch (InvalidRequestException e) {
assertEquals("Fulltext search is not enabled on this service, can not process parameter: _content", e.getMessage());
@ -206,7 +209,7 @@ public class FhirResourceDaoDstu3SearchWithLuceneDisabledTest extends BaseJpaTes
SearchParameterMap map = new SearchParameterMap();
map.add(Constants.PARAM_TEXT, new StringParam(methodName));
try {
myOrganizationDao.search(map);
myOrganizationDao.search(map).size();
fail();
} catch (InvalidRequestException e) {
assertEquals("Fulltext search is not enabled on this service, can not process parameter: _text", e.getMessage());

View File

@ -726,7 +726,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
SearchParameterMap params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "AAA").setModifier(TokenParamModifier.ABOVE));
try {
myObservationDao.search(params);
myObservationDao.search(params).size();
fail();
} catch (InvalidRequestException e) {
assertEquals("Expansion of ValueSet produced too many codes (maximum 1) - Operation aborted!", e.getMessage());
@ -794,9 +794,19 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "childAA").setModifier(TokenParamModifier.ABOVE));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
}
@Test
public void testSearchCodeInUnknownCodeSystem() {
SearchParameterMap params = new SearchParameterMap();
try {
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
} catch (InvalidRequestException e) {
assertEquals("Unable to find imported value set http://example.com/my_value_set", e.getMessage());
}
}
@Test

View File

@ -24,62 +24,22 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.hamcrest.Matchers;
import org.hamcrest.core.StringContains;
import org.hl7.fhir.dstu3.model.Age;
import org.hl7.fhir.dstu3.model.BaseResource;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
import org.hl7.fhir.dstu3.model.CarePlan;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.CompartmentDefinition;
import org.hl7.fhir.dstu3.model.ConceptMap;
import org.hl7.fhir.dstu3.model.Condition;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.DateType;
import org.hl7.fhir.dstu3.model.Device;
import org.hl7.fhir.dstu3.model.DiagnosticReport;
import org.hl7.fhir.dstu3.model.Encounter;
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Meta;
import org.hl7.fhir.dstu3.model.NamingSystem;
import org.hl7.fhir.dstu3.model.Observation;
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
import org.hl7.fhir.dstu3.model.OperationDefinition;
import org.hl7.fhir.dstu3.model.OperationOutcome;
import org.hl7.fhir.dstu3.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.dstu3.model.OperationOutcome.IssueType;
import org.hl7.fhir.dstu3.model.Organization;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Period;
import org.hl7.fhir.dstu3.model.Quantity;
import org.hl7.fhir.dstu3.model.Quantity.QuantityComparator;
import org.hl7.fhir.dstu3.model.Questionnaire;
import org.hl7.fhir.dstu3.model.Range;
import org.hl7.fhir.dstu3.model.Reference;
import org.hl7.fhir.dstu3.model.SimpleQuantity;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.StructureDefinition;
import org.hl7.fhir.dstu3.model.Timing;
import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@ -94,7 +54,6 @@ import com.google.common.collect.Lists;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
@ -105,21 +64,10 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenOrListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.exceptions.*;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.TestUtil;
@ -226,21 +174,24 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
SearchParameterMap map = new SearchParameterMap();
map.add("_language", new StringParam("EN_ca"));
assertEquals(1, myOrganizationDao.search(map).size());
assertEquals(1, myOrganizationDao.search(map).size().intValue());
map = new SearchParameterMap();
map.setLoadSynchronous(true);
map.add("_tag", new TokenParam(methodName, methodName));
assertEquals(1, myOrganizationDao.search(map).size());
assertEquals(1, myOrganizationDao.search(map).size().intValue());
myOrganizationDao.delete(orgId, mySrd);
map = new SearchParameterMap();
map.setLoadSynchronous(true);
map.add("_language", new StringParam("EN_ca"));
assertEquals(0, myOrganizationDao.search(map).size());
assertEquals(0, myOrganizationDao.search(map).size().intValue());
map = new SearchParameterMap();
map.setLoadSynchronous(true);
map.add("_tag", new TokenParam(methodName, methodName));
assertEquals(0, myOrganizationDao.search(map).size());
assertEquals(0, myOrganizationDao.search(map).size().intValue());
}
@Test
@ -251,8 +202,8 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
IIdType id1 = myObservationDao.create(o1, mySrd).getId();
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_CONCEPT, new TokenParam("testChoiceParam01CCS", "testChoiceParam01CCV"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_CONCEPT, new TokenParam("testChoiceParam01CCS", "testChoiceParam01CCV")).setLoadSynchronous(true));
assertEquals(1, found.size().intValue());
assertEquals(id1, found.getResources(0, 1).get(0).getIdElement());
}
}
@ -265,8 +216,8 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
IIdType id2 = myObservationDao.create(o2, mySrd).getId();
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_DATE, new DateParam("2001"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_DATE, new DateParam("2001")).setLoadSynchronous(true));
assertEquals(1, found.size().intValue());
assertEquals(id2, found.getResources(0, 1).get(0).getIdElement());
}
}
@ -300,18 +251,18 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
* This should not match, per the definition of eq
*/
ids = toUnqualifiedVersionlessIdValues(myEncounterDao.search(Encounter.SP_DATE, new DateParam("2016-05-15")));
ids = toUnqualifiedVersionlessIdValues(myEncounterDao.search(new SearchParameterMap(Encounter.SP_DATE, new DateParam("2016-05-15")).setLoadSynchronous(true)));
assertThat(ids, empty());
ids = toUnqualifiedVersionlessIdValues(myEncounterDao.search(Encounter.SP_DATE, new DateParam("eq2016-05-15")));
ids = toUnqualifiedVersionlessIdValues(myEncounterDao.search(new SearchParameterMap(Encounter.SP_DATE, new DateParam("eq2016-05-15")).setLoadSynchronous(true)));
assertThat(ids, empty());
// Should match
ids = toUnqualifiedVersionlessIdValues(myEncounterDao.search(Encounter.SP_DATE, new DateParam("eq2016")));
ids = toUnqualifiedVersionlessIdValues(myEncounterDao.search(new SearchParameterMap(Encounter.SP_DATE, new DateParam("eq2016")).setLoadSynchronous(true)));
assertThat(ids, contains(id));
ids = toUnqualifiedVersionlessIdValues(myEncounterDao.search(Encounter.SP_DATE, new DateParam("2016")));
ids = toUnqualifiedVersionlessIdValues(myEncounterDao.search(new SearchParameterMap(Encounter.SP_DATE, new DateParam("2016")).setLoadSynchronous(true)));
assertThat(ids, contains(id));
}
@ -329,27 +280,27 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
IIdType id2 = myObservationDao.create(o2, mySrd).getId().toUnqualifiedVersionless();
{
IBundleProvider found = myObservationDao.search(Observation.SP_DATE, new DateParam("ge2015-01-02T00:00:00Z"));
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_DATE, new DateParam("ge2015-01-02T00:00:00Z")).setLoadSynchronous(true));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertThat(list, containsInAnyOrder(id1, id2));
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_DATE, new DateParam("gt2015-01-02T00:00:00Z"));
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_DATE, new DateParam("gt2015-01-02T00:00:00Z")).setLoadSynchronous(true));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertThat(list, containsInAnyOrder(id1, id2));
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_DATE, new DateParam("gt2015-01-10T00:00:00Z"));
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_DATE, new DateParam("gt2015-01-10T00:00:00Z")).setLoadSynchronous(true));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertThat(list, containsInAnyOrder(id2));
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_DATE, new DateParam("sa2015-01-02T00:00:00Z"));
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_DATE, new DateParam("sa2015-01-02T00:00:00Z")).setLoadSynchronous(true));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertThat(list, containsInAnyOrder(id2));
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_DATE, new DateParam("eb2015-01-13T00:00:00Z"));
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_DATE, new DateParam("eb2015-01-13T00:00:00Z")).setLoadSynchronous(true));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertThat(list, containsInAnyOrder(id1));
}
@ -363,54 +314,54 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
IIdType id3 = myObservationDao.create(o3, mySrd).getId();
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam(">100", "foo", "bar"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam(">100", "foo", "bar")).setLoadSynchronous(true));
assertEquals(1, found.size().intValue());
assertEquals(id3, found.getResources(0, 1).get(0).getIdElement());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("gt100", "foo", "bar"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("gt100", "foo", "bar")).setLoadSynchronous(true));
assertEquals(1, found.size().intValue());
assertEquals(id3, found.getResources(0, 1).get(0).getIdElement());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("<100", "foo", "bar"));
assertEquals(0, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("<100", "foo", "bar")).setLoadSynchronous(true));
assertEquals(0, found.size().intValue());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("lt100", "foo", "bar"));
assertEquals(0, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("lt100", "foo", "bar")).setLoadSynchronous(true));
assertEquals(0, found.size().intValue());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.0001", "foo", "bar"));
assertEquals(0, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.0001", "foo", "bar")).setLoadSynchronous(true));
assertEquals(0, found.size().intValue());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("~120", "foo", "bar"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("~120", "foo", "bar")).setLoadSynchronous(true));
assertEquals(1, found.size().intValue());
assertEquals(id3, found.getResources(0, 1).get(0).getIdElement());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("ap120", "foo", "bar"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("ap120", "foo", "bar")).setLoadSynchronous(true));
assertEquals(1, found.size().intValue());
assertEquals(id3, found.getResources(0, 1).get(0).getIdElement());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("eq123", "foo", "bar"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("eq123", "foo", "bar")).setLoadSynchronous(true));
assertEquals(1, found.size().intValue());
assertEquals(id3, found.getResources(0, 1).get(0).getIdElement());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("eq120", "foo", "bar"));
assertEquals(0, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("eq120", "foo", "bar")).setLoadSynchronous(true));
assertEquals(0, found.size().intValue());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("ne120", "foo", "bar"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("ne120", "foo", "bar")).setLoadSynchronous(true));
assertEquals(1, found.size().intValue());
assertEquals(id3, found.getResources(0, 1).get(0).getIdElement());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("ne123", "foo", "bar"));
assertEquals(0, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("ne123", "foo", "bar")).setLoadSynchronous(true));
assertEquals(0, found.size().intValue());
}
}
@ -422,32 +373,32 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
IIdType id3 = myObservationDao.create(o3, mySrd).getId().toUnqualifiedVersionless();
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123", "foo", "bar"));
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("123", "foo", "bar")).setLoadSynchronous(true));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertThat(list, containsInAnyOrder(id3));
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.0", "foo", "bar"));
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.0", "foo", "bar")).setLoadSynchronous(true));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertThat(list, containsInAnyOrder(id3));
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.01", "foo", "bar"));
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.01", "foo", "bar")).setLoadSynchronous(true));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertThat(list, containsInAnyOrder(id3));
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.010", "foo", "bar"));
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.010", "foo", "bar")).setLoadSynchronous(true));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertThat(list, containsInAnyOrder(id3));
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.02", "foo", "bar"));
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.02", "foo", "bar")).setLoadSynchronous(true));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertThat(list, containsInAnyOrder());
}
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.001", "foo", "bar"));
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_QUANTITY, new QuantityParam("123.001", "foo", "bar")).setLoadSynchronous(true));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertThat(list, containsInAnyOrder());
}
@ -462,8 +413,8 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
IIdType id4 = myObservationDao.create(o4, mySrd).getId();
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_STRING, new StringParam("testChoiceParam04Str"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_STRING, new StringParam("testChoiceParam04Str")).setLoadSynchronous(true));
assertEquals(1, found.size().intValue());
assertEquals(id4, found.getResources(0, 1).get(0).getIdElement());
}
}
@ -594,7 +545,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
NamingSystem res = myFhirCtx.newXmlParser().parseResource(NamingSystem.class, input);
IIdType id = myNamingSystemDao.create(res, mySrd).getId().toUnqualifiedVersionless();
assertThat(toUnqualifiedVersionlessIdValues(myNamingSystemDao.search(NamingSystem.SP_NAME, new StringParam("NDF"))), contains(id.getValue()));
assertThat(toUnqualifiedVersionlessIdValues(myNamingSystemDao.search(new SearchParameterMap(NamingSystem.SP_NAME, new StringParam("NDF")).setLoadSynchronous(true))), contains(id.getValue()));
}
@Test
@ -789,8 +740,8 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
IIdType id1 = myObservationDao.create(o1, mySrd).getId();
{
IBundleProvider found = myObservationDao.search(Observation.SP_VALUE_CONCEPT, new TokenParam("testChoiceParam01CCS", "testChoiceParam01CCV"));
assertEquals(1, found.size());
IBundleProvider found = myObservationDao.search(new SearchParameterMap(Observation.SP_VALUE_CONCEPT, new TokenParam("testChoiceParam01CCS", "testChoiceParam01CCV")).setLoadSynchronous(true));
assertEquals(1, found.size().intValue());
assertEquals(id1, found.getResources(0, 1).get(0).getIdElement());
}
}
@ -1075,8 +1026,9 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
ourLog.info("ID1:{} ID2:{} ID2b:{}", new Object[] { id1, id2, id2b });
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
params.put(Patient.SP_FAMILY, new StringParam("Tester_testDeleteResource"));
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronous(true);
params.add(Patient.SP_FAMILY, new StringParam("Tester_testDeleteResource"));
List<Patient> patients = toList(myPatientDao.search(params));
assertEquals(2, patients.size());
@ -1094,7 +1046,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
IBundleProvider history = myPatientDao.history((Date) null, null, mySrd);
assertEquals(4 + initialHistory, history.size());
assertEquals(4 + initialHistory, history.size().intValue());
List<IBaseResource> resources = history.getResources(0, 4);
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) resources.get(0)));
@ -1203,7 +1155,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
assertEquals(2, history.size());
assertEquals(2, history.size().intValue());
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());
@ -1457,7 +1409,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// By instance
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
assertEquals(fullSize + 1, history.size());
assertEquals(fullSize + 1, history.size().intValue());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1466,7 +1418,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// By type
history = myPatientDao.history((Date) null, null, mySrd);
assertEquals(fullSize + 1, history.size());
assertEquals(fullSize + 1, history.size().intValue());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1475,7 +1427,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// By server
history = mySystemDao.history(null, null, mySrd);
assertEquals(fullSize + 1, history.size());
assertEquals(fullSize + 1, history.size().intValue());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1488,7 +1440,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// By instance
history = myPatientDao.history(id, middleDate, null, mySrd);
assertEquals(halfSize, history.size());
assertEquals(halfSize, history.size().intValue());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1497,7 +1449,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// By type
history = myPatientDao.history(middleDate, null, mySrd);
assertEquals(halfSize, history.size());
assertEquals(halfSize, history.size().intValue());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1506,7 +1458,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// By server
history = mySystemDao.history(middleDate, null, mySrd);
assertEquals(halfSize, history.size());
assertEquals(halfSize, history.size().intValue());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1524,7 +1476,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// By instance
history = myPatientDao.history(id, null, null, mySrd);
assertEquals(fullSize + 1, history.size());
assertEquals(fullSize + 1, history.size().intValue());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1533,7 +1485,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// By type
history = myPatientDao.history((Date) null, null, mySrd);
assertEquals(fullSize + 1, history.size());
assertEquals(fullSize + 1, history.size().intValue());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1542,7 +1494,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// By server
history = mySystemDao.history(null, null, mySrd);
assertEquals(fullSize + 1, history.size());
assertEquals(fullSize + 1, history.size().intValue());
for (int i = 0; i < fullSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1555,7 +1507,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// By instance
history = myPatientDao.history(id, middleDate, null, mySrd);
assertEquals(halfSize, history.size());
assertEquals(halfSize, history.size().intValue());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1564,7 +1516,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// By type
history = myPatientDao.history(middleDate, null, mySrd);
assertEquals(halfSize, history.size());
assertEquals(halfSize, history.size().intValue());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1573,7 +1525,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// By server
history = mySystemDao.history(middleDate, null, mySrd);
assertEquals(halfSize, history.size());
assertEquals(halfSize, history.size().intValue());
for (int i = 0; i < halfSize; i++) {
String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue();
String actual = history.getResources(i, i + 1).get(0).getIdElement().getValue();
@ -1590,7 +1542,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
IIdType id = myPatientDao.create(inPatient, mySrd).getId().toUnqualifiedVersionless();
IBundleProvider history = myPatientDao.history((Date) null, null, mySrd);
assertEquals(1, history.size());
assertEquals(1, history.size().intValue());
Patient outPatient = (Patient) history.getResources(0, 1).get(0);
assertEquals("version1", inPatient.getName().get(0).getFamily());
List<String> profiles = toStringList(outPatient.getMeta().getProfile());
@ -1604,7 +1556,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
myPatientDao.metaAddOperation(id, inPatient.getMeta(), mySrd);
history = myPatientDao.history((Date) null, null, mySrd);
assertEquals(1, history.size());
assertEquals(1, history.size().intValue());
outPatient = (Patient) history.getResources(0, 1).get(0);
assertEquals("version1", inPatient.getName().get(0).getFamily());
profiles = toStringList(outPatient.getMeta().getProfile());
@ -1620,7 +1572,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
myPatientDao.update(inPatient, mySrd);
history = myPatientDao.history((Date) null, null, mySrd);
assertEquals(2, history.size());
assertEquals(2, history.size().intValue());
outPatient = (Patient) history.getResources(0, 2).get(0);
assertEquals("version2", outPatient.getName().get(0).getFamily());
profiles = toStringList(outPatient.getMeta().getProfile());
@ -1650,7 +1602,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
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(3, history.size().intValue());
assertEquals(id.withVersion("3"), entries.get(0).getIdElement());
assertEquals(id.withVersion("2"), entries.get(1).getIdElement());
@ -1714,7 +1666,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// No since
IBundleProvider history = myPatientDao.history((Date) null, null, mySrd);
assertEquals(1, history.size());
assertEquals(1, history.size().intValue());
Patient outPatient = (Patient) history.getResources(0, 1).get(0);
assertEquals("version1", inPatient.getName().get(0).getFamily());
List<String> profiles = toStringList(outPatient.getMeta().getProfile());
@ -1723,7 +1675,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// Before since
history = myPatientDao.history(before, null, mySrd);
assertEquals(1, history.size());
assertEquals(1, history.size().intValue());
outPatient = (Patient) history.getResources(0, 1).get(0);
assertEquals("version1", inPatient.getName().get(0).getFamily());
profiles = toStringList(outPatient.getMeta().getProfile());
@ -1732,7 +1684,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// After since
history = myPatientDao.history(after, null, mySrd);
assertEquals(0, history.size());
assertEquals(0, history.size().intValue());
}
@ -1980,17 +1932,17 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
// OK
param = new ReferenceParam("999999999999");
param.setChain("organization");
myLocationDao.search("partof", param);
myLocationDao.search(new SearchParameterMap("partof", param).setLoadSynchronous(true));
// OK
param = new ReferenceParam("999999999999");
param.setChain("organization.name");
myLocationDao.search("partof", param);
myLocationDao.search(new SearchParameterMap("partof", param).setLoadSynchronous(true));
try {
param = new ReferenceParam("999999999999");
param.setChain("foo");
myLocationDao.search("partof", param);
myLocationDao.search(new SearchParameterMap("partof", param).setLoadSynchronous(true));
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Invalid parameter chain: partof." + param.getChain()));
@ -1999,7 +1951,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
try {
param = new ReferenceParam("999999999999");
param.setChain("organization.foo");
myLocationDao.search("partof", param);
myLocationDao.search(new SearchParameterMap("partof", param).setLoadSynchronous(true));
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Invalid parameter chain: " + param.getChain()));
@ -2008,7 +1960,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
try {
param = new ReferenceParam("999999999999");
param.setChain("organization.name.foo");
myLocationDao.search("partof", param);
myLocationDao.search(new SearchParameterMap("partof", param).setLoadSynchronous(true));
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Invalid parameter chain: " + param.getChain()));
@ -2031,7 +1983,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
p2.getManagingOrganization().setReference("http://foo.com/identifier/2");
String p2id = myPatientDao.create(p2, mySrd).getId().toUnqualifiedVersionless().getValue();
IBundleProvider found = myPatientDao.search(Patient.SP_ORGANIZATION, new ReferenceParam("http://foo.com/identifier/1"));
IBundleProvider found = myPatientDao.search(new SearchParameterMap(Patient.SP_ORGANIZATION, new ReferenceParam("http://foo.com/identifier/1")).setLoadSynchronous(true));
assertThat(toUnqualifiedVersionlessIdValues(found), contains(p1id));
assertThat(toUnqualifiedVersionlessIdValues(found), not(contains(p2id)));
}
@ -2077,7 +2029,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
@Test
public void testPersistContactPoint() {
List<IAnyResource> found = toList(myPatientDao.search(Patient.SP_TELECOM, new TokenParam(null, "555-123-4567")));
List<IAnyResource> found = toList(myPatientDao.search(new SearchParameterMap(Patient.SP_TELECOM, new TokenParam(null, "555-123-4567")).setLoadSynchronous(true)));
int initialSize2000 = found.size();
Patient patient = new Patient();
@ -2085,7 +2037,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
patient.addTelecom().setValue("555-123-4567");
myPatientDao.create(patient, mySrd);
found = toList(myPatientDao.search(Patient.SP_TELECOM, new TokenParam(null, "555-123-4567")));
found = toList(myPatientDao.search(new SearchParameterMap(Patient.SP_TELECOM, new TokenParam(null, "555-123-4567")).setLoadSynchronous(true)));
assertEquals(1 + initialSize2000, found.size());
}
@ -2117,25 +2069,25 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
ourLog.info("P1[{}] P2[{}] O1[{}] O2[{}] D1[{}]", new Object[] { patientId01, patientId02, obsId01, obsId02, drId01 });
List<Observation> result = toList(myObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(patientId01.getIdPart())));
List<Observation> result = toList(myObservationDao.search(new SearchParameterMap(Observation.SP_SUBJECT, new ReferenceParam(patientId01.getIdPart())).setLoadSynchronous(true)));
assertEquals(1, result.size());
assertEquals(obsId01.getIdPart(), result.get(0).getIdElement().getIdPart());
result = toList(myObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(patientId02.getIdPart())));
result = toList(myObservationDao.search(new SearchParameterMap(Observation.SP_SUBJECT, new ReferenceParam(patientId02.getIdPart())).setLoadSynchronous(true)));
assertEquals(1, result.size());
assertEquals(obsId02.getIdPart(), result.get(0).getIdElement().getIdPart());
result = toList(myObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam("999999999999")));
result = toList(myObservationDao.search(new SearchParameterMap(Observation.SP_SUBJECT, new ReferenceParam("999999999999")).setLoadSynchronous(true)));;
assertEquals(0, result.size());
}
@Test
public void testPersistSearchParamDate() {
List<Patient> found = toList(myPatientDao.search(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01")));
List<Patient> found = toList(myPatientDao.search(new SearchParameterMap(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01")).setLoadSynchronous(true)));
int initialSize2000 = found.size();
found = toList(myPatientDao.search(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2002-01-01")));
found = toList(myPatientDao.search(new SearchParameterMap(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2002-01-01")).setLoadSynchronous(true)));
int initialSize2002 = found.size();
Patient patient = new Patient();
@ -2144,16 +2096,19 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
myPatientDao.create(patient, mySrd);
found = toList(myPatientDao.search(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01")));
found = toList(myPatientDao.search(new SearchParameterMap(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01")).setLoadSynchronous(true)));
assertEquals(1 + initialSize2000, found.size());
found = toList(myPatientDao.search(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2002-01-01")));
found = toList(myPatientDao.search(new SearchParameterMap(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2002-01-01")).setLoadSynchronous(true)));
assertEquals(initialSize2002, found.size());
// If this throws an exception, that would be an acceptable outcome as well..
found = toList(myPatientDao.search(Patient.SP_BIRTHDATE + "AAAA", new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01")));
assertEquals(0, found.size());
try {
found = toList(myPatientDao.search(new SearchParameterMap(Patient.SP_BIRTHDATE + "AAAA", new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01")).setLoadSynchronous(true)));
assertEquals(0, found.size());
} catch (InvalidRequestException e) {
assertEquals("Unknown search parameter birthdateAAAA for resource type Patient", e.getMessage());
}
}
@Test
@ -2164,10 +2119,10 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
myObservationDao.create(obs, mySrd);
List<Observation> found = toList(myObservationDao.search("value-string", new StringParam("AAAABBBB")));
List<Observation> found = toList(myObservationDao.search(new SearchParameterMap("value-string", new StringParam("AAAABBBB")).setLoadSynchronous(true)));
assertEquals(1, found.size());
found = toList(myObservationDao.search("value-string", new StringParam("AAAABBBBCCC")));
found = toList(myObservationDao.search(new SearchParameterMap("value-string", new StringParam("AAAABBBBCCC")).setLoadSynchronous(true)));
assertEquals(0, found.size());
}
@ -2180,13 +2135,13 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
myObservationDao.create(obs, mySrd);
List<Observation> found = toList(myObservationDao.search("value-quantity", new QuantityParam(111)));
List<Observation> found = toList(myObservationDao.search(new SearchParameterMap("value-quantity", new QuantityParam(111)).setLoadSynchronous(true)));
assertEquals(1, found.size());
found = toList(myObservationDao.search("value-quantity", new QuantityParam(112)));
found = toList(myObservationDao.search(new SearchParameterMap("value-quantity", new QuantityParam(112)).setLoadSynchronous(true)));
assertEquals(0, found.size());
found = toList(myObservationDao.search("value-quantity", new QuantityParam(212)));
found = toList(myObservationDao.search(new SearchParameterMap("value-quantity", new QuantityParam(212)).setLoadSynchronous(true)));
assertEquals(0, found.size());
}
@ -2205,7 +2160,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
long id = outcome.getId().getIdPartAsLong();
TokenParam value = new TokenParam("urn:system", "001testPersistSearchParams");
List<Patient> found = toList(myPatientDao.search(Patient.SP_IDENTIFIER, value));
List<Patient> found = toList(myPatientDao.search(new SearchParameterMap(Patient.SP_IDENTIFIER, value).setLoadSynchronous(true)));;
assertEquals(1, found.size());
assertEquals(id, found.get(0).getIdElement().getIdPartAsLong().longValue());
@ -2248,8 +2203,8 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
q.setTitle("testQuestionnaireTitleGetsIndexedQ_NOTITLE");
IIdType qid2 = myQuestionnaireDao.create(q, mySrd).getId().toUnqualifiedVersionless();
IBundleProvider results = myQuestionnaireDao.search("title", new StringParam("testQuestionnaireTitleGetsIndexedQ_TITLE"));
assertEquals(1, results.size());
IBundleProvider results = myQuestionnaireDao.search(new SearchParameterMap("title", new StringParam("testQuestionnaireTitleGetsIndexedQ_TITLE")).setLoadSynchronous(true));
assertEquals(1, results.size().intValue());
assertEquals(qid1, results.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
assertNotEquals(qid2, results.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless());
@ -2705,7 +2660,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
map.add(Organization.SP_NAME, new StringParam("X" + methodName + "X"));
map.setRevIncludes(Collections.singleton(Patient.INCLUDE_ORGANIZATION));
IBundleProvider resultsP = myOrganizationDao.search(map);
assertEquals(1, resultsP.size());
assertEquals(1, resultsP.size().intValue());
List<IBaseResource> results = resultsP.getResources(0, resultsP.size());
assertEquals(2, results.size());
@ -2724,7 +2679,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
SearchParameterMap pm = new SearchParameterMap();
pm.setSort(new SortSpec(Observation.SP_CODE_VALUE_CONCEPT));
try {
myObservationDao.search(pm);
myObservationDao.search(pm).size();
fail();
} catch (InvalidRequestException e) {
assertEquals("This server does not support _sort specifications of type COMPOSITE - Can't serve _sort=code-value-concept", e.getMessage());
@ -2778,7 +2733,9 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
pm.setSort(new SortSpec(Patient.SP_BIRTHDATE).setOrder(SortOrderEnum.DESC));
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
assertEquals(4, actual.size());
assertThat(actual, contains(id3, id2, id1, id4));
// The first would be better, but JPA doesn't do NULLS LAST
// assertThat(actual, contains(id3, id2, id1, id4));
assertThat(actual, contains(id4, id3, id2, id1));
}
@ -3060,7 +3017,9 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
pm.setSort(new SortSpec(Patient.SP_FAMILY).setOrder(SortOrderEnum.DESC));
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
assertEquals(4, actual.size());
assertThat(actual, contains(id3, id2, id1, id4));
// The first would be better, but JPA doesn't do NULLS LAST
// assertThat(actual, contains(id3, id2, id1, id4));
assertThat(actual, contains(id4, id3, id2, id1));
}
/**
@ -3350,7 +3309,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
assertEquals("http://profile/1", profiles.get(0).getValue());
assertEquals("http://profile/2", profiles.get(1).getValue());
List<Patient> search = toList(myPatientDao.search(Patient.SP_IDENTIFIER, new TokenParam(patient.getIdentifier().get(0).getSystem(), patient.getIdentifier().get(0).getValue())));
List<Patient> search = toList(myPatientDao.search(new SearchParameterMap(Patient.SP_IDENTIFIER, new TokenParam(patient.getIdentifier().get(0).getSystem(), patient.getIdentifier().get(0).getValue())).setLoadSynchronous(true)));
assertEquals(1, search.size());
retrieved = search.get(0);

View File

@ -110,7 +110,7 @@ public class FhirResourceDaoDstu3UpdateTest extends BaseJpaDstu3Test {
IBundleProvider historyBundle = myPatientDao.history(outcome.getId(), null, null, mySrd);
assertEquals(2, historyBundle.size());
assertEquals(2, historyBundle.size().intValue());
List<IBaseResource> history = historyBundle.getResources(0, 2);

View File

@ -126,7 +126,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outputBundle));
IBundleProvider allPatients = myPatientDao.search(new SearchParameterMap());
assertEquals(1, allPatients.size());
assertEquals(1, allPatients.size().intValue());
}
@Test
@ -696,13 +696,13 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
SearchParameterMap map = new SearchParameterMap();
map.add(Organization.SP_PARTOF, new ReferenceParam(id1.toUnqualifiedVersionless().getValue()));
IBundleProvider res = myOrganizationDao.search(map);
assertEquals(1, res.size());
assertEquals(1, res.size().intValue());
assertEquals(id2.toUnqualifiedVersionless().getValue(), res.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless().getValue());
map = new SearchParameterMap();
map.add(Organization.SP_PARTOF, new ReferenceParam(id2.toUnqualifiedVersionless().getValue()));
res = myOrganizationDao.search(map);
assertEquals(1, res.size());
assertEquals(1, res.size().intValue());
assertEquals(id1.toUnqualifiedVersionless().getValue(), res.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless().getValue());
/*
@ -735,13 +735,13 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
map = new SearchParameterMap();
map.add(Organization.SP_PARTOF, new ReferenceParam(id1.toUnqualifiedVersionless().getValue()));
res = myOrganizationDao.search(map);
assertEquals(1, res.size());
assertEquals(1, res.size().intValue());
assertEquals(id1.toUnqualifiedVersionless().getValue(), res.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless().getValue());
map = new SearchParameterMap();
map.add(Organization.SP_PARTOF, new ReferenceParam(id2.toUnqualifiedVersionless().getValue()));
res = myOrganizationDao.search(map);
assertEquals(1, res.size());
assertEquals(1, res.size().intValue());
assertEquals(id2.toUnqualifiedVersionless().getValue(), res.getResources(0, 1).get(0).getIdElement().toUnqualifiedVersionless().getValue());
}
@ -1139,7 +1139,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
}
IBundleProvider history = myPatientDao.history(id, null, null, mySrd);
assertEquals(2, history.size());
assertEquals(2, history.size().intValue());
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());

View File

@ -39,6 +39,7 @@ import ca.uhn.fhir.jpa.dao.BaseJpaTest;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
@ -87,6 +88,7 @@ public class ResourceProviderDstu1Test extends BaseJpaTest {
private static CloseableHttpClient ourHttpClient;
private static EntityManager ourEntityManager;
private static PlatformTransactionManager ourTxManager;
private static ISearchParamPresenceSvc ourSearchParamPresenceSvc;
@AfterClass
public static void afterClassClearContext() throws Exception {
@ -551,6 +553,7 @@ public class ResourceProviderDstu1Test extends BaseJpaTest {
ourOrganizationDao = (IFhirResourceDao<Organization>) ourAppCtx.getBean("myOrganizationDaoDstu1", IFhirResourceDao.class);
ourEntityManager = ourAppCtx.getBean(EntityManager.class);
ourTxManager = ourAppCtx.getBean(PlatformTransactionManager.class);
ourSearchParamPresenceSvc = ourAppCtx.getBean(ISearchParamPresenceSvc.class);
List<IResourceProvider> rpsDev = (List<IResourceProvider>) ourAppCtx.getBean("myResourceProvidersDstu1", List.class);
restServer.setResourceProviders(rpsDev);
@ -593,7 +596,7 @@ public class ResourceProviderDstu1Test extends BaseJpaTest {
@Before
public void before() {
super.purgeDatabase(ourEntityManager, ourTxManager);
super.purgeDatabase(ourEntityManager, ourTxManager, ourSearchParamPresenceSvc);
}
}

View File

@ -1276,33 +1276,38 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
response.close();
}
get = new HttpGet(ourServerBase + "/Patient/" + pId.getIdPart() + "/$everything?_lastUpdated=%3E" + new InstantDt(new Date(time1)).getValueAsString() + "&_sort=_lastUpdated");
response = ourHttpClient.execute(get);
try {
assertEquals(200, response.getStatusLine().getStatusCode());
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(response.getEntity().getContent());
ourLog.info(output);
List<IdDt> ids = toIdListUnqualifiedVersionless(myFhirCtx.newXmlParser().parseBundle(output));
ourLog.info(ids.toString());
assertThat(ids, contains(pId, cId));
} finally {
response.close();
}
get = new HttpGet(ourServerBase + "/Patient/" + pId.getIdPart() + "/$everything?_sort:desc=_lastUpdated");
response = ourHttpClient.execute(get);
try {
assertEquals(200, response.getStatusLine().getStatusCode());
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(response.getEntity().getContent());
ourLog.info(output);
List<IdDt> ids = toIdListUnqualifiedVersionless(myFhirCtx.newXmlParser().parseBundle(output));
ourLog.info(ids.toString());
assertThat(ids, contains(cId, pId, oId));
} finally {
response.close();
}
/*
* Sorting is not working since the performance enhancements in 2.4 but
* sorting for lastupdated is non-standard anyhow.. Hopefully at some point
* we can bring this back
*/
// get = new HttpGet(ourServerBase + "/Patient/" + pId.getIdPart() + "/$everything?" + "_sort=_lastUpdated");
// response = ourHttpClient.execute(get);
// try {
// assertEquals(200, response.getStatusLine().getStatusCode());
// String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
// IOUtils.closeQuietly(response.getEntity().getContent());
// ourLog.info(output);
// List<IdDt> ids = toIdListUnqualifiedVersionless(myFhirCtx.newXmlParser().parseBundle(output));
// ourLog.info(ids.toString());
// assertThat(ids, contains(pId, cId));
// } finally {
// response.close();
// }
//
// get = new HttpGet(ourServerBase + "/Patient/" + pId.getIdPart() + "/$everything?_sort:desc=_lastUpdated");
// response = ourHttpClient.execute(get);
// try {
// assertEquals(200, response.getStatusLine().getStatusCode());
// String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
// IOUtils.closeQuietly(response.getEntity().getContent());
// ourLog.info(output);
// List<IdDt> ids = toIdListUnqualifiedVersionless(myFhirCtx.newXmlParser().parseBundle(output));
// ourLog.info(ids.toString());
// assertThat(ids, contains(cId, pId, oId));
// } finally {
// response.close();
// }
}

View File

@ -1,11 +1,15 @@
package ca.uhn.fhir.jpa.provider;
import static java.util.Collections.addAll;
import static org.junit.Assert.assertEquals;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.util.TestUtil;
public class SearchParameterMapTest {
@ -15,6 +19,29 @@ public class SearchParameterMapTest {
TestUtil.clearAllStaticFieldsForUnitTest();
}
private static FhirContext ourCtx = FhirContext.forDstu3();
@Test
public void testToQueryString() {
SearchParameterMap map = new SearchParameterMap();
StringAndListParam familyAnd = new StringAndListParam()
.addAnd(new StringOrListParam().add(new StringParam("ZZZ?").setExact(true)))
.addAnd(new StringOrListParam().add(new StringParam("homer")).add(new StringParam("jay")))
.addAnd(new StringOrListParam().add(new StringParam("simpson")).add(new StringParam("bouvier")));
map.add("name", familyAnd);
DateAndListParam birthdateAnd = new DateAndListParam()
.addAnd(new DateOrListParam().add(new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, "2001")))
.addAnd(new DateOrListParam().add(new DateParam(ParamPrefixEnum.LESSTHAN, "2002")));
map.add("birthdate", birthdateAnd);
String queryString = map.toNormalizedQueryString(ourCtx);
ourLog.info(queryString);
}
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParameterMapTest.class);
/**
* {@link Search} uses these ordinals so they shouldn't get out of order

View File

@ -31,6 +31,7 @@ import ca.uhn.fhir.jpa.config.dstu3.WebsocketDstu3Config;
import ca.uhn.fhir.jpa.config.dstu3.WebsocketDstu3DispatcherConfig;
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
@ -53,6 +54,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
protected static String ourServerBase;
private static GenericWebApplicationContext ourWebApplicationContext;
private TerminologyUploaderProviderDstu3 myTerminologyUploaderProvider;
protected static ISearchCoordinatorSvc mySearchCoordinatorSvc;
public BaseResourceProviderDstu3Test() {
super();
@ -138,6 +140,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(subsServletHolder.getServlet().getServletConfig().getServletContext());
myValidationSupport = wac.getBean(JpaValidationSupportChainDstu3.class);
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
ourClient = myFhirCtx.newRestfulGenericClient(ourServerBase);
ourClient.registerInterceptor(new LoggingInterceptor(true));

View File

@ -29,6 +29,9 @@ import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.DaoConfig;
@ -72,7 +75,6 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
}
}
@Test
public void testSearchForExtension() {
SearchParameter eyeColourSp = new SearchParameter();
@ -87,12 +89,12 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(eyeColourSp));
ourClient
.create()
.resource(eyeColourSp)
.execute();
// mySearchParamRegsitry.forceRefresh();
.create()
.resource(eyeColourSp)
.execute();
// mySearchParamRegsitry.forceRefresh();
Patient p1 = new Patient();
p1.setActive(true);
p1.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("blue"));
@ -106,21 +108,21 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
Bundle bundle = ourClient
.search()
.forResource(Patient.class)
.where(new TokenClientParam("eyecolour").exactly().code("blue"))
.returnBundle(Bundle.class)
.execute();
.search()
.forResource(Patient.class)
.where(new TokenClientParam("eyecolour").exactly().code("blue"))
.returnBundle(Bundle.class)
.execute();
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
List<String> foundResources = toUnqualifiedVersionlessIdValues(bundle);
assertThat(foundResources, contains(p1id.getValue()));
}
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderCustomSearchParamDstu3Test.class);
@Override
@Before
public void beforeResetConfig() {
@ -146,29 +148,45 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
param = map.get("gender");
assertNotNull(param);
// Add a custom search parameter
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
TransactionTemplate txTemplate = newTxTemplate();
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
// Add a custom search parameter
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
}
});
// Disable an existing parameter
fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("gender");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("Gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.RETIRED);
mySearchParameterDao.create(fooSp, mySrd);
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("gender");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("Gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.RETIRED);
mySearchParameterDao.create(fooSp, mySrd);
}
});
mySearchParamRegsitry.forceRefresh();
txTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
mySearchParamRegsitry.forceRefresh();
}
});
conformance = ourClient
.fetchConformance()
@ -187,7 +205,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
@Test
public void testConformanceOverrideNotAllowed() {
myDaoConfig.setDefaultSearchParamsCanBeOverridden(false);
CapabilityStatement conformance = ourClient
.fetchConformance()
.ofType(CapabilityStatement.class)
@ -288,7 +306,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
.where(new TokenClientParam("foo").exactly().code("male"))
.returnBundle(Bundle.class)
.execute();
foundResources = toUnqualifiedVersionlessIdValues(result);
assertThat(foundResources, contains(patId.getValue()));

View File

@ -308,9 +308,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
@Test
public void testCountParam() throws Exception {
// NB this does not get used- The paging provider has its own limits built in
myDaoConfig.setHardSearchLimit(100);
List<IBaseResource> resources = new ArrayList<IBaseResource>();
for (int i = 0; i < 100; i++) {
Organization org = new Organization();
@ -409,7 +406,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor();
assertTrue(interceptor.isAddValidationResultsToResponseOperationOutcome());
interceptor.setFailOnSeverity(null);
ourRestServer.registerInterceptor(interceptor);
try {
// Missing status, which is mandatory
@ -421,7 +418,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
ourLog.info(encodedOo);
assertThat(encodedOo, containsString("cvc-complex-type.2.4.b"));
assertThat(encodedOo, containsString("Successfully created resource \\\"Observation/"));
interceptor.setAddValidationResultsToResponseOperationOutcome(false);
outcome = ourClient.create().resource(obs).execute().getOperationOutcome();
encodedOo = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
@ -1463,33 +1460,38 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
response.close();
}
get = new HttpGet(ourServerBase + "/Patient/" + pId.getIdPart() + "/$everything?_lastUpdated=%3E" + new InstantType(new Date(time1)).getValueAsString() + "&_sort=_lastUpdated");
response = ourHttpClient.execute(get);
try {
assertEquals(200, response.getStatusLine().getStatusCode());
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(response.getEntity().getContent());
ourLog.info(output);
List<IIdType> ids = toUnqualifiedVersionlessIds(myFhirCtx.newXmlParser().parseResource(Bundle.class, output));
ourLog.info(ids.toString());
assertThat(ids, contains(pId, cId));
} finally {
response.close();
}
get = new HttpGet(ourServerBase + "/Patient/" + pId.getIdPart() + "/$everything?_sort:desc=_lastUpdated");
response = ourHttpClient.execute(get);
try {
assertEquals(200, response.getStatusLine().getStatusCode());
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(response.getEntity().getContent());
ourLog.info(output);
List<IIdType> ids = toUnqualifiedVersionlessIds(myFhirCtx.newXmlParser().parseResource(Bundle.class, output));
ourLog.info(ids.toString());
assertThat(ids, contains(cId, pId, oId));
} finally {
response.close();
}
/*
* Sorting is not working since the performance enhancements in 2.4 but
* sorting for lastupdated is non-standard anyhow.. Hopefully at some point
* we can bring this back
*/
// get = new HttpGet(ourServerBase + "/Patient/" + pId.getIdPart() + "/$everything?_lastUpdated=%3E" + new InstantType(new Date(time1)).getValueAsString() + "&_sort=_lastUpdated");
// response = ourHttpClient.execute(get);
// try {
// assertEquals(200, response.getStatusLine().getStatusCode());
// String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
// IOUtils.closeQuietly(response.getEntity().getContent());
// ourLog.info(output);
// List<IIdType> ids = toUnqualifiedVersionlessIds(myFhirCtx.newXmlParser().parseResource(Bundle.class, output));
// ourLog.info(ids.toString());
// assertThat(ids, contains(pId, cId));
// } finally {
// response.close();
// }
//
// get = new HttpGet(ourServerBase + "/Patient/" + pId.getIdPart() + "/$everything?_sort:desc=_lastUpdated");
// response = ourHttpClient.execute(get);
// try {
// assertEquals(200, response.getStatusLine().getStatusCode());
// String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
// IOUtils.closeQuietly(response.getEntity().getContent());
// ourLog.info(output);
// List<IIdType> ids = toUnqualifiedVersionlessIds(myFhirCtx.newXmlParser().parseResource(Bundle.class, output));
// ourLog.info(ids.toString());
// assertThat(ids, contains(cId, pId, oId));
// } finally {
// response.close();
// }
}
@ -1498,11 +1500,13 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
*/
@Test
public void testEverythingWithLargeSet() throws Exception {
String inputString = IOUtils.toString(getClass().getResourceAsStream("/david_big_bundle.json"), StandardCharsets.UTF_8);
Bundle inputBundle = myFhirCtx.newJsonParser().parseResource(Bundle.class, inputString);
inputBundle.setType(BundleType.TRANSACTION);
assertEquals(53, inputBundle.getEntry().size());
Set<String> allIds = new TreeSet<String>();
for (BundleEntryComponent nextEntry : inputBundle.getEntry()) {
nextEntry.getRequest().setMethod(HTTPVerb.PUT);
@ -1510,6 +1514,8 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
allIds.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
}
assertEquals(53, allIds.size());
mySystemDao.transaction(mySrd, inputBundle);
Bundle responseBundle = ourClient
@ -1522,6 +1528,9 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
.execute();
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(responseBundle));
// FIXME re-enable
// assertEquals(50, responseBundle.getEntry().size());
TreeSet<String> ids = new TreeSet<String>();
for (int i = 0; i < responseBundle.getEntry().size(); i++) {
@ -1530,13 +1539,13 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
}
String nextUrl = responseBundle.getLink("next").getUrl();
responseBundle = ourClient.fetchResourceFromUrl(Bundle.class, nextUrl);
for (int i = 0; i < responseBundle.getEntry().size(); i++) {
for (BundleEntryComponent nextEntry : responseBundle.getEntry()) {
ids.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
}
}
// String nextUrl = responseBundle.getLink("next").getUrl();
// responseBundle = ourClient.fetchResourceFromUrl(Bundle.class, nextUrl);
// for (int i = 0; i < responseBundle.getEntry().size(); i++) {
// for (BundleEntryComponent nextEntry : responseBundle.getEntry()) {
// ids.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
// }
// }
assertThat(ids, hasItem("List/A161444"));
assertThat(ids, hasItem("List/A161468"));
@ -1599,6 +1608,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
assertEquals(77, ids.size());
}
@SuppressWarnings("unused")
@Test
public void testFullTextSearch() throws RuntimeException, Exception {
Observation obs1 = new Observation();
@ -1908,6 +1918,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
}
@SuppressWarnings("unused")
@Test
public void testMetadataSuperParamsAreIncluded() throws IOException {
StructureDefinition p = new StructureDefinition();
@ -2169,7 +2180,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
}
@Test
public void testSaveAndRetrieveExistingNarrativeJson() {
Patient p1 = new Patient();
@ -2347,14 +2357,16 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
id2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
Bundle found = ourClient
Bundle found;
found = ourClient
.search()
.forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().systemAndValues(null, id1.getIdPart(), id2.getIdPart()))
.returnBundle(Bundle.class)
.execute();
assertThat(toUnqualifiedVersionlessIds(found), empty());
assertThat(toUnqualifiedVersionlessIds(found), containsInAnyOrder(id1, id2));
found = ourClient
.search()
@ -2363,7 +2375,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
.returnBundle(Bundle.class)
.execute();
assertThat(toUnqualifiedVersionlessIds(found), empty());
assertThat(toUnqualifiedVersionlessIds(found), containsInAnyOrder(id1, id2));
found = ourClient
.search()
@ -2372,7 +2384,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
.returnBundle(Bundle.class)
.execute();
assertThat(toUnqualifiedVersionlessIds(found), empty());
assertThat(toUnqualifiedVersionlessIds(found), containsInAnyOrder(id1));
found = ourClient
.search()
@ -2403,6 +2415,15 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
assertThat(toUnqualifiedVersionlessIds(found), containsInAnyOrder(id1, id2));
found = ourClient
.search()
.forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().codes("FOOO"))
.returnBundle(Bundle.class)
.execute();
assertThat(toUnqualifiedVersionlessIds(found), empty());
}
@Test
@ -2600,6 +2621,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
@SuppressWarnings("unused")
@Test
public void testSearchPagingKeepsOldSearches() throws Exception {
String methodName = "testSearchPagingKeepsOldSearches";
@ -3046,42 +3068,38 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
p.addName().addGiven("Sarah").setFamily("Graham");
ourClient.create().resource(p).execute();
//@formatter:off
Bundle resp = ourClient
.search()
.forResource(Patient.class)
.where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", methodName))
.sort().ascending(Patient.FAMILY)
.sort().ascending(Patient.GIVEN)
.count(100)
.returnBundle(Bundle.class)
.execute();
//@formatter:on
.search()
.forResource(Patient.class)
.where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", methodName))
.sort().ascending(Patient.FAMILY)
.sort().ascending(Patient.GIVEN)
.count(100)
.returnBundle(Bundle.class)
.execute();
List<String> names = toNameList(resp);
ourLog.info(StringUtils.join(names, '\n'));
//@formatter:off
assertThat(names, contains( // this matches in order only
"Daniel Adams",
"Aaron Alexis",
"Carol Allen",
"Ruth Black",
"Brian Brooks",
"Amy Clark",
"Susan Clark",
"Anthony Coleman",
"Lisa Coleman",
"Steven Coleman",
"Ruth Cook",
"Betty Davis",
"Joshua Diaz",
"Brian Gracia",
"Sarah Graham",
"Stephan Graham"));
//@formatter:om
"Daniel Adams",
"Aaron Alexis",
"Carol Allen",
"Ruth Black",
"Brian Brooks",
"Amy Clark",
"Susan Clark",
"Anthony Coleman",
"Lisa Coleman",
"Steven Coleman",
"Ruth Cook",
"Betty Davis",
"Joshua Diaz",
"Brian Gracia",
"Sarah Graham",
"Stephan Graham"));
}
/**
@ -3118,7 +3136,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
CloseableHttpResponse resp = ourHttpClient.execute(post);
try {
assertEquals(200, resp.getStatusLine().getStatusCode());
String output= IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
String output = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
} finally {
resp.close();
@ -3157,7 +3175,8 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
ourLog.info(responseString);
assertEquals(400, response.getStatusLine().getStatusCode());
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, responseString);
assertThat(oo.getIssue().get(0).getDiagnostics(), containsString("Can not update resource, request URL must contain an ID element for update (PUT) operation (it must be of the form [base]/[resource type]/[id])"));
assertThat(oo.getIssue().get(0).getDiagnostics(),
containsString("Can not update resource, request URL must contain an ID element for update (PUT) operation (it must be of the form [base]/[resource type]/[id])"));
} finally {
response.close();
}
@ -3223,10 +3242,10 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
// Try to update with the wrong ID in the resource body
p1.setId("FOO");
p1.addAddress().addLine("NEWLINE");
String encoded = myFhirCtx.newJsonParser().encodeResourceToString(p1);
ourLog.info(encoded);
HttpPut put = new HttpPut(ourServerBase + "/Patient/" + p1id.getIdPart());
put.setEntity(new StringEntity(encoded, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
put.addHeader("Accept", Constants.CT_FHIR_JSON);
@ -3234,14 +3253,17 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
try {
assertEquals(400, response.getStatusLine().getStatusCode());
OperationOutcome oo = myFhirCtx.newJsonParser().parseResource(OperationOutcome.class, new InputStreamReader(response.getEntity().getContent()));
assertEquals("Can not update resource, resource body must contain an ID element which matches the request URL for update (PUT) operation - Resource body ID of \"FOO\" does not match URL ID of \"" + p1id.getIdPart() + "\"", oo.getIssue().get(0).getDiagnostics());
assertEquals(
"Can not update resource, resource body must contain an ID element which matches the request URL for update (PUT) operation - Resource body ID of \"FOO\" does not match URL ID of \""
+ p1id.getIdPart() + "\"",
oo.getIssue().get(0).getDiagnostics());
} finally {
response.close();
}
// Try to update with the no ID in the resource body
p1.setId((String)null);
p1.setId((String) null);
encoded = myFhirCtx.newJsonParser().encodeResourceToString(p1);
put = new HttpPut(ourServerBase + "/Patient/" + p1id.getIdPart());
put.setEntity(new StringEntity(encoded, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
@ -3257,7 +3279,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
// Try to update with the to correct ID in the resource body
p1.setId(p1id.getIdPart());
encoded = myFhirCtx.newJsonParser().encodeResourceToString(p1);
put = new HttpPut(ourServerBase + "/Patient/" + p1id.getIdPart());
put.setEntity(new StringEntity(encoded, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
@ -3270,7 +3292,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
@Test
public void testUpdateRejectsInvalidTypes() throws InterruptedException {

View File

@ -0,0 +1,101 @@
package ca.uhn.fhir.jpa.search;
import static org.apache.commons.lang3.StringUtils.leftPad;
import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.assertThat;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Patient;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Test;
import org.springframework.cglib.proxy.Proxy;
import org.springframework.test.util.AopTestUtils;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.util.TestUtil;
public class PagingMultinodeProviderDstu3Test extends BaseResourceProviderDstu3Test {
private SearchCoordinatorSvcImpl mySearchCoordinatorSvcRaw;
@Override
@After
public void after() throws Exception {
super.after();
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
mySearchCoordinatorSvcRaw.setLoadingThrottleForUnitTests(null);
mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE);
mySearchCoordinatorSvcRaw.setNeverUseLocalSearchForUnitTests(false);
}
@Override
public void before() throws Exception {
super.before();
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
myDaoConfig.setAllowMultipleDelete(true);
mySearchCoordinatorSvcRaw = AopTestUtils.getTargetObject(mySearchCoordinatorSvc);
// mySearchCoordinatorSvcRaw = (SearchCoordinatorSvcImpl)mySearchCoordinatorSvc;
}
@Test
public void testSearch() {
{
for (int i = 0; i < 100; i++) {
Patient patient = new Patient();
String id = "A" + leftPad(Integer.toString(i), 3, '0');
patient.setId(id);
patient.addIdentifier().setSystem("urn:system").setValue("A" + i);
patient.addName().setFamily(id);
myPatientDao.update(patient, mySrd).getId().toUnqualifiedVersionless();
}
}
Bundle found;
mySearchCoordinatorSvcRaw.setLoadingThrottleForUnitTests(50);
mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(10);
mySearchCoordinatorSvcRaw.setNeverUseLocalSearchForUnitTests(true);
found = ourClient
.search()
.forResource(Patient.class)
.sort().ascending(Patient.SP_FAMILY)
.count(10)
.returnBundle(Bundle.class)
.execute();
assertThat(toUnqualifiedVersionlessIdValues(found), contains("Patient/A000", "Patient/A001", "Patient/A002", "Patient/A003", "Patient/A004", "Patient/A005", "Patient/A006", "Patient/A007", "Patient/A008", "Patient/A009"));
found = ourClient
.loadPage()
.next(found)
.execute();
assertThat(toUnqualifiedVersionlessIdValues(found), contains("Patient/A010", "Patient/A011", "Patient/A012", "Patient/A013", "Patient/A014", "Patient/A015", "Patient/A016", "Patient/A017", "Patient/A018", "Patient/A019"));
found = ourClient
.loadPage()
.next(found)
.execute();
assertThat(toUnqualifiedVersionlessIdValues(found), contains("Patient/A020", "Patient/A021", "Patient/A022", "Patient/A023", "Patient/A024", "Patient/A025", "Patient/A026", "Patient/A027", "Patient/A028", "Patient/A029"));
found = ourClient
.loadPage()
.next(found)
.execute();
assertThat(toUnqualifiedVersionlessIdValues(found), contains("Patient/A030", "Patient/A031", "Patient/A032", "Patient/A033", "Patient/A034", "Patient/A035", "Patient/A036", "Patient/A037", "Patient/A038", "Patient/A039"));
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -1,17 +0,0 @@
package ca.uhn.fhir.jpa.search;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.domain.Pageable;
public class PersistedJpaBundleProviderTest {
@Test
public void testGetPage() {
Pageable page = PersistedJpaBundleProvider.toPage(50, 73);
assertEquals(50, page.getOffset());
// assertEquals(50, page.get);
}
}

View File

@ -0,0 +1,468 @@
package ca.uhn.fhir.jpa.search;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.*;
import javax.persistence.EntityManager;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.PlatformTransactionManager;
import com.google.common.collect.Lists;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IDao;
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.entity.SearchResult;
import ca.uhn.fhir.jpa.entity.SearchStatusEnum;
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.TestUtil;
@SuppressWarnings({ "unchecked" })
@RunWith(MockitoJUnitRunner.class)
public class SearchCoordinatorSvcImplTest {
private static FhirContext ourCtx = FhirContext.forDstu3();
@Mock
private IDao myCallingDao;
@Mock
private EntityManager myEntityManager;
private int myExpectedNumberOfSearchBuildersCreated = 2;
@Mock
private ISearchBuilder mySearchBuider;
@Mock
private ISearchDao mySearchDao;
@Mock
private ISearchIncludeDao mySearchIncludeDao;
@Mock
private ISearchResultDao mySearchResultDao;
@Captor
ArgumentCaptor<Iterable<SearchResult>> mySearchResultIterCaptor;
private SearchCoordinatorSvcImpl mySvc;
@Mock
private PlatformTransactionManager myTxManager;
@After
public void after() {
verify(myCallingDao, atMost(myExpectedNumberOfSearchBuildersCreated)).newSearchBuilder();
}
@Before
public void before() {
mySvc = new SearchCoordinatorSvcImpl();
mySvc.setEntityManagerForUnitTest(myEntityManager);
mySvc.setTransactionManagerForUnitTest(myTxManager);
mySvc.setContextForUnitTest(ourCtx);
mySvc.setSearchDaoForUnitTest(mySearchDao);
mySvc.setSearchDaoIncludeForUnitTest(mySearchIncludeDao);
mySvc.setSearchDaoResultForUnitTest(mySearchResultDao);
when(myCallingDao.newSearchBuilder()).thenReturn(mySearchBuider);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock theInvocation) throws Throwable {
PersistedJpaBundleProvider provider = (PersistedJpaBundleProvider) theInvocation.getArguments()[0];
provider.setSearchCoordinatorSvc(mySvc);
provider.setPlatformTransactionManager(myTxManager);
provider.setSearchDao(mySearchDao);
provider.setEntityManager(myEntityManager);
provider.setContext(ourCtx);
return null;
}}).when(myCallingDao).injectDependenciesIntoBundleProvider(any(PersistedJpaBundleProvider.class));
}
private List<Long> createPidSequence(int from, int to) {
List<Long> pids = new ArrayList<Long>();
for (long i = from; i < to; i++) {
pids.add(i);
}
return pids;
}
private Answer<Void> loadPids() {
Answer<Void> retVal = new Answer<Void>() {
@Override
public Void answer(InvocationOnMock theInvocation) throws Throwable {
List<Long> pids = (List<Long>) theInvocation.getArguments()[0];
List<IBaseResource> resources = (List<IBaseResource>) theInvocation.getArguments()[1];
for (Long nextPid : pids) {
Patient pt = new Patient();
pt.setId(nextPid.toString());
resources.add(pt);
}
return null;
}
};
return retVal;
}
@Test
public void testAsyncSearchFailDuringSearchSameCoordinator() {
SearchParameterMap params = new SearchParameterMap();
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 800);
Iterator<Long> iter = new FailAfterNIterator<Long>(new SlowIterator<Long>(pids.iterator(), 2), 300);
when(mySearchBuider.createQuery(Mockito.same(params))).thenReturn(iter);
doAnswer(loadPids()).when(mySearchBuider).loadResourcesByPid(any(List.class), any(List.class), any(Set.class), anyBoolean(), any(EntityManager.class), any(FhirContext.class), same(myCallingDao));
IBundleProvider result = mySvc.registerSearch(myCallingDao, params, "Patient");
assertNotNull(result.getUuid());
assertEquals(null, result.size());
try {
result.getResources(0, 100000);
} catch (InternalErrorException e) {
assertEquals("FAILED", e.getMessage());
}
}
@Test
public void testAsyncSearchLargeResultSetBigCountSameCoordinator() {
SearchParameterMap params = new SearchParameterMap();
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 800);
Iterator<Long> iter = new SlowIterator<Long>(pids.iterator(), 2);
when(mySearchBuider.createQuery(Mockito.same(params))).thenReturn(iter);
doAnswer(loadPids()).when(mySearchBuider).loadResourcesByPid(any(List.class), any(List.class), any(Set.class), anyBoolean(), any(EntityManager.class), any(FhirContext.class), same(myCallingDao));
IBundleProvider result = mySvc.registerSearch(myCallingDao, params, "Patient");
assertNotNull(result.getUuid());
assertEquals(null, result.size());
List<IBaseResource> resources;
resources = result.getResources(0, 100000);
assertEquals(790, resources.size());
assertEquals("10", resources.get(0).getIdElement().getValueAsString());
assertEquals("799", resources.get(789).getIdElement().getValueAsString());
ArgumentCaptor<Search> searchCaptor = ArgumentCaptor.forClass(Search.class);
verify(mySearchDao, atLeastOnce()).save(searchCaptor.capture());
verify(mySearchResultDao, atLeastOnce()).save(mySearchResultIterCaptor.capture());
List<SearchResult> allResults= new ArrayList<SearchResult>();
for (Iterable<SearchResult> next : mySearchResultIterCaptor.getAllValues()) {
allResults.addAll(Lists.newArrayList(next));
}
assertEquals(790, allResults.size());
assertEquals(10, allResults.get(0).getResourcePid().longValue());
assertEquals(799, allResults.get(789).getResourcePid().longValue());
}
@Test
public void testAsyncSearchLargeResultSetSameCoordinator() {
SearchParameterMap params = new SearchParameterMap();
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 800);
SlowIterator<Long> iter = new SlowIterator<Long>(pids.iterator(), 2);
when(mySearchBuider.createQuery(Mockito.same(params))).thenReturn(iter);
doAnswer(loadPids()).when(mySearchBuider).loadResourcesByPid(any(List.class), any(List.class), any(Set.class), anyBoolean(), any(EntityManager.class), any(FhirContext.class), same(myCallingDao));
IBundleProvider result = mySvc.registerSearch(myCallingDao, params, "Patient");
assertNotNull(result.getUuid());
assertEquals(null, result.size());
List<IBaseResource> resources;
resources = result.getResources(0, 30);
assertEquals(30, resources.size());
assertEquals("10", resources.get(0).getIdElement().getValueAsString());
assertEquals("39", resources.get(29).getIdElement().getValueAsString());
}
/**
* Subsequent requests for the same search (i.e. a request for the next
* page) within the same JVM will not use the original bundle provider
*/
@Test
public void testAsyncSearchLargeResultSetSecondRequestSameCoordinator() {
SearchParameterMap params = new SearchParameterMap();
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 800);
Iterator<Long> iter = new SlowIterator<Long>(pids.iterator(), 2);
when(mySearchBuider.createQuery(Mockito.same(params))).thenReturn(iter);
doAnswer(loadPids()).when(mySearchBuider).loadResourcesByPid(any(List.class), any(List.class), any(Set.class), anyBoolean(), any(EntityManager.class), any(FhirContext.class), same(myCallingDao));
IBundleProvider result = mySvc.registerSearch(myCallingDao, params, "Patient");
assertNotNull(result.getUuid());
assertEquals(null, result.size());
ArgumentCaptor<Search> searchCaptor = ArgumentCaptor.forClass(Search.class);
verify(mySearchDao, atLeast(1)).save(searchCaptor.capture());
Search search = searchCaptor.getValue();
assertEquals(SearchTypeEnum.SEARCH, search.getSearchType());
List<IBaseResource> resources;
PersistedJpaBundleProvider provider;
resources = result.getResources(0, 10);
assertNull(result.size());
assertEquals(10, resources.size());
assertEquals("10", resources.get(0).getIdElement().getValueAsString());
assertEquals("19", resources.get(9).getIdElement().getValueAsString());
when(mySearchDao.findByUuid(eq(result.getUuid()))).thenReturn(search);
/*
* Now call from a new bundle provider. This simulates a separate HTTP
* client request coming in.
*/
provider = new PersistedJpaBundleProvider(result.getUuid(), myCallingDao);
resources = provider.getResources(10, 20);
assertEquals(10, resources.size());
assertEquals("20", resources.get(0).getIdElement().getValueAsString());
assertEquals("29", resources.get(9).getIdElement().getValueAsString());
provider = new PersistedJpaBundleProvider(result.getUuid(), myCallingDao);
resources = provider.getResources(20, 99999);
assertEquals(770, resources.size());
assertEquals("30", resources.get(0).getIdElement().getValueAsString());
assertEquals("799", resources.get(769).getIdElement().getValueAsString());
myExpectedNumberOfSearchBuildersCreated = 4;
}
@Test
public void testAsyncSearchSmallResultSetSameCoordinator() {
SearchParameterMap params = new SearchParameterMap();
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 100);
SlowIterator<Long> iter = new SlowIterator<Long>(pids.iterator(), 2);
when(mySearchBuider.createQuery(Mockito.same(params))).thenReturn(iter);
doAnswer(loadPids()).when(mySearchBuider).loadResourcesByPid(any(List.class), any(List.class), any(Set.class), anyBoolean(), any(EntityManager.class), any(FhirContext.class), same(myCallingDao));
IBundleProvider result = mySvc.registerSearch(myCallingDao, params, "Patient");
assertNotNull(result.getUuid());
assertEquals(90, result.size().intValue());
List<IBaseResource> resources = result.getResources(0, 30);
assertEquals(30, resources.size());
assertEquals("10", resources.get(0).getIdElement().getValueAsString());
assertEquals("39", resources.get(29).getIdElement().getValueAsString());
}
@Test
public void testGetPage() {
Pageable page = SearchCoordinatorSvcImpl.toPage(50, 73);
assertEquals(50, page.getOffset());
}
@Test
public void testLoadSearchResultsFromDifferentCoordinator() {
final String uuid = UUID.randomUUID().toString();
final Search search = new Search();
search.setUuid(uuid);
search.setSearchType(SearchTypeEnum.SEARCH);
search.setResourceType("Patient");
when(mySearchDao.findByUuid(eq(uuid))).thenReturn(search);
doAnswer(loadPids()).when(mySearchBuider).loadResourcesByPid(any(List.class), any(List.class), any(Set.class), anyBoolean(), any(EntityManager.class), any(FhirContext.class), same(myCallingDao));
PersistedJpaBundleProvider provider;
List<IBaseResource> resources;
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// ignore
}
when(mySearchResultDao.findWithSearchUuid(any(Search.class), any(Pageable.class))).thenAnswer(new Answer<Page<SearchResult>>() {
@Override
public Page<SearchResult> answer(InvocationOnMock theInvocation) throws Throwable {
Pageable page = (Pageable) theInvocation.getArguments()[1];
ArrayList<SearchResult> results = new ArrayList<SearchResult>();
int max = (page.getPageNumber() * page.getPageSize()) + page.getPageSize();
for (int i = page.getOffset(); i < max; i++) {
results.add(new SearchResult().setResourcePid(i + 10L));
}
return new PageImpl<SearchResult>(results);
}});
search.setStatus(SearchStatusEnum.FINISHED);
}
}.start();
/*
* Now call from a new bundle provider. This simulates a separate HTTP
* client request coming in.
*/
provider = new PersistedJpaBundleProvider(uuid, myCallingDao);
resources = provider.getResources(10, 20);
assertEquals(10, resources.size());
assertEquals("20", resources.get(0).getIdElement().getValueAsString());
assertEquals("29", resources.get(9).getIdElement().getValueAsString());
provider = new PersistedJpaBundleProvider(uuid, myCallingDao);
resources = provider.getResources(20, 40);
assertEquals(20, resources.size());
assertEquals("30", resources.get(0).getIdElement().getValueAsString());
assertEquals("49", resources.get(19).getIdElement().getValueAsString());
myExpectedNumberOfSearchBuildersCreated = 3;
}
@Test
public void testSynchronousSearch() {
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronous(true);
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 800);
when(mySearchBuider.createQuery(Mockito.same(params))).thenReturn(pids.iterator());
doAnswer(loadPids()).when(mySearchBuider).loadResourcesByPid(eq(pids), any(List.class), any(Set.class), anyBoolean(), any(EntityManager.class), any(FhirContext.class), same(myCallingDao));
IBundleProvider result = mySvc.registerSearch(myCallingDao, params, "Patient");
assertNull(result.getUuid());
assertEquals(790, result.size().intValue());
List<IBaseResource> resources = result.getResources(0, 10000);
assertEquals(790, resources.size());
assertEquals("10", resources.get(0).getIdElement().getValueAsString());
assertEquals("799", resources.get(789).getIdElement().getValueAsString());
}
@Test
public void testSynchronousSearchUpTo() {
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(100);
params.add("name", new StringParam("ANAME"));
List<Long> pids = createPidSequence(10, 800);
when(mySearchBuider.createQuery(Mockito.same(params))).thenReturn(pids.iterator());
pids = createPidSequence(10, 110);
doAnswer(loadPids()).when(mySearchBuider).loadResourcesByPid(eq(pids), any(List.class), any(Set.class), anyBoolean(), any(EntityManager.class), any(FhirContext.class), same(myCallingDao));
IBundleProvider result = mySvc.registerSearch(myCallingDao, params, "Patient");
assertNull(result.getUuid());
assertEquals(100, result.size().intValue());
List<IBaseResource> resources = result.getResources(0, 10000);
assertEquals(100, resources.size());
assertEquals("10", resources.get(0).getIdElement().getValueAsString());
assertEquals("109", resources.get(99).getIdElement().getValueAsString());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
public static class FailAfterNIterator<T> implements Iterator<T> {
private int myCount;
private Iterator<T> myWrap;
public FailAfterNIterator(Iterator<T> theWrap, int theCount) {
myWrap = theWrap;
myCount = theCount;
}
@Override
public boolean hasNext() {
return myWrap.hasNext();
}
@Override
public T next() {
myCount--;
if (myCount == 0) {
throw new NullPointerException("FAILED");
}
return myWrap.next();
}
}
public static class SlowIterator<T> implements Iterator<T> {
private int myDelay;
private Iterator<T> myWrap;
public SlowIterator(Iterator<T> theWrap, int theDelay) {
myWrap = theWrap;
myDelay = theDelay;
}
@Override
public boolean hasNext() {
return myWrap.hasNext();
}
@Override
public T next() {
try {
Thread.sleep(myDelay);
} catch (InterruptedException e) {
// ignore
}
return myWrap.next();
}
}
}

View File

@ -34,7 +34,7 @@
</logger>
<!-- Set to 'trace' to enable SQL logging -->
<logger name="org.hibernate.SQL" additivity="false" level="info">
<logger name="org.hibernate.SQL" additivity="false" level="trace">
<appender-ref ref="STDOUT" />
</logger>
<!-- Set to 'trace' to enable SQL Value logging -->

View File

@ -20,17 +20,6 @@
<version>${project.version}</version>
</dependency>
<!--
The JPA project uses a newer API but we'll try to hold to this version
as much as possible. See #283.
-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<!-- Testing -->
<dependency>
<groupId>xmlunit</groupId>
@ -150,6 +139,22 @@
<artifactId>guava</artifactId>
<scope>test</scope>
</dependency>
<!--
The JPA project uses a newer API but we'll try to hold to this version
as much as possible. See #283.
This dependency comes last so that the newer version brought in by
Jetty for unit tests comes first on the classpath (since jetty
needs that newer version. The unit tests are slooooow otherwise.
-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View File

@ -60,6 +60,23 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
myContext = theContext;
}
private void addProfileIfNeeded(IRestfulServer<?> theServer, String theServerBase, IBaseResource nextRes) {
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardType()) {
TagList tl = ResourceMetadataKeyEnum.TAG_LIST.get((IResource) nextRes);
if (tl == null) {
tl = new TagList();
ResourceMetadataKeyEnum.TAG_LIST.put((IResource) nextRes, tl);
}
RuntimeResourceDefinition nextDef = myContext.getResourceDefinition(nextRes);
String profile = nextDef.getResourceProfile(theServerBase);
if (isNotBlank(profile)) {
tl.add(new Tag(Tag.HL7_ORG_PROFILE_TAG, profile, null));
}
}
}
@Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
if (myBundle == null) {
@ -149,7 +166,7 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
myBundle.getAuthorName().setValue(theAuthor);
}
if (myBundle.getUpdated().isEmpty() && isNotBlank(theLastUpdated.getValueAsString())) {
if (theLastUpdated != null && myBundle.getUpdated().isEmpty() && isNotBlank(theLastUpdated.getValueAsString())) {
myBundle.getUpdated().setValueAsString(theLastUpdated.getValueAsString());
}
@ -190,27 +207,31 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
int numToReturn;
String searchId = null;
List<IBaseResource> resourceList;
Integer numTotalResults = theResult.size();
if (theServer.getPagingProvider() == null) {
numToReturn = theResult.size();
numToReturn = numTotalResults;
resourceList = theResult.getResources(0, numToReturn);
RestfulServerUtils.validateResourceListNotNull(resourceList);
} else {
IPagingProvider pagingProvider = theServer.getPagingProvider();
if (theLimit == null) {
if (theLimit == null || theLimit.equals(Integer.valueOf(0))) {
numToReturn = pagingProvider.getDefaultPageSize();
} else {
numToReturn = Math.min(pagingProvider.getMaximumPageSize(), theLimit);
}
numToReturn = Math.min(numToReturn, theResult.size() - theOffset);
if (numTotalResults != null) {
numToReturn = Math.min(numToReturn, numTotalResults - theOffset);
}
resourceList = theResult.getResources(theOffset, numToReturn + theOffset);
RestfulServerUtils.validateResourceListNotNull(resourceList);
if (theSearchId != null) {
searchId = theSearchId;
} else {
if (theResult.size() > numToReturn) {
if (numTotalResults == null || numTotalResults > numToReturn) {
searchId = pagingProvider.storeResultList(theResult);
Validate.notNull(searchId, "Paging provider returned null searchId");
}
@ -230,7 +251,7 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes);
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, theResult.getPublished());
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, numTotalResults, theBundleType, theResult.getPublished());
if (theServer.getPagingProvider() != null) {
int limit;
@ -238,7 +259,7 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
limit = Math.min(limit, theServer.getPagingProvider().getMaximumPageSize());
if (searchId != null) {
if (theOffset + numToReturn < theResult.size()) {
if (numTotalResults == null || theOffset + numToReturn < numTotalResults) {
myBundle.getLinkNext()
.setValue(RestfulServerUtils.createPagingLink(theIncludes, theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint, theBundleType));
}
@ -250,23 +271,6 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
}
}
private void addProfileIfNeeded(IRestfulServer<?> theServer, String theServerBase, IBaseResource nextRes) {
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardType()) {
TagList tl = ResourceMetadataKeyEnum.TAG_LIST.get((IResource) nextRes);
if (tl == null) {
tl = new TagList();
ResourceMetadataKeyEnum.TAG_LIST.put((IResource) nextRes, tl);
}
RuntimeResourceDefinition nextDef = myContext.getResourceDefinition(nextRes);
String profile = nextDef.getResourceProfile(theServerBase);
if (isNotBlank(profile)) {
tl.add(new Tag(Tag.HL7_ORG_PROFILE_TAG, profile, null));
}
}
}
@Override
public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults,
BundleTypeEnum theBundleType) {

View File

@ -0,0 +1,186 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import com.google.common.collect.Lists;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class SearchBundleProviderWithNoSizeDstu1Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu1();
private static TokenAndListParam ourIdentifiers;
private static IBundleProvider ourLastBundleProvider;
private static String ourLastMethod;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBundleProviderWithNoSizeDstu1Test.class);
private static int ourPort;
private static Server ourServer;
@Before
public void before() {
ourLastMethod = null;
ourIdentifiers = null;
}
@Test
public void testBundleProviderReturnsNoSize() throws Exception {
Bundle respBundle;
ourLastBundleProvider = mock(IBundleProvider.class);
when(ourLastBundleProvider.size()).thenReturn(null);
when(ourLastBundleProvider.getResources(any(int.class), any(int.class))).then(new Answer<List<IBaseResource>>() {
@Override
public List<IBaseResource> answer(InvocationOnMock theInvocation) throws Throwable {
int from =(Integer)theInvocation.getArguments()[0];
int to =(Integer)theInvocation.getArguments()[1];
ArrayList<IBaseResource> retVal = Lists.newArrayList();
for (int i = from; i < to; i++) {
Patient p = new Patient();
p.setId(Integer.toString(i));
retVal.add(p);
}
return retVal;
}});
HttpGet httpGet;
CloseableHttpResponse status = null;
StringDt linkNext;
try {
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json");
status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("searchAll", ourLastMethod);
respBundle = ourCtx.newJsonParser().parseBundle(responseContent);
assertEquals(10, respBundle.getEntries().size());
assertEquals("Patient/0", respBundle.getEntries().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue());
linkNext = respBundle.getLinkNext();
assertNotNull(linkNext.getValue());
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
when(ourLastBundleProvider.size()).thenReturn(25);
try {
httpGet = new HttpGet(linkNext.getValue());
status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("searchAll", ourLastMethod);
respBundle = ourCtx.newJsonParser().parseBundle(responseContent);
assertEquals(10, respBundle.getEntries().size());
assertEquals("Patient/10", respBundle.getEntries().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue());
linkNext = respBundle.getLinkNext();
assertNotNull(linkNext.getValue());
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
try {
httpGet = new HttpGet(linkNext.getValue());
status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("searchAll", ourLastMethod);
respBundle = ourCtx.newJsonParser().parseBundle(responseContent);
assertEquals(5, respBundle.getEntries().size());
assertEquals("Patient/20", respBundle.getEntries().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue());
linkNext = respBundle.getLinkNext();
assertNull(linkNext.getValue());
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setPagingProvider(new FifoMemoryPagingProvider(10));
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class DummyPatientResourceProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Search()
public IBundleProvider searchAll() {
ourLastMethod = "searchAll";
return ourLastBundleProvider;
}
}
}

View File

@ -306,8 +306,9 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
int numToReturn;
String searchId = null;
List<IBaseResource> resourceList;
Integer numTotalResults = theResult.size();
if (theServer.getPagingProvider() == null) {
numToReturn = theResult.size();
numToReturn = numTotalResults;
if (numToReturn > 0) {
resourceList = theResult.getResources(0, numToReturn);
} else {
@ -317,13 +318,16 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
} else {
IPagingProvider pagingProvider = theServer.getPagingProvider();
if (theLimit == null) {
if (theLimit == null || theLimit.equals(Integer.valueOf(0))) {
numToReturn = pagingProvider.getDefaultPageSize();
} else {
numToReturn = Math.min(pagingProvider.getMaximumPageSize(), theLimit);
}
numToReturn = Math.min(numToReturn, theResult.size() - theOffset);
if (numTotalResults != null) {
numToReturn = Math.min(numToReturn, numTotalResults - theOffset);
}
if (numToReturn > 0) {
resourceList = theResult.getResources(theOffset, numToReturn + theOffset);
} else {
@ -334,7 +338,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
if (theSearchId != null) {
searchId = theSearchId;
} else {
if (theResult.size() > numToReturn) {
if (numTotalResults == null || numTotalResults > numToReturn) {
searchId = pagingProvider.storeResultList(theResult);
Validate.notNull(searchId, "Paging provider returned null searchId");
}
@ -350,7 +354,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
}
addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes);
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, theResult.getPublished());
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, numTotalResults, theBundleType, theResult.getPublished());
if (theServer.getPagingProvider() != null) {
int limit;
@ -358,7 +362,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
limit = Math.min(limit, theServer.getPagingProvider().getMaximumPageSize());
if (searchId != null) {
if (theOffset + numToReturn < theResult.size()) {
if (numTotalResults == null || theOffset + numToReturn < numTotalResults) {
myBundle.addLink().setRelation(Constants.LINK_NEXT)
.setUrl(RestfulServerUtils.createPagingLink(theIncludes, theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint, theBundleType));
}

View File

@ -0,0 +1,186 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import com.google.common.collect.Lists;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Link;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class SearchBundleProviderWithNoSizeDstu2Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu2();
private static TokenAndListParam ourIdentifiers;
private static IBundleProvider ourLastBundleProvider;
private static String ourLastMethod;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBundleProviderWithNoSizeDstu2Test.class);
private static int ourPort;
private static Server ourServer;
@Before
public void before() {
ourLastMethod = null;
ourIdentifiers = null;
}
@Test
public void testBundleProviderReturnsNoSize() throws Exception {
Bundle respBundle;
ourLastBundleProvider = mock(IBundleProvider.class);
when(ourLastBundleProvider.size()).thenReturn(null);
when(ourLastBundleProvider.getResources(any(int.class), any(int.class))).then(new Answer<List<IBaseResource>>() {
@Override
public List<IBaseResource> answer(InvocationOnMock theInvocation) throws Throwable {
int from =(Integer)theInvocation.getArguments()[0];
int to =(Integer)theInvocation.getArguments()[1];
ArrayList<IBaseResource> retVal = Lists.newArrayList();
for (int i = from; i < to; i++) {
Patient p = new Patient();
p.setId(Integer.toString(i));
retVal.add(p);
}
return retVal;
}});
HttpGet httpGet;
CloseableHttpResponse status = null;
Link linkNext;
try {
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json");
status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("searchAll", ourLastMethod);
respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, responseContent);
assertEquals(10, respBundle.getEntry().size());
assertEquals("Patient/0", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue());
linkNext = respBundle.getLink("next");
assertNotNull(linkNext);
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
when(ourLastBundleProvider.size()).thenReturn(25);
try {
httpGet = new HttpGet(linkNext.getUrl());
status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("searchAll", ourLastMethod);
respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, responseContent);
assertEquals(10, respBundle.getEntry().size());
assertEquals("Patient/10", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue());
linkNext = respBundle.getLink("next");
assertNotNull(linkNext);
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
try {
httpGet = new HttpGet(linkNext.getUrl());
status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("searchAll", ourLastMethod);
respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, responseContent);
assertEquals(5, respBundle.getEntry().size());
assertEquals("Patient/20", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue());
linkNext = respBundle.getLink("next");
assertNull(linkNext);
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setPagingProvider(new FifoMemoryPagingProvider(10));
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class DummyPatientResourceProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Search()
public IBundleProvider searchAll() {
ourLastMethod = "searchAll";
return ourLastBundleProvider;
}
}
}

View File

@ -572,7 +572,7 @@ public class SearchDstu2Test {
}
@Override
public int size() {
public Integer size() {
return 0;
}

View File

@ -10,7 +10,7 @@ package org.hl7.fhir.dstu3.hapi.rest.server;
* 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
* 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,
@ -45,388 +45,392 @@ import ca.uhn.fhir.util.ResourceReferenceInfo;
public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
private Bundle myBundle;
private FhirContext myContext;
private String myBase;
private Bundle myBundle;
private FhirContext myContext;
private String myBase;
public Dstu3BundleFactory(FhirContext theContext) {
myContext = theContext;
}
public Dstu3BundleFactory(FhirContext theContext) {
myContext = theContext;
}
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
private void addResourcesForSearch(List<? extends IBaseResource> theResult) {
List<IBaseResource> includedResources = new ArrayList<IBaseResource>();
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
for (IBaseResource next : theResult) {
if (next.getIdElement().isEmpty() == false) {
addedResourceIds.add(next.getIdElement());
}
}
for (IBaseResource next : theResult) {
if (next.getIdElement().isEmpty() == false) {
addedResourceIds.add(next.getIdElement());
}
}
for (IBaseResource nextBaseRes : theResult) {
Resource next = (Resource) nextBaseRes;
Set<String> containedIds = new HashSet<String>();
if (next instanceof DomainResource) {
for (Resource nextContained : ((DomainResource)next).getContained()) {
if (nextContained.getIdElement().isEmpty() == false) {
containedIds.add(nextContained.getIdElement().getValue());
}
}
}
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class);
do {
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
for (IBaseResource nextBaseRes : theResult) {
Resource next = (Resource) nextBaseRes;
Set<String> containedIds = new HashSet<String>();
if (next instanceof DomainResource) {
for (Resource nextContained : ((DomainResource) next).getContained()) {
if (nextContained.getIdElement().isEmpty() == false) {
containedIds.add(nextContained.getIdElement().getValue());
}
}
}
for (IBaseReference nextRef : references) {
IAnyResource nextRes = (IAnyResource) nextRef.getResource();
if (nextRes != null) {
if (nextRes.getIdElement().hasIdPart()) {
if (containedIds.contains(nextRes.getIdElement().getValue())) {
// Don't add contained IDs as top level resources
continue;
}
List<IBaseReference> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, IBaseReference.class);
do {
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
IIdType id = nextRes.getIdElement();
if (id.hasResourceType() == false) {
String resName = myContext.getResourceDefinition(nextRes).getName();
id = id.withResourceType(resName);
}
for (IBaseReference nextRef : references) {
IAnyResource nextRes = (IAnyResource) nextRef.getResource();
if (nextRes != null) {
if (nextRes.getIdElement().hasIdPart()) {
if (containedIds.contains(nextRes.getIdElement().getValue())) {
// Don't add contained IDs as top level resources
continue;
}
if (!addedResourceIds.contains(id)) {
addedResourceIds.add(id);
addedResourcesThisPass.add(nextRes);
}
IIdType id = nextRes.getIdElement();
if (id.hasResourceType() == false) {
String resName = myContext.getResourceDefinition(nextRes).getName();
id = id.withResourceType(resName);
}
}
}
}
if (!addedResourceIds.contains(id)) {
addedResourceIds.add(id);
addedResourcesThisPass.add(nextRes);
}
// Linked resources may themselves have linked resources
references = new ArrayList<IBaseReference>();
for (IAnyResource iResource : addedResourcesThisPass) {
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class);
references.addAll(newReferences);
}
}
}
}
includedResources.addAll(addedResourcesThisPass);
// Linked resources may themselves have linked resources
references = new ArrayList<IBaseReference>();
for (IAnyResource iResource : addedResourcesThisPass) {
List<IBaseReference> newReferences = myContext.newTerser().getAllPopulatedChildElementsOfType(iResource, IBaseReference.class);
references.addAll(newReferences);
}
} while (references.isEmpty() == false);
includedResources.addAll(addedResourcesThisPass);
BundleEntryComponent entry = myBundle.addEntry().setResource(next);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getId());
}
} while (references.isEmpty() == false);
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next);
if (httpVerb != null) {
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
entry.getRequest().getUrlElement().setValue(next.getId());
}
}
BundleEntryComponent entry = myBundle.addEntry().setResource(next);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getId());
}
/*
* Actually add the resources to the bundle
*/
for (IBaseResource next : includedResources) {
BundleEntryComponent entry = myBundle.addEntry();
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getIdElement().getValue());
}
}
}
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next);
if (httpVerb != null) {
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
entry.getRequest().getUrlElement().setValue(next.getId());
}
}
@Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
if (myBundle == null) {
myBundle = new Bundle();
}
/*
* Actually add the resources to the bundle
*/
for (IBaseResource next : includedResources) {
BundleEntryComponent entry = myBundle.addEntry();
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
if (next.getIdElement().hasBaseUrl()) {
entry.setFullUrl(next.getIdElement().getValue());
}
}
}
List<IAnyResource> includedResources = new ArrayList<IAnyResource>();
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
@Override
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
if (myBundle == null) {
myBundle = new Bundle();
}
for (IBaseResource next : theResult) {
if (next.getIdElement().isEmpty() == false) {
addedResourceIds.add(next.getIdElement());
}
}
List<IAnyResource> includedResources = new ArrayList<IAnyResource>();
Set<IIdType> addedResourceIds = new HashSet<IIdType>();
for (IBaseResource next : theResult) {
for (IBaseResource next : theResult) {
if (next.getIdElement().isEmpty() == false) {
addedResourceIds.add(next.getIdElement());
}
}
Set<String> containedIds = new HashSet<String>();
if (next instanceof DomainResource) {
for (Resource nextContained : ((DomainResource)next).getContained()) {
if (isNotBlank(nextContained.getId())) {
containedIds.add(nextContained.getId());
}
}
}
for (IBaseResource next : theResult) {
List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
do {
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
Set<String> containedIds = new HashSet<String>();
for (ResourceReferenceInfo nextRefInfo : references) {
if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) {
continue;
}
if (next instanceof DomainResource) {
for (Resource nextContained : ((DomainResource) next).getContained()) {
if (isNotBlank(nextContained.getId())) {
containedIds.add(nextContained.getId());
}
}
}
IAnyResource nextRes = (IAnyResource) nextRefInfo.getResourceReference().getResource();
if (nextRes != null) {
if (nextRes.getIdElement().hasIdPart()) {
if (containedIds.contains(nextRes.getIdElement().getValue())) {
// Don't add contained IDs as top level resources
continue;
}
List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
do {
List<IAnyResource> addedResourcesThisPass = new ArrayList<IAnyResource>();
IIdType id = nextRes.getIdElement();
if (id.hasResourceType() == false) {
String resName = myContext.getResourceDefinition(nextRes).getName();
id = id.withResourceType(resName);
}
for (ResourceReferenceInfo nextRefInfo : references) {
if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes)) {
continue;
}
if (!addedResourceIds.contains(id)) {
addedResourceIds.add(id);
addedResourcesThisPass.add(nextRes);
}
IAnyResource nextRes = (IAnyResource) nextRefInfo.getResourceReference().getResource();
if (nextRes != null) {
if (nextRes.getIdElement().hasIdPart()) {
if (containedIds.contains(nextRes.getIdElement().getValue())) {
// Don't add contained IDs as top level resources
continue;
}
}
}
}
IIdType id = nextRes.getIdElement();
if (id.hasResourceType() == false) {
String resName = myContext.getResourceDefinition(nextRes).getName();
id = id.withResourceType(resName);
}
includedResources.addAll(addedResourcesThisPass);
if (!addedResourceIds.contains(id)) {
addedResourceIds.add(id);
addedResourcesThisPass.add(nextRes);
}
// Linked resources may themselves have linked resources
references = new ArrayList<ResourceReferenceInfo>();
for (IAnyResource iResource : addedResourcesThisPass) {
List<ResourceReferenceInfo> newReferences = myContext.newTerser().getAllResourceReferences(iResource);
references.addAll(newReferences);
}
} while (references.isEmpty() == false);
}
}
}
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
Resource nextAsResource = (Resource)next;
IIdType id = populateBundleEntryFullUrl(next, entry);
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(nextAsResource);
if (httpVerb != null) {
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
if (id != null) {
entry.getRequest().setUrl(id.getValue());
}
}
String searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource);
if (searchMode != null) {
entry.getSearch().getModeElement().setValueAsString(searchMode);
}
}
includedResources.addAll(addedResourcesThisPass);
/*
* Actually add the resources to the bundle
*/
for (IAnyResource next : includedResources) {
BundleEntryComponent entry = myBundle.addEntry();
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
populateBundleEntryFullUrl(next, entry);
}
// Linked resources may themselves have linked resources
references = new ArrayList<ResourceReferenceInfo>();
for (IAnyResource iResource : addedResourcesThisPass) {
List<ResourceReferenceInfo> newReferences = myContext.newTerser().getAllResourceReferences(iResource);
references.addAll(newReferences);
}
} while (references.isEmpty() == false);
}
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
Resource nextAsResource = (Resource) next;
IIdType id = populateBundleEntryFullUrl(next, entry);
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(nextAsResource);
if (httpVerb != null) {
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
if (id != null) {
entry.getRequest().setUrl(id.getValue());
}
}
private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
IIdType idElement = null;
if (next.getIdElement().hasBaseUrl()) {
idElement = next.getIdElement();
entry.setFullUrl(idElement.toVersionless().getValue());
} else {
if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) {
idElement = next.getIdElement();
idElement = idElement.withServerBase(myBase, myContext.getResourceDefinition(next).getName());
entry.setFullUrl(idElement.toVersionless().getValue());
}
}
return idElement;
}
String searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource);
if (searchMode != null) {
entry.getSearch().getModeElement().setValueAsString(searchMode);
}
}
@Override
public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated) {
/*
* Actually add the resources to the bundle
*/
for (IAnyResource next : includedResources) {
BundleEntryComponent entry = myBundle.addEntry();
entry.setResource((Resource) next).getSearch().setMode(SearchEntryMode.INCLUDE);
populateBundleEntryFullUrl(next, entry);
}
myBase = theServerBase;
if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(UUID.randomUUID().toString());
}
}
if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) {
myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString());
}
private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
IIdType idElement = null;
if (next.getIdElement().hasBaseUrl()) {
idElement = next.getIdElement();
entry.setFullUrl(idElement.toVersionless().getValue());
} else {
if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) {
idElement = next.getIdElement();
idElement = idElement.withServerBase(myBase, myContext.getResourceDefinition(next).getName());
entry.setFullUrl(idElement.toVersionless().getValue());
}
}
return idElement;
}
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theCompleteUrl)) {
myBundle.addLink().setRelation("self").setUrl(theCompleteUrl);
}
@Override
public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated) {
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
}
myBase = theServerBase;
if (myBundle.getTotalElement().isEmpty() && theTotalResults != null) {
myBundle.getTotalElement().setValue(theTotalResults);
}
}
if (myBundle.getIdElement().isEmpty()) {
myBundle.setId(UUID.randomUUID().toString());
}
@Override
public ca.uhn.fhir.model.api.Bundle getDstu1Bundle() {
return null;
}
if (myBundle.getMeta().getLastUpdated() == null && theLastUpdated != null) {
myBundle.getMeta().getLastUpdatedElement().setValueAsString(theLastUpdated.getValueAsString());
}
@Override
public IBaseResource getResourceBundle() {
return myBundle;
}
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theCompleteUrl)) {
myBundle.addLink().setRelation("self").setUrl(theCompleteUrl);
}
private boolean hasLink(String theLinkType, Bundle theBundle) {
for (BundleLinkComponent next : theBundle.getLink()) {
if (theLinkType.equals(next.getRelation())) {
return true;
}
}
return false;
}
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
}
@Override
public void initializeBundleFromBundleProvider(IRestfulServer<?> theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl,
boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
myBase = theServerBase;
int numToReturn;
String searchId = null;
List<IBaseResource> resourceList;
if (theServer.getPagingProvider() == null) {
numToReturn = theResult.size();
if (numToReturn > 0) {
resourceList = theResult.getResources(0, numToReturn);
} else {
resourceList = Collections.emptyList();
}
RestfulServerUtils.validateResourceListNotNull(resourceList);
if (myBundle.getTotalElement().isEmpty() && theTotalResults != null) {
myBundle.getTotalElement().setValue(theTotalResults);
}
}
} else {
IPagingProvider pagingProvider = theServer.getPagingProvider();
if (theLimit == null) {
numToReturn = pagingProvider.getDefaultPageSize();
} else {
numToReturn = Math.min(pagingProvider.getMaximumPageSize(), theLimit);
}
@Override
public ca.uhn.fhir.model.api.Bundle getDstu1Bundle() {
return null;
}
numToReturn = Math.min(numToReturn, theResult.size() - theOffset);
if (numToReturn > 0) {
resourceList = theResult.getResources(theOffset, numToReturn + theOffset);
} else {
resourceList = Collections.emptyList();
}
RestfulServerUtils.validateResourceListNotNull(resourceList);
@Override
public IBaseResource getResourceBundle() {
return myBundle;
}
if (theSearchId != null) {
searchId = theSearchId;
} else {
if (theResult.size() > numToReturn) {
searchId = pagingProvider.storeResultList(theResult);
Validate.notNull(searchId, "Paging provider returned null searchId");
}
}
}
private boolean hasLink(String theLinkType, Bundle theBundle) {
for (BundleLinkComponent next : theBundle.getLink()) {
if (theLinkType.equals(next.getRelation())) {
return true;
}
}
return false;
}
for (IBaseResource next : resourceList) {
if (next.getIdElement() == null || next.getIdElement().isEmpty()) {
if (!(next instanceof BaseOperationOutcome)) {
throw new InternalErrorException("Server method returned resource of type[" + next.getClass().getSimpleName() + "] with no ID specified (IResource#setId(IdDt) must be called)");
}
}
}
@Override
public void initializeBundleFromBundleProvider(IRestfulServer<?> theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl,
boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
myBase = theServerBase;
addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes);
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, theResult.getPublished());
int numToReturn;
String searchId = null;
List<IBaseResource> resourceList;
Integer numTotalResults = theResult.size();
if (theServer.getPagingProvider() == null) {
numToReturn = numTotalResults;
if (numToReturn > 0) {
resourceList = theResult.getResources(0, numToReturn);
} else {
resourceList = Collections.emptyList();
}
RestfulServerUtils.validateResourceListNotNull(resourceList);
if (theServer.getPagingProvider() != null) {
int limit;
limit = theLimit != null ? theLimit : theServer.getPagingProvider().getDefaultPageSize();
limit = Math.min(limit, theServer.getPagingProvider().getMaximumPageSize());
} else {
IPagingProvider pagingProvider = theServer.getPagingProvider();
if (theLimit == null || theLimit.equals(Integer.valueOf(0))) {
numToReturn = pagingProvider.getDefaultPageSize();
} else {
numToReturn = Math.min(pagingProvider.getMaximumPageSize(), theLimit);
}
if (searchId != null) {
if (theOffset + numToReturn < theResult.size()) {
myBundle.addLink().setRelation(Constants.LINK_NEXT)
.setUrl(RestfulServerUtils.createPagingLink(theIncludes, theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint, theBundleType));
}
if (theOffset > 0) {
int start = Math.max(0, theOffset - limit);
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS)
.setUrl(RestfulServerUtils.createPagingLink(theIncludes, theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint, theBundleType));
}
}
}
}
if (numTotalResults != null) {
numToReturn = Math.min(numToReturn, numTotalResults - theOffset);
}
@Override
public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResources, String theServerBase, String theCompleteUrl, int theTotalResults,
BundleTypeEnum theBundleType) {
myBundle = new Bundle();
if (numToReturn > 0) {
resourceList = theResult.getResources(theOffset, numToReturn + theOffset);
} else {
resourceList = Collections.emptyList();
}
RestfulServerUtils.validateResourceListNotNull(resourceList);
myBundle.setId(UUID.randomUUID().toString());
if (theSearchId != null) {
searchId = theSearchId;
} else {
if (numTotalResults == null || numTotalResults > numToReturn) {
searchId = pagingProvider.storeResultList(theResult);
Validate.notNull(searchId, "Paging provider returned null searchId");
}
}
}
myBundle.getMeta().setLastUpdated(new Date());
for (IBaseResource next : resourceList) {
if (next.getIdElement() == null || next.getIdElement().isEmpty()) {
if (!(next instanceof BaseOperationOutcome)) {
throw new InternalErrorException("Server method returned resource of type[" + next.getClass().getSimpleName() + "] with no ID specified (IResource#setId(IdDt) must be called)");
}
}
}
myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase);
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl);
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
addResourcesToBundle(new ArrayList<IBaseResource>(resourceList), theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes);
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType, theResult.getPublished());
if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) {
for (IBaseResource nextBaseRes : theResources) {
Resource next = (Resource) nextBaseRes;
BundleEntryComponent nextEntry = myBundle.addEntry();
if (theServer.getPagingProvider() != null) {
int limit;
limit = theLimit != null ? theLimit : theServer.getPagingProvider().getDefaultPageSize();
limit = Math.min(limit, theServer.getPagingProvider().getMaximumPageSize());
nextEntry.setResource(next);
if (next.getIdElement().isEmpty()) {
nextEntry.getRequest().setMethod(HTTPVerb.POST);
} else {
nextEntry.getRequest().setMethod(HTTPVerb.PUT);
if (next.getIdElement().isAbsolute()) {
nextEntry.getRequest().setUrl(next.getId());
} else {
String resourceType = myContext.getResourceDefinition(next).getName();
nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(), next.getIdElement().getVersionIdPart()).getValue());
}
}
}
} else {
addResourcesForSearch(theResources);
}
if (searchId != null) {
if (numTotalResults == null || theOffset + numToReturn < numTotalResults) {
myBundle.addLink().setRelation(Constants.LINK_NEXT)
.setUrl(RestfulServerUtils.createPagingLink(theIncludes, theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint, theBundleType));
}
if (theOffset > 0) {
int start = Math.max(0, theOffset - limit);
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS)
.setUrl(RestfulServerUtils.createPagingLink(theIncludes, theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint, theBundleType));
}
}
}
}
myBundle.getTotalElement().setValue(theTotalResults);
}
@Override
public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResources, String theServerBase, String theCompleteUrl, int theTotalResults,
BundleTypeEnum theBundleType) {
myBundle = new Bundle();
@Override
public void initializeWithBundleResource(IBaseResource theBundle) {
myBundle = (Bundle) theBundle;
}
myBundle.setId(UUID.randomUUID().toString());
@Override
public List<IBaseResource> toListOfResources() {
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
for (BundleEntryComponent next : myBundle.getEntry()) {
if (next.getResource() != null) {
retVal.add(next.getResource());
} else if (next.getResponse().getLocationElement().isEmpty() == false) {
IdType id = new IdType(next.getResponse().getLocation());
String resourceType = id.getResourceType();
if (isNotBlank(resourceType)) {
IAnyResource res = (IAnyResource) myContext.getResourceDefinition(resourceType).newInstance();
res.setId(id);
retVal.add(res);
}
}
}
return retVal;
}
myBundle.getMeta().setLastUpdated(new Date());
myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase);
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl);
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
if (theBundleType.equals(BundleTypeEnum.TRANSACTION)) {
for (IBaseResource nextBaseRes : theResources) {
Resource next = (Resource) nextBaseRes;
BundleEntryComponent nextEntry = myBundle.addEntry();
nextEntry.setResource(next);
if (next.getIdElement().isEmpty()) {
nextEntry.getRequest().setMethod(HTTPVerb.POST);
} else {
nextEntry.getRequest().setMethod(HTTPVerb.PUT);
if (next.getIdElement().isAbsolute()) {
nextEntry.getRequest().setUrl(next.getId());
} else {
String resourceType = myContext.getResourceDefinition(next).getName();
nextEntry.getRequest().setUrl(new IdType(theServerBase, resourceType, next.getIdElement().getIdPart(), next.getIdElement().getVersionIdPart()).getValue());
}
}
}
} else {
addResourcesForSearch(theResources);
}
myBundle.getTotalElement().setValue(theTotalResults);
}
@Override
public void initializeWithBundleResource(IBaseResource theBundle) {
myBundle = (Bundle) theBundle;
}
@Override
public List<IBaseResource> toListOfResources() {
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
for (BundleEntryComponent next : myBundle.getEntry()) {
if (next.getResource() != null) {
retVal.add(next.getResource());
} else if (next.getResponse().getLocationElement().isEmpty() == false) {
IdType id = new IdType(next.getResponse().getLocation());
String resourceType = id.getResourceType();
if (isNotBlank(resourceType)) {
IAnyResource res = (IAnyResource) myContext.getResourceDefinition(resourceType).newInstance();
res.setId(id);
retVal.add(res);
}
}
}
return retVal;
}
}

View File

@ -0,0 +1,5 @@
[*.java]
charset = utf-8
indent_style = tab
indent_size = 3

View File

@ -0,0 +1,189 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Bundle.BundleLinkComponent;
import org.hl7.fhir.dstu3.model.HumanName;
import org.hl7.fhir.dstu3.model.OperationOutcome;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import com.google.common.collect.Lists;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
public class SearchBundleProviderWithNoSizeDstu3Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu3();
private static TokenAndListParam ourIdentifiers;
private static IBundleProvider ourLastBundleProvider;
private static String ourLastMethod;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBundleProviderWithNoSizeDstu3Test.class);
private static int ourPort;
private static Server ourServer;
@Before
public void before() {
ourLastMethod = null;
ourIdentifiers = null;
}
@Test
public void testBundleProviderReturnsNoSize() throws Exception {
Bundle respBundle;
ourLastBundleProvider = mock(IBundleProvider.class);
when(ourLastBundleProvider.size()).thenReturn(null);
when(ourLastBundleProvider.getResources(any(int.class), any(int.class))).then(new Answer<List<IBaseResource>>() {
@Override
public List<IBaseResource> answer(InvocationOnMock theInvocation) throws Throwable {
int from =(Integer)theInvocation.getArguments()[0];
int to =(Integer)theInvocation.getArguments()[1];
ArrayList<IBaseResource> retVal = Lists.newArrayList();
for (int i = from; i < to; i++) {
Patient p = new Patient();
p.setId(Integer.toString(i));
retVal.add(p);
}
return retVal;
}});
HttpGet httpGet;
CloseableHttpResponse status = null;
BundleLinkComponent linkNext;
try {
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json");
status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("searchAll", ourLastMethod);
respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, responseContent);
assertEquals(10, respBundle.getEntry().size());
assertEquals("Patient/0", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue());
linkNext = respBundle.getLink("next");
assertNotNull(linkNext);
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
when(ourLastBundleProvider.size()).thenReturn(25);
try {
httpGet = new HttpGet(linkNext.getUrl());
status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("searchAll", ourLastMethod);
respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, responseContent);
assertEquals(10, respBundle.getEntry().size());
assertEquals("Patient/10", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue());
linkNext = respBundle.getLink("next");
assertNotNull(linkNext);
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
try {
httpGet = new HttpGet(linkNext.getUrl());
status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("searchAll", ourLastMethod);
respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, responseContent);
assertEquals(5, respBundle.getEntry().size());
assertEquals("Patient/20", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue());
linkNext = respBundle.getLink("next");
assertNull(linkNext);
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = PortUtil.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setPagingProvider(new FifoMemoryPagingProvider(10));
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class DummyPatientResourceProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Search()
public IBundleProvider searchAll() {
ourLastMethod = "searchAll";
return ourLastBundleProvider;
}
}
}

View File

@ -1,8 +1,6 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@ -28,7 +26,7 @@ import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.util.PortUtil;
@ -38,11 +36,12 @@ public class SearchDstu3Test {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu3();
private static TokenAndListParam ourIdentifiers;
private static String ourLastMethod;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDstu3Test.class);
private static int ourPort;
private static Server ourServer;
private static String ourLastMethod;
private static TokenAndListParam ourIdentifiers;
@Before
public void before() {
@ -77,9 +76,11 @@ public class SearchDstu3Test {
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
OperationOutcome oo = (OperationOutcome) ourCtx.newXmlParser().parseResource(responseContent);
assertEquals("Invalid search parameter \"identifier.chain\". Parameter contains a chain (.chain) and chains are not supported for this parameter (chaining is only allowed on reference parameters)", oo.getIssueFirstRep().getDiagnostics());
assertEquals(
"Invalid search parameter \"identifier.chain\". Parameter contains a chain (.chain) and chains are not supported for this parameter (chaining is only allowed on reference parameters)",
oo.getIssueFirstRep().getDiagnostics());
} finally {
IOUtils.closeQuietly(status.getEntity().getContent());
}
@ -123,19 +124,17 @@ public class SearchDstu3Test {
return Patient.class;
}
//@formatter:off
@SuppressWarnings("rawtypes")
@Search()
public List search(
@OptionalParam(name=Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers
) {
@RequiredParam(name = Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers) {
ourLastMethod = "search";
ourIdentifiers = theIdentifiers;
ArrayList<Patient> retVal = new ArrayList<Patient>();
retVal.add((Patient) new Patient().addName(new HumanName().setFamily("FAMILY")).setId("1"));
return retVal;
}
//@formatter:on
}

View File

@ -157,7 +157,7 @@ public class SearchHl7OrgDstu2Test {
}
@Override
public int size() {
public Integer size() {
return 0;
}

View File

@ -152,11 +152,10 @@ public class ${className}ResourceProvider extends
paramMap.setIncludes(theIncludes);
paramMap.setSort(theSort);
paramMap.setCount(theCount);
paramMap.setRequestDetails(theRequestDetails);
getDao().translateRawParameters(theAdditionalRawParams, paramMap);
ca.uhn.fhir.rest.server.IBundleProvider retVal = getDao().search(paramMap);
ca.uhn.fhir.rest.server.IBundleProvider retVal = getDao().search(paramMap, theRequestDetails);
return retVal;
} finally {
endRequest(theServletRequest);

22
pom.xml
View File

@ -394,6 +394,11 @@
<artifactId>xml-patch</artifactId>
<version>0.3.0</version>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
@ -522,7 +527,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
<version>3.5</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
@ -614,6 +619,21 @@
<artifactId>velocity-tools</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-compiler-javac</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-compiler-javac-errorprone</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
<version>3.0.22</version>
</dependency>
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>