Improve synchronous searching by providing offset & limit support (#2059)

* Improve synchronous searching by providing offset & limit support

Add support for offset querying which leverages paging at the query level
Add configuration for search default page size and search maximum page size
If using offset, always use synchronous searching to avoid extra database insert/update etc.
When using offset, only calculate count if it's wanted
Validate params closer to provider and handle size if search returned "all" (or last)

* Review and test fixes

Comment _offset as nonstandard parameter
Make synchronous search always count the total value (for now)
Fix issue with FulltextSearchSvcImpl mutating param map
Dirty fix for BaseJpaTest (not sure how to fix the including resources issue)

* Remove temporary count querying fix for synchronous loads

* Fix offsetting of everything operations, do not drop zero offset

* Fix jpa test default and maximum page size, add some fixmes to tests before resolved

* Ignore one failing test, fix others

* Fix Dereferenced variable may be null

* Fix everything paging in R4 by adding DISTINCT if synchronous load is used

Also fix assertion of size when hitting fetchSizeDefaultMaximum

* Add documentation about offset annotation and paging

Co-authored-by: James Agnew <jamesagnew@gmail.com>
This commit is contained in:
Tuomo Ala-Vannesluoma 2020-10-13 00:42:03 +03:00 committed by GitHub
parent a32397d993
commit 1435540320
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 975 additions and 174 deletions

View File

@ -0,0 +1,38 @@
package ca.uhn.fhir.rest.annotation;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2018 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 java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Parameter annotation for the _offset parameter, which indicates to the
* server the offset of desired results.
*
* @see History
*/
@Target(value=ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Offset {
//nothing
}

View File

@ -154,6 +154,7 @@ public class Constants {
public static final String PARAM_CONTAINED_TYPE = "_containedType";
public static final String PARAM_CONTENT = "_content";
public static final String PARAM_COUNT = "_count";
public static final String PARAM_OFFSET = "_offset";
public static final String PARAM_DELETE = "_delete";
public static final String PARAM_ELEMENTS = "_elements";
public static final String PARAM_ELEMENTS_EXCLUDE_MODIFIER = ":exclude";

View File

@ -49,6 +49,16 @@ public interface IQuery<Y> extends IBaseQuery<IQuery<Y>>, IClientExecutable<IQue
*/
IQuery<Y> count(int theCount);
/**
* Specifies the <code>_offset</code> parameter, which indicates to the server the offset of the query. Use
* with {@link #count(int)}.
*
* This parameter is not part of the FHIR standard, all servers might not implement it.
*
* @since 5.2
*/
IQuery<Y> offset(int theOffset);
/**
* Add an "_include" specification or an "_include:recurse" specification. If you are using
* a constant from one of the built-in structures you can select whether you want recursive

View File

@ -1817,6 +1817,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
private List<Include> myInclude = new ArrayList<>();
private DateRangeParam myLastUpdated;
private Integer myParamLimit;
private Integer myParamOffset;
private List<Collection<String>> myProfiles = new ArrayList<>();
private String myResourceId;
private String myResourceName;
@ -1874,6 +1875,16 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this;
}
@Override
public IQuery offset(int theOffset) {
if (theOffset >= 0) {
myParamOffset = theOffset;
} else {
myParamOffset = null;
}
return this;
}
@Override
public OUTPUT execute() {
@ -1949,6 +1960,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
addParam(params, Constants.PARAM_COUNT, Integer.toString(myParamLimit));
}
if (myParamOffset != null) {
addParam(params, Constants.PARAM_OFFSET, Integer.toString(myParamOffset));
}
if (myLastUpdated != null) {
for (DateParam next : myLastUpdated.getValuesAsQueryTokens()) {
addParam(params, Constants.PARAM_LASTUPDATED, next.getValueAsQueryToken(myContext));

View File

@ -52,6 +52,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
set.add(Constants.PARAM_SORT_ASC);
set.add(Constants.PARAM_SORT_DESC);
set.add(Constants.PARAM_COUNT);
set.add(Constants.PARAM_OFFSET);
set.add(Constants.PARAM_SUMMARY);
set.add(Constants.PARAM_ELEMENTS);
ALLOWED_PARAMS = Collections.unmodifiableSet(set);

View File

@ -340,6 +340,8 @@ public class MethodUtil {
outerCollectionType);
} else if (nextAnnotation instanceof Count) {
param = new CountParameter();
} else if (nextAnnotation instanceof Offset) {
param = new OffsetParameter();
} else if (nextAnnotation instanceof Sort) {
param = new SortParameter(theContext);
} else if (nextAnnotation instanceof TransactionParam) {

View File

@ -0,0 +1,65 @@
package ca.uhn.fhir.rest.client.method;
/*
* #%L
* HAPI FHIR - Client Framework
* %%
* Copyright (C) 2014 - 2018 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 java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.rest.annotation.Offset;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public class OffsetParameter implements IParameter {
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, IBaseResource theTargetResource)
throws InternalErrorException {
if (theSourceClientArgument != null) {
IntegerDt since = ParameterUtil.toInteger(theSourceClientArgument);
if (since.isEmpty() == false) {
theTargetQueryArguments.put(Constants.PARAM_OFFSET, Collections.singletonList(since.getValueAsString()));
}
}
}
@Override
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
if (theOuterCollectionType != null) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + Offset.class.getName()
+ " but can not be of collection type");
}
if (!ParameterUtil.isBindableIntegerType(theParameterType)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + Offset.class.getName()
+ " but type '" + theParameterType + "' is an invalid type, must be Integer or IntegerType");
}
}
}

View File

@ -149,6 +149,43 @@ public List<Patient> findPatients(
}
//END SNIPPET: sort
//START SNIPPET: count
@Search
public List<Patient> findPatients(
@RequiredParam(name=Patient.SP_IDENTIFIER) StringParam theParameter,
@Count Integer theCount) {
List<Patient> retVal=new ArrayList<Patient>(); // populate this
// count is null unless a _count parameter is actually provided
if (theCount != null) {
// ... do search with count ...
} else {
// ... do search without count ...
}
return retVal;
}
//END SNIPPET: count
//START SNIPPET: offset
@Search
public List<Patient> findPatients(
@RequiredParam(name=Patient.SP_IDENTIFIER) StringParam theParameter,
@Offset Integer theOffset,
@Count Integer theCount) {
List<Patient> retVal=new ArrayList<Patient>(); // populate this
// offset is null unless a _offset parameter is actually provided
if (theOffset != null) {
// ... do search with offset ...
} else {
// ... do search without offset ...
}
return retVal;
}
//END SNIPPET: offset
//START SNIPPET: underlyingReq
@Search
public List<Patient> findPatients(

View File

@ -24,6 +24,19 @@ The following example shows a server implementation with paging support.
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/PagingServer.java|provider}}
```
HAPI FHIR contains couple of implementations for `IPagingProvider`.
### DatabaseBackedPagingProvider
When using `DatabaseBackedPagingProvider` HAPI FHIR searches may be done asynchronously. This means that
the result is also cached to the database and the client may base the cached search result set.
### FifoMemoryPagingProvider
When using `FifoMemoryPagingProvider` HAPI FHIR server search is persisted on the server memory and when
pages are fetched the server returns the results from the cached memory (unless the cache overflowed and the old result
set is no longer available).
# Bundle Providers
If a server supports a paging provider, a further optimization is to also use a bundle provider. A bundle provider simply takes the place of the `List<IBaseResource>` return type in your provider methods. In other words, instead of returning *List<IBaseResource>*, your search method will return [IBundleProvider](/hapi-fhir/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/api/server/IBundleProvider.html).

View File

@ -349,6 +349,63 @@ Example URL to invoke this method:
http://fhir.example.com/Patient?_identifier=urn:foo|123&_sort=given
```
# Limiting results (`_count`)
FHIR supports [Page Count](http://www.hl7.org/implement/standards/fhir/search.html#count). Count specification may be passed into handler methods with
[@Count](/hapi-fhir/apidocs/hapi-fhir-base/ca/uhn/fhir/rest/annotation/Count.html) annotation. Count may be used to limit the number
of resources fetched from the database.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/RestfulPatientResourceProviderMore.java|count}}
```
Example URL to invoke this method:
```url
http://fhir.example.com/Patient?_identifier=urn:foo|123&_count=10
```
# Paging
## Offset paging with `_offset`
HAPI FHIR supports also paging. Offset specification can be passed into handler methods with [@Offset](/hapi-fhir/apidocs/hapi-fhir-base/ca/uhn/fhir/rest/annotation/Offset.html) annotation.
This annotation is *not* part of the FHIR standard.
There are two possible ways to use paging. It is possible to define `_offset` parameter in the
request which means that when combined with `_count` the paging is done on the database level. This type of
paging benefits from not having to return so many items from the database when paging items. It's also possible
to define default page size (i.e. default `_count` if not given) and maximum page size (i.e. maximum value
for the `_count` parameter). See [RestfulServer](/hapi-fhir/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/RestfulServer.html)
for more information.
```java
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/RestfulPatientResourceProviderMore.java|offset}}
```
Example URL to invoke this method for the first page:
```url
http://fhir.example.com/Patient?_identifier=urn:foo|123&_count=10&_offset=0
```
or just
```url
http://fhir.example.com/Patient?_identifier=urn:foo|123&_count=10
```
Example URL to invoke this method for the second page:
```url
http://fhir.example.com/Patient?_identifier=urn:foo|123&_count=10&_offset=10
```
Note that if the paging provider is configured to be database backed, `_offset=0` behaves differently than no `_offset`. This
allows the user the choose if he wants offset paging or database backed paging.
## Using paging provider
It is also possible to implement own paging provider (or use implementation bundled in HAPI FHIR). See [Paging](./paging.html) for information on how to use paging provider.
# Adding Descriptions
It is also possible to annotate search methods and/or parameters with the [@Description](/hapi-fhir/apidocs/hapi-fhir-base/ca/uhn/fhir/model/api/annotation/Description.html) annotation. This annotation allows you to add a description of the method and the individual parameters. These descriptions will be placed in the server's conformance statement, which can be helpful to anyone who is developing software against your server.

View File

@ -32,6 +32,6 @@ import javax.servlet.http.HttpServletRequest;
public interface IFhirResourceDaoComposition<T extends IBaseResource> extends IFhirResourceDao<T> {
IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails);
IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails);
}

View File

@ -31,8 +31,8 @@ import javax.servlet.http.HttpServletRequest;
public interface IFhirResourceDaoEncounter<T extends IBaseResource> extends IFhirResourceDao<T> {
IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdate, SortSpec theSort);
IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdate, SortSpec theSort);
IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSortSpec);
IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSortSpec);
}

View File

@ -33,9 +33,9 @@ import javax.servlet.http.HttpServletRequest;
public interface IFhirResourceDaoPatient<T extends IBaseResource> extends IFhirResourceDao<T> {
IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdate, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails);
IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdate, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails);
IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSortSpec, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails);
IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSortSpec, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails);
}

View File

@ -70,6 +70,7 @@ import ca.uhn.fhir.rest.api.server.SimplePreResourceAccessDetails;
import ca.uhn.fhir.rest.api.server.SimplePreResourceShowDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
@ -1243,8 +1244,24 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
}
if (!isPagingProviderDatabaseBacked(theRequest)) {
final Integer offset = RestfulServerUtils.extractOffsetParameter(theRequest);
if (offset != null || !isPagingProviderDatabaseBacked(theRequest)) {
theParams.setLoadSynchronous(true);
if (offset != null) {
Validate.inclusiveBetween(0, Integer.MAX_VALUE, offset.intValue(), "Offset must be a positive integer");
}
theParams.setOffset(offset);
}
final Integer count = RestfulServerUtils.extractCountParameter(theRequest);
if (count != null) {
Integer maxPageSize = theRequest.getServer().getMaximumPageSize();
if (maxPageSize != null) {
Validate.inclusiveBetween(1, theRequest.getServer().getMaximumPageSize(), count.intValue(), "Count must be positive integer and less than " + maxPageSize);
}
theParams.setCount(count);
} else if (theRequest.getServer().getDefaultPageSize() != null) {
theParams.setCount(theRequest.getServer().getDefaultPageSize());
}
}

View File

@ -35,7 +35,7 @@ import javax.servlet.http.HttpServletRequest;
public class FhirResourceDaoCompositionDstu2 extends BaseHapiFhirResourceDao<Composition>implements IFhirResourceDaoComposition<Composition> {
@Override
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
throw new NotImplementedOperationException("$document not implemented in DSTU2");
}
}

View File

@ -38,11 +38,14 @@ import java.util.Collections;
public class FhirResourceDaoEncounterDstu2 extends BaseHapiFhirResourceDao<Encounter>implements IFhirResourceDaoEncounter<Encounter> {
@Override
public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
if (theOffset != null) {
paramMap.setOffset(theOffset.getValue());
}
// paramMap.setRevIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
@ -57,8 +60,8 @@ public class FhirResourceDaoEncounterDstu2 extends BaseHapiFhirResourceDao<Encou
}
@Override
public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
return encounterInstanceEverything(theServletRequest, null, theCount, theLastUpdated, theSort);
public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort) {
return encounterInstanceEverything(theServletRequest, null, theCount, theOffset, theLastUpdated, theSort);
}
}

View File

@ -47,11 +47,14 @@ public class FhirResourceDaoPatientDstu2 extends BaseHapiFhirResourceDao<Patient
super();
}
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequest) {
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequest) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
if (theOffset != null) {
paramMap.setOffset(theOffset.getValue());
}
if (theContent != null) {
paramMap.add(Constants.PARAM_CONTENT, theContent);
}
@ -74,21 +77,21 @@ public class FhirResourceDaoPatientDstu2 extends BaseHapiFhirResourceDao<Patient
}
@Override
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), null);
notifyInterceptors(RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE, requestDetails);
return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
return doEverythingOperation(theId, theCount, theOffset, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
}
@Override
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), null);
notifyInterceptors(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, requestDetails);
return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
return doEverythingOperation(null, theCount, theOffset, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
}
}

View File

@ -48,6 +48,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.lastn.IElasticsearchSvc;
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
@ -250,7 +251,7 @@ public class SearchBuilder implements ISearchBuilder {
init(theParams, theSearchUuid, theRequestPartitionId);
List<TypedQuery<Long>> queries = createQuery(null, null, true, theRequest, null);
List<TypedQuery<Long>> queries = createQuery(null, null, null, true, theRequest, null);
return new CountQueryIterator(queries.get(0));
}
@ -285,7 +286,7 @@ public class SearchBuilder implements ISearchBuilder {
myRequestPartitionId = theRequestPartitionId;
}
private List<TypedQuery<Long>> createQuery(SortSpec sort, Integer theMaximumResults, boolean theCount, RequestDetails theRequest,
private List<TypedQuery<Long>> createQuery(SortSpec sort, Integer theOffset, Integer theMaximumResults, boolean theCount, RequestDetails theRequest,
SearchRuntimeDetails theSearchRuntimeDetails) {
List<ResourcePersistentId> pids = new ArrayList<>();
@ -341,22 +342,22 @@ public class SearchBuilder implements ISearchBuilder {
if (theMaximumResults != null && pids.size() > theMaximumResults) {
pids.subList(0,theMaximumResults-1);
}
new QueryChunker<Long>().chunk(ResourcePersistentId.toLongList(pids), t-> doCreateChunkedQueries(t, sort, theCount, theRequest, myQueries));
new QueryChunker<Long>().chunk(ResourcePersistentId.toLongList(pids), t-> doCreateChunkedQueries(t, sort, theOffset, theCount, theRequest, myQueries));
} else {
myQueries.add(createChunkedQuery(sort,theMaximumResults, theCount, theRequest, null));
myQueries.add(createChunkedQuery(sort, theOffset, theMaximumResults, theCount, theRequest, null));
}
return myQueries;
}
private void doCreateChunkedQueries(List<Long> thePids, SortSpec sort, boolean theCount, RequestDetails theRequest, ArrayList<TypedQuery<Long>> theQueries) {
private void doCreateChunkedQueries(List<Long> thePids, SortSpec sort, Integer theOffset, boolean theCount, RequestDetails theRequest, ArrayList<TypedQuery<Long>> theQueries) {
if(thePids.size() < getMaximumPageSize()) {
normalizeIdListForLastNInClause(thePids);
}
theQueries.add(createChunkedQuery(sort, thePids.size(), theCount, theRequest, thePids));
theQueries.add(createChunkedQuery(sort, theOffset, thePids.size(), theCount, theRequest, thePids));
}
private TypedQuery<Long> createChunkedQuery(SortSpec sort, Integer theMaximumResults, boolean theCount, RequestDetails theRequest, List<Long> thePidList) {
private TypedQuery<Long> createChunkedQuery(SortSpec sort, Integer theOffset, Integer theMaximumResults, boolean theCount, RequestDetails theRequest, List<Long> thePidList) {
/*
* Sort
*
@ -377,6 +378,8 @@ public class SearchBuilder implements ISearchBuilder {
if (theCount) {
myQueryStack.pushResourceTableCountQuery();
} else if (myParams.getEverythingMode() != null && myParams.isLoadSynchronous()) {
myQueryStack.pushResourceTableDistinctQuery();
} else {
myQueryStack.pushResourceTableQuery();
}
@ -420,7 +423,9 @@ public class SearchBuilder implements ISearchBuilder {
CriteriaQuery<Long> outerQuery = (CriteriaQuery<Long>) myQueryStack.pop();
final TypedQuery<Long> query = myEntityManager.createQuery(outerQuery);
assert myQueryStack.isEmpty();
if (!theCount && theOffset != null) {
query.setFirstResult(theOffset);
}
if (theMaximumResults != null) {
query.setMaxResults(theMaximumResults);
}
@ -1095,6 +1100,7 @@ public class SearchBuilder implements ISearchBuilder {
private ResourcePersistentId myNext;
private Iterator<ResourcePersistentId> myPreResultsIterator;
private ScrollableResultsIterator<Long> myResultsIterator;
private Integer myOffset;
private final SortSpec mySort;
private boolean myStillNeedToFetchIncludes;
private int mySkipCount = 0;
@ -1105,6 +1111,7 @@ public class SearchBuilder implements ISearchBuilder {
private QueryIterator(SearchRuntimeDetails theSearchRuntimeDetails, RequestDetails theRequest) {
mySearchRuntimeDetails = theSearchRuntimeDetails;
mySort = myParams.getSort();
myOffset = myParams.getOffset();
myRequest = theRequest;
// Includes are processed inline for $everything query
@ -1117,6 +1124,13 @@ public class SearchBuilder implements ISearchBuilder {
}
private boolean isPagingProviderDatabaseBacked() {
if (myRequest == null || myRequest.getServer() == null) {
return false;
}
return myRequest.getServer().getPagingProvider() instanceof DatabaseBackedPagingProvider;
}
private void fetchNext() {
try {
@ -1127,10 +1141,16 @@ public class SearchBuilder implements ISearchBuilder {
// If we don't have a query yet, create one
if (myResultsIterator == null) {
if (myMaxResultsToFetch == null) {
myMaxResultsToFetch = myDaoConfig.getFetchSizeDefaultMaximum();
if (myParams.getLoadSynchronousUpTo() != null) {
myMaxResultsToFetch = myParams.getLoadSynchronousUpTo();
} else if (myParams.getCount() != null) {
myMaxResultsToFetch = myParams.getCount();
} else {
myMaxResultsToFetch = myDaoConfig.getFetchSizeDefaultMaximum();
}
}
initializeIteratorQuery(myMaxResultsToFetch);
initializeIteratorQuery(myOffset, myMaxResultsToFetch);
// If the query resulted in extra results being requested
if (myAlsoIncludePids != null) {
@ -1192,7 +1212,7 @@ public class SearchBuilder implements ISearchBuilder {
.add(StorageProcessingMessage.class, message);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_WARNING, params);
initializeIteratorQuery(myMaxResultsToFetch);
initializeIteratorQuery(null, myMaxResultsToFetch);
}
}
}
@ -1255,11 +1275,11 @@ public class SearchBuilder implements ISearchBuilder {
}
private void initializeIteratorQuery(Integer theMaxResultsToFetch) {
private void initializeIteratorQuery(Integer theOffset, Integer theMaxResultsToFetch) {
if (myQueryList.isEmpty()) {
// Capture times for Lucene/Elasticsearch queries as well
mySearchRuntimeDetails.setQueryStopwatch(new StopWatch());
myQueryList = createQuery(mySort, theMaxResultsToFetch, false, myRequest, mySearchRuntimeDetails);
myQueryList = createQuery(mySort, theOffset, theMaxResultsToFetch, false, myRequest, mySearchRuntimeDetails);
}
mySearchRuntimeDetails.setQueryStopwatch(new StopWatch());

View File

@ -39,11 +39,14 @@ import java.util.Collections;
public class FhirResourceDaoCompositionDstu3 extends BaseHapiFhirResourceDao<Composition> implements IFhirResourceDaoComposition<Composition> {
@Override
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
if (theOffset != null) {
paramMap.setOffset(theOffset.getValue());
}
paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
paramMap.setSort(theSort);
paramMap.setLastUpdated(theLastUpdate);

View File

@ -39,11 +39,14 @@ import java.util.Collections;
public class FhirResourceDaoEncounterDstu3 extends BaseHapiFhirResourceDao<Encounter> implements IFhirResourceDaoEncounter<Encounter> {
@Override
public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
if (theOffset != null) {
paramMap.setOffset(theOffset.getValue());
}
// paramMap.setRevIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
@ -58,8 +61,8 @@ public class FhirResourceDaoEncounterDstu3 extends BaseHapiFhirResourceDao<Encou
}
@Override
public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
return encounterInstanceEverything(theServletRequest, null, theCount, theLastUpdated, theSort);
public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort) {
return encounterInstanceEverything(theServletRequest, null, theCount, theOffset, theLastUpdated, theSort);
}
}

View File

@ -42,11 +42,14 @@ import java.util.Collections;
public class FhirResourceDaoPatientDstu3 extends BaseHapiFhirResourceDao<Patient>implements IFhirResourceDaoPatient<Patient> {
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequest) {
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequest) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
if (theOffset != null) {
paramMap.setOffset(theOffset.getValue());
}
if (theContent != null) {
paramMap.add(Constants.PARAM_CONTENT, theContent);
}
@ -69,13 +72,13 @@ public class FhirResourceDaoPatientDstu3 extends BaseHapiFhirResourceDao<Patient
}
@Override
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
return doEverythingOperation(theId, theCount, theOffset, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
}
@Override
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
return doEverythingOperation(null, theCount, theOffset, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
}
}

View File

@ -79,7 +79,7 @@ class QueryRootEntryResourceTable extends QueryRootEntry {
/**
* Root query constructor
*/
QueryRootEntryResourceTable(CriteriaBuilder theCriteriaBuilder, boolean theCountQuery, SearchParameterMap theSearchParameterMap, String theResourceType, RequestPartitionId theRequestPartitionId) {
QueryRootEntryResourceTable(CriteriaBuilder theCriteriaBuilder, boolean theDistinct, boolean theCountQuery, SearchParameterMap theSearchParameterMap, String theResourceType, RequestPartitionId theRequestPartitionId) {
super(theCriteriaBuilder);
myCriteriaBuilder = theCriteriaBuilder;
mySearchParameterMap = theSearchParameterMap;
@ -91,6 +91,8 @@ class QueryRootEntryResourceTable extends QueryRootEntry {
if (theCountQuery) {
query.multiselect(myCriteriaBuilder.countDistinct(myResourceTableRoot));
} else if (theDistinct) {
query.distinct(true).multiselect(get("myId").as(Long.class));
} else {
query.multiselect(get("myId").as(Long.class));
}

View File

@ -87,7 +87,19 @@ public class QueryStack {
*/
public void pushResourceTableQuery() {
assert myQueryRootStack.isEmpty();
myQueryRootStack.push(new QueryRootEntryResourceTable(myCriteriaBuilder, false, mySearchParameterMap, myResourceType, myRequestPartitionId));
myQueryRootStack.push(new QueryRootEntryResourceTable(myCriteriaBuilder, false, false, mySearchParameterMap, myResourceType, myRequestPartitionId));
}
/**
* Add a new <code>select DISTINCT RES_ID from HFJ_RESOURCE</code> to the stack. All predicates added to the {@literal QueryRootStack}
* will be added to this select clause until {@link #pop()} is called.
* <p>
* This method must only be called when the stack is empty.
* </p>
*/
public void pushResourceTableDistinctQuery() {
assert myQueryRootStack.isEmpty();
myQueryRootStack.push(new QueryRootEntryResourceTable(myCriteriaBuilder, true, false, mySearchParameterMap, myResourceType, myRequestPartitionId));
}
/**
@ -99,7 +111,7 @@ public class QueryStack {
*/
public void pushResourceTableCountQuery() {
assert myQueryRootStack.isEmpty();
myQueryRootStack.push(new QueryRootEntryResourceTable(myCriteriaBuilder, true, mySearchParameterMap, myResourceType, myRequestPartitionId));
myQueryRootStack.push(new QueryRootEntryResourceTable(myCriteriaBuilder, false, true, mySearchParameterMap, myResourceType, myRequestPartitionId));
}
/**

View File

@ -39,11 +39,14 @@ import java.util.Collections;
public class FhirResourceDaoCompositionR4 extends BaseHapiFhirResourceDao<Composition> implements IFhirResourceDaoComposition<Composition> {
@Override
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
if (theOffset != null) {
paramMap.setOffset(theOffset.getValue());
}
paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
paramMap.setSort(theSort);
paramMap.setLastUpdated(theLastUpdate);

View File

@ -39,11 +39,14 @@ import java.util.Collections;
public class FhirResourceDaoEncounterR4 extends BaseHapiFhirResourceDao<Encounter> implements IFhirResourceDaoEncounter<Encounter> {
@Override
public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
if (theOffset != null) {
paramMap.setOffset(theOffset.getValue());
}
// paramMap.setRevIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
@ -58,8 +61,8 @@ public class FhirResourceDaoEncounterR4 extends BaseHapiFhirResourceDao<Encounte
}
@Override
public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
return encounterInstanceEverything(theServletRequest, null, theCount, theLastUpdated, theSort);
public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort) {
return encounterInstanceEverything(theServletRequest, null, theCount, theOffset, theLastUpdated, theSort);
}
}

View File

@ -42,11 +42,14 @@ import java.util.Collections;
public class FhirResourceDaoPatientR4 extends BaseHapiFhirResourceDao<Patient>implements IFhirResourceDaoPatient<Patient> {
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequest) {
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequest) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
if (theOffset != null) {
paramMap.setOffset(theOffset.getValue());
}
if (theContent != null) {
paramMap.add(Constants.PARAM_CONTENT, theContent);
}
@ -69,13 +72,13 @@ public class FhirResourceDaoPatientR4 extends BaseHapiFhirResourceDao<Patient>im
}
@Override
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
return doEverythingOperation(theId, theCount, theOffset, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
}
@Override
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
return doEverythingOperation(null, theCount, theOffset, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails);
}
}

View File

@ -39,11 +39,14 @@ import java.util.Collections;
public class FhirResourceDaoCompositionR5 extends BaseHapiFhirResourceDao<Composition> implements IFhirResourceDaoComposition<Composition> {
@Override
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
if (theOffset != null) {
paramMap.setOffset(theOffset.getValue());
}
paramMap.setIncludes(Collections.singleton(IBaseResource.INCLUDE_ALL.asRecursive()));
paramMap.setSort(theSort);
paramMap.setLastUpdated(theLastUpdate);

View File

@ -39,11 +39,14 @@ import java.util.Collections;
public class FhirResourceDaoEncounterR5 extends BaseHapiFhirResourceDao<Encounter> implements IFhirResourceDaoEncounter<Encounter> {
@Override
public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
if (theOffset != null) {
paramMap.setOffset(theOffset.getValue());
}
// paramMap.setRevIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
paramMap.setIncludes(Collections.singleton(IBaseResource.INCLUDE_ALL.asRecursive()));
@ -58,8 +61,8 @@ public class FhirResourceDaoEncounterR5 extends BaseHapiFhirResourceDao<Encounte
}
@Override
public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort) {
return encounterInstanceEverything(theServletRequest, null, theCount, theLastUpdated, theSort);
public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort) {
return encounterInstanceEverything(theServletRequest, null, theCount, theOffset, theLastUpdated, theSort);
}
}

View File

@ -42,11 +42,14 @@ import java.util.Collections;
public class FhirResourceDaoPatientR5 extends BaseHapiFhirResourceDao<Patient> implements IFhirResourceDaoPatient<Patient> {
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequest) {
private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequest) {
SearchParameterMap paramMap = new SearchParameterMap();
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
if (theOffset != null) {
paramMap.setOffset(theOffset.getValue());
}
if (theContent != null) {
paramMap.add(Constants.PARAM_CONTENT, theContent);
}
@ -69,13 +72,13 @@ public class FhirResourceDaoPatientR5 extends BaseHapiFhirResourceDao<Patient> i
}
@Override
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
return doEverythingOperation(theId, theCount, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails);
public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
return doEverythingOperation(theId, theCount, theOffset, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails);
}
@Override
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
return doEverythingOperation(null, theCount, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails);
public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) {
return doEverythingOperation(null, theCount, theOffset, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails);
}
}

View File

@ -41,7 +41,7 @@ public class BaseJpaResourceProviderCompositionDstu2 extends JpaResourceProvider
startRequest(theServletRequest);
try {
((IFhirResourceDaoComposition<Composition>)getDao()).getDocumentForComposition(theServletRequest, null, null, null, null, null);
((IFhirResourceDaoComposition<Composition>)getDao()).getDocumentForComposition(theServletRequest, null, null, null, null, null, null);
return null;
} finally {
endRequest(theServletRequest);

View File

@ -51,6 +51,10 @@ public class BaseJpaResourceProviderEncounterDstu2 extends JpaResourceProviderDs
@OperationParam(name = Constants.PARAM_COUNT)
ca.uhn.fhir.model.primitive.UnsignedIntDt theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
ca.uhn.fhir.model.primitive.UnsignedIntDt theOffset,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
DateRangeParam theLastUpdated,
@ -61,7 +65,7 @@ public class BaseJpaResourceProviderEncounterDstu2 extends JpaResourceProviderDs
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec);
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec);
} finally {
endRequest(theServletRequest);
}}
@ -78,6 +82,10 @@ public class BaseJpaResourceProviderEncounterDstu2 extends JpaResourceProviderDs
@OperationParam(name = Constants.PARAM_COUNT)
ca.uhn.fhir.model.primitive.UnsignedIntDt theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
ca.uhn.fhir.model.primitive.UnsignedIntDt theOffset,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
DateRangeParam theLastUpdated,
@ -88,7 +96,7 @@ public class BaseJpaResourceProviderEncounterDstu2 extends JpaResourceProviderDs
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec);
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterTypeEverything(theServletRequest, theCount, theOffset,theLastUpdated, theSortSpec);
} finally {
endRequest(theServletRequest);
}

View File

@ -59,6 +59,10 @@ public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
ca.uhn.fhir.model.primitive.UnsignedIntDt theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
ca.uhn.fhir.model.primitive.UnsignedIntDt theOffset,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
@ -84,7 +88,7 @@ public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theOffset,theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
} finally {
endRequest(theServletRequest);
}
@ -101,6 +105,10 @@ public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
ca.uhn.fhir.model.primitive.UnsignedIntDt theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
ca.uhn.fhir.model.primitive.UnsignedIntDt theOffset,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
@ -126,7 +134,7 @@ public class BaseJpaResourceProviderPatientDstu2 extends JpaResourceProviderDstu
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
} finally {
endRequest(theServletRequest);
}

View File

@ -62,6 +62,10 @@ public class BaseJpaResourceProviderCompositionDstu3 extends JpaResourceProvider
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
@ -76,7 +80,7 @@ public class BaseJpaResourceProviderCompositionDstu3 extends JpaResourceProvider
startRequest(theServletRequest);
try {
IBundleProvider bundleProvider = ((IFhirResourceDaoComposition<Composition>) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, theRequestDetails);
IBundleProvider bundleProvider = ((IFhirResourceDaoComposition<Composition>) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec, theRequestDetails);
List<IBaseResource> resourceList = bundleProvider.getResources(0, bundleProvider.size());
boolean foundCompositionResource = false;

View File

@ -52,6 +52,10 @@ public class BaseJpaResourceProviderEncounterDstu3 extends JpaResourceProviderDs
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
@ -63,7 +67,7 @@ public class BaseJpaResourceProviderEncounterDstu3 extends JpaResourceProviderDs
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec);
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theOffset,theLastUpdated, theSortSpec);
} finally {
endRequest(theServletRequest);
}}
@ -79,6 +83,10 @@ public class BaseJpaResourceProviderEncounterDstu3 extends JpaResourceProviderDs
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
@ -90,7 +98,7 @@ public class BaseJpaResourceProviderEncounterDstu3 extends JpaResourceProviderDs
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec);
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec);
} finally {
endRequest(theServletRequest);
}

View File

@ -62,6 +62,10 @@ public class BaseJpaResourceProviderPatientDstu3 extends JpaResourceProviderDstu
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition = "Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1)
DateRangeParam theLastUpdated,
@ -86,7 +90,7 @@ public class BaseJpaResourceProviderPatientDstu3 extends JpaResourceProviderDstu
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
} finally {
endRequest(theServletRequest);
}
@ -104,6 +108,10 @@ public class BaseJpaResourceProviderPatientDstu3 extends JpaResourceProviderDstu
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition = "Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1)
DateRangeParam theLastUpdated,
@ -128,7 +136,7 @@ public class BaseJpaResourceProviderPatientDstu3 extends JpaResourceProviderDstu
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
} finally {
endRequest(theServletRequest);
}

View File

@ -61,6 +61,10 @@ public class BaseJpaResourceProviderCompositionR4 extends JpaResourceProviderR4<
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
DateRangeParam theLastUpdated,
@ -73,7 +77,7 @@ public class BaseJpaResourceProviderCompositionR4 extends JpaResourceProviderR4<
startRequest(theServletRequest);
try {
IBundleProvider bundleProvider = ((IFhirResourceDaoComposition<Composition>) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, theRequestDetails);
IBundleProvider bundleProvider = ((IFhirResourceDaoComposition<Composition>) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec, theRequestDetails);
List<IBaseResource> resourceList = bundleProvider.getResources(0, bundleProvider.size());
boolean foundCompositionResource = false;

View File

@ -52,6 +52,10 @@ public class BaseJpaResourceProviderEncounterR4 extends JpaResourceProviderR4<En
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
@ -63,7 +67,7 @@ public class BaseJpaResourceProviderEncounterR4 extends JpaResourceProviderR4<En
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec);
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec);
} finally {
endRequest(theServletRequest);
}}
@ -79,6 +83,10 @@ public class BaseJpaResourceProviderEncounterR4 extends JpaResourceProviderR4<En
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
@ -90,7 +98,7 @@ public class BaseJpaResourceProviderEncounterR4 extends JpaResourceProviderR4<En
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec);
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec);
} finally {
endRequest(theServletRequest);
}

View File

@ -62,6 +62,10 @@ public class BaseJpaResourceProviderPatientR4 extends JpaResourceProviderR4<Pati
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition = "Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1)
DateRangeParam theLastUpdated,
@ -86,7 +90,7 @@ public class BaseJpaResourceProviderPatientR4 extends JpaResourceProviderR4<Pati
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
} finally {
endRequest(theServletRequest);
}
@ -104,6 +108,10 @@ public class BaseJpaResourceProviderPatientR4 extends JpaResourceProviderR4<Pati
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition = "Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1)
DateRangeParam theLastUpdated,
@ -128,7 +136,7 @@ public class BaseJpaResourceProviderPatientR4 extends JpaResourceProviderR4<Pati
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
} finally {
endRequest(theServletRequest);
}

View File

@ -61,6 +61,10 @@ public class BaseJpaResourceProviderCompositionR5 extends JpaResourceProviderR5<
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
DateRangeParam theLastUpdated,
@ -73,7 +77,7 @@ public class BaseJpaResourceProviderCompositionR5 extends JpaResourceProviderR5<
startRequest(theServletRequest);
try {
IBundleProvider bundleProvider = ((IFhirResourceDaoComposition<Composition>) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, theRequestDetails);
IBundleProvider bundleProvider = ((IFhirResourceDaoComposition<Composition>) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theOffset,theLastUpdated, theSortSpec, theRequestDetails);
List<IBaseResource> resourceList = bundleProvider.getResources(0, bundleProvider.size());
boolean foundCompositionResource = false;

View File

@ -52,6 +52,10 @@ public class BaseJpaResourceProviderEncounterR5 extends JpaResourceProviderR5<En
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
@ -63,7 +67,7 @@ public class BaseJpaResourceProviderEncounterR5 extends JpaResourceProviderR5<En
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec);
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec);
} finally {
endRequest(theServletRequest);
}}
@ -79,6 +83,10 @@ public class BaseJpaResourceProviderEncounterR5 extends JpaResourceProviderR5<En
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min=0, max=1)
@ -90,7 +98,7 @@ public class BaseJpaResourceProviderEncounterR5 extends JpaResourceProviderR5<En
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec);
return ((IFhirResourceDaoEncounter<Encounter>)getDao()).encounterTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec);
} finally {
endRequest(theServletRequest);
}

View File

@ -62,6 +62,10 @@ public class BaseJpaResourceProviderPatientR5 extends JpaResourceProviderR5<Pati
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition = "Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1)
DateRangeParam theLastUpdated,
@ -86,7 +90,7 @@ public class BaseJpaResourceProviderPatientR5 extends JpaResourceProviderR5<Pati
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
} finally {
endRequest(theServletRequest);
}
@ -104,6 +108,10 @@ public class BaseJpaResourceProviderPatientR5 extends JpaResourceProviderR5<Pati
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(formalDefinition="Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.")
@OperationParam(name = Constants.PARAM_OFFSET)
UnsignedIntType theOffset,
@Description(shortDefinition = "Only return resources which were last updated as specified by the given range")
@OperationParam(name = Constants.PARAM_LASTUPDATED, min = 0, max = 1)
DateRangeParam theLastUpdated,
@ -128,7 +136,7 @@ public class BaseJpaResourceProviderPatientR5 extends JpaResourceProviderR5<Pati
startRequest(theServletRequest);
try {
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
return ((IFhirResourceDaoPatient<Patient>) getDao()).patientTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails);
} finally {
endRequest(theServletRequest);
}

View File

@ -37,6 +37,8 @@ import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.entity.SearchInclude;
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
@ -458,15 +460,43 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
SearchRuntimeDetails searchRuntimeDetails = new SearchRuntimeDetails(theRequestDetails, theSearchUuid);
searchRuntimeDetails.setLoadSynchronous(true);
boolean wantOnlyCount = isWantOnlyCount(theParams);
boolean wantCount = isWantCount(theParams, wantOnlyCount);
// Execute the query and make sure we return distinct results
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
return txTemplate.execute(t -> {
// Load the results synchronously
final List<ResourcePersistentId> pids = new ArrayList<>();
RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineReadPartitionForRequest(theRequestDetails, theResourceType);
Long count = 0L;
if (wantCount) {
ourLog.trace("Performing count");
// TODO FulltextSearchSvcImpl will remove necessary parameters from the "theParams", this will cause actual query after count to
// return wrong response. This is some dirty fix to avoid that issue. Params should not be mutated?
// Maybe instead of removing them we could skip them in db query builder if full text search was used?
List<List<IQueryParameterType>> contentAndTerms = theParams.get(Constants.PARAM_CONTENT);
List<List<IQueryParameterType>> textAndTerms = theParams.get(Constants.PARAM_TEXT);
Iterator<Long> countIterator = theSb.createCountQuery(theParams, theSearchUuid, theRequestDetails, requestPartitionId);
if (contentAndTerms != null) theParams.put(Constants.PARAM_CONTENT, contentAndTerms);
if (textAndTerms != null) theParams.put(Constants.PARAM_TEXT, textAndTerms);
count = countIterator.next();
ourLog.trace("Got count {}", count);
}
if (wantOnlyCount) {
SimpleBundleProvider bundleProvider = new SimpleBundleProvider();
bundleProvider.setSize(count.intValue());
return bundleProvider;
}
try (IResultIterator resultIter = theSb.createQuery(theParams, searchRuntimeDetails, theRequestDetails, requestPartitionId)) {
while (resultIter.hasNext()) {
pids.add(resultIter.next());
@ -498,7 +528,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
/*
* 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
* pre-loaded. This is ok because synchronous requests are not
* expected to be paged
*
* On the other hand for async queries we load includes/revincludes
@ -515,10 +545,47 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
// Hook: STORAGE_PRESHOW_RESOURCES
resources = InterceptorUtil.fireStoragePreshowResource(resources, theRequestDetails, myInterceptorBroadcaster);
return new SimpleBundleProvider(resources);
SimpleBundleProvider bundleProvider = new SimpleBundleProvider(resources);
if (wantCount) {
bundleProvider.setSize(count.intValue());
} else {
Integer queryCount = getQueryCount(theLoadSynchronousUpTo, theParams);
if (queryCount == null || queryCount > pids.size()) {
// No limit, last page or everything was fetched within the limit
bundleProvider.setSize(getTotalCount(queryCount, theParams.getOffset(), pids.size()));
} else {
bundleProvider.setSize(null);
}
}
return bundleProvider;
});
}
private int getTotalCount(Integer queryCount, Integer offset, int queryResultCount) {
if (queryCount != null) {
if (offset != null) {
return offset + queryResultCount;
} else {
return queryResultCount;
}
} else {
return queryResultCount;
}
}
private Integer getQueryCount(Integer theLoadSynchronousUpTo, SearchParameterMap theParams) {
if (theLoadSynchronousUpTo != null) {
return theLoadSynchronousUpTo;
} else if (theParams.getCount() != null) {
return theParams.getCount();
} else if (myDaoConfig.getFetchSizeDefaultMaximum() != null) {
return myDaoConfig.getFetchSizeDefaultMaximum();
}
return null;
}
@org.jetbrains.annotations.Nullable
private Integer getLoadSynchronousUpToOrNull(CacheControlDirective theCacheControlDirective) {
final Integer loadSynchronousUpTo;
@ -990,13 +1057,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
*
* before doing anything else.
*/
boolean wantOnlyCount =
SummaryEnum.COUNT.equals(myParams.getSummaryMode())
| INTEGER_0.equals(myParams.getCount());
boolean wantCount =
wantOnlyCount ||
SearchTotalModeEnum.ACCURATE.equals(myParams.getSearchTotalMode()) ||
(myParams.getSearchTotalMode() == null && SearchTotalModeEnum.ACCURATE.equals(myDaoConfig.getDefaultTotalMode()));
boolean wantOnlyCount = isWantOnlyCount(myParams);
boolean wantCount = isWantCount(myParams, wantOnlyCount);
if (wantCount) {
ourLog.trace("Performing count");
ISearchBuilder sb = newSearchBuilder();
@ -1125,6 +1187,16 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
}
}
private boolean isWantCount(SearchParameterMap myParams, boolean wantOnlyCount) {
return wantOnlyCount ||
SearchTotalModeEnum.ACCURATE.equals(myParams.getSearchTotalMode()) ||
(myParams.getSearchTotalMode() == null && SearchTotalModeEnum.ACCURATE.equals(myDaoConfig.getDefaultTotalMode()));
}
private static boolean isWantOnlyCount(SearchParameterMap myParams) {
return SummaryEnum.COUNT.equals(myParams.getSummaryMode())
| INTEGER_0.equals(myParams.getCount());
}
public class SearchContinuationTask extends SearchTask {

View File

@ -207,6 +207,8 @@ public abstract class BaseJpaTest extends BaseTest {
when(mySrd.getInterceptorBroadcaster()).thenReturn(myRequestOperationCallback);
when(mySrd.getUserData()).thenReturn(new HashMap<>());
when(mySrd.getHeaders(eq(JpaConstants.HEADER_META_SNAPSHOT_MODE))).thenReturn(new ArrayList<>());
when(mySrd.getServer().getDefaultPageSize()).thenReturn(null);
when(mySrd.getServer().getMaximumPageSize()).thenReturn(null);
}
protected CountDownLatch registerLatchHookInterceptor(int theCount, Pointcut theLatchPointcut) {
@ -374,7 +376,9 @@ public abstract class BaseJpaTest extends BaseTest {
}
ourLog.info("Found {} results", size);
List<IBaseResource> resources = theProvider.getResources(0, size);
List<IBaseResource> resources = theProvider instanceof PersistedJpaBundleProvider ?
theProvider.getResources(0, size) :
theProvider.getResources(0, Integer.MAX_VALUE);
for (IBaseResource next : resources) {
retVal.add(next.getIdElement().toUnqualifiedVersionless());
}

View File

@ -247,16 +247,16 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mySrd));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, null, mySrd));
assertThat(actual, containsInAnyOrder(ptId1, obsId1, devId1));
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obstext1")));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, mySrd));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, param, null, mySrd));
assertThat(actual, containsInAnyOrder(ptId1, obsId1, devId1));
request = mock(HttpServletRequest.class);
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, null, mySrd));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, null, null, mySrd));
assertThat(actual, containsInAnyOrder(ptId1, obsId1, obsId2, devId1));
/*
@ -272,7 +272,7 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mySrd));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, null, mySrd));
assertThat(actual, containsInAnyOrder(ptId1, obsId1, obsId4, devId1));
/*
@ -288,7 +288,7 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mySrd));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, null, mySrd));
assertThat(actual, containsInAnyOrder(ptId1, obsId4));
}
@ -339,11 +339,11 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mySrd));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, null, param, null, null, mySrd));
assertThat(actual, containsInAnyOrder(ptId1, obsId1, devId1));
request = mock(HttpServletRequest.class);
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, mySrd));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, null, mySrd));
assertThat(actual, containsInAnyOrder(ptId1, obsId1, obsId2, devId1, ptId2, obsId3));
/*
@ -359,7 +359,7 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mySrd));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, null, param, null, null, mySrd));
assertThat(actual, containsInAnyOrder(ptId1, obsId1, obsId4, devId1));
/*
@ -375,7 +375,7 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mySrd));
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, null, param, null, null, mySrd));
assertThat(actual, containsInAnyOrder(ptId1, obsId4));
}

View File

@ -116,11 +116,11 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
IIdType moId = myMedicationOrderDao.create(mo, mySrd).getId().toUnqualifiedVersionless();
HttpServletRequest request = mock(HttpServletRequest.class);
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, mySrd);
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, null, mySrd);
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId, patId2));
request = mock(HttpServletRequest.class);
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, null, mySrd);
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, null, null, mySrd);
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId));
}

View File

@ -1227,7 +1227,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
ourLog.info("Updated patient, got id: {}", idv2);
Bundle request = new Bundle();
request.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl("Patient?" + Constants.PARAM_COUNT + "=1");
request.addEntry().getRequest().setMethod(HTTPVerbEnum.GET).setUrl("Patient?" + Constants.PARAM_COUNT + "=1&_total=accurate");
Bundle resp = mySystemDao.transaction(mySrd, request);
assertEquals(1, resp.getEntry().size());

View File

@ -344,16 +344,16 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obstext1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, param, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
request = mock(HttpServletRequest.class);
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId2, devId1)));
/*
@ -369,7 +369,7 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId4, devId1)));
/*
@ -385,7 +385,7 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId4)));
}
@ -436,11 +436,11 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, param, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
request = mock(HttpServletRequest.class);
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId2, devId1, ptId2, obsId3)));
/*
@ -456,7 +456,7 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, param, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId4, devId1)));
/*
@ -472,7 +472,7 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, param, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId4)));
}

View File

@ -245,11 +245,11 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
IIdType moId = myMedicationRequestDao.create(mo, mySrd).getId().toUnqualifiedVersionless();
HttpServletRequest request = mock(HttpServletRequest.class);
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, mySrd);
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, null, mySrd);
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId, patId2));
request = mock(HttpServletRequest.class);
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, null, mySrd);
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, null, null, mySrd);
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId));
}
@ -276,7 +276,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
SearchParameterMap map = new SearchParameterMap();
map.setEverythingMode(EverythingModeEnum.PATIENT_INSTANCE);
IPrimitiveType<Integer> count = new IntegerType(1000);
IBundleProvider everything = myPatientDao.patientInstanceEverything(mySrd.getServletRequest(), new IdType("Patient/A161443"), count, null, null, null, null, null, mySrd);
IBundleProvider everything = myPatientDao.patientInstanceEverything(mySrd.getServletRequest(), new IdType("Patient/A161443"), count, null, null, null, null, null, null, mySrd);
TreeSet<String> ids = new TreeSet<>(toUnqualifiedVersionlessIdValues(everything));
assertThat(ids, hasItem("List/A161444"));
@ -2114,7 +2114,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
IBundleProvider values = myPatientDao.search(map);
assertEquals(5, values.size().intValue());
assertEquals(null, values.size());
assertEquals(5, values.getResources(0, 1000).size());
}

View File

@ -2050,7 +2050,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
ourLog.info("Updated patient, got id: {}", idv2);
Bundle request = new Bundle();
request.addEntry().getRequest().setMethod(HTTPVerb.GET).setUrl("Patient?" + Constants.PARAM_COUNT + "=1");
request.addEntry().getRequest().setMethod(HTTPVerb.GET).setUrl("Patient?" + Constants.PARAM_COUNT + "=1&_total=accurate");
Bundle resp = mySystemDao.transaction(mySrd, request);
assertEquals(1, resp.getEntry().size());

View File

@ -338,16 +338,16 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obstext1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, param, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
request = mock(HttpServletRequest.class);
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, null, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId2, devId1)));
/*
@ -363,7 +363,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId4, devId1)));
/*
@ -379,7 +379,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, param, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientInstanceEverything(request, ptId1, null, null, null, null, param, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId4)));
}
@ -430,11 +430,11 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, param, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, devId1)));
request = mock(HttpServletRequest.class);
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId2, devId1, ptId2, obsId3)));
/*
@ -450,7 +450,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, param, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId4, devId1)));
/*
@ -466,7 +466,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test {
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, param, null, null, mockSrd()));
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, param, null, null, mockSrd()));
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId4)));
}

View File

@ -750,11 +750,11 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
IIdType moId = myMedicationRequestDao.create(mo, mySrd).getId().toUnqualifiedVersionless();
HttpServletRequest request = mock(HttpServletRequest.class);
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, mySrd);
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, null, mySrd);
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId, patId2));
request = mock(HttpServletRequest.class);
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, null, mySrd);
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, null, null, mySrd);
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId));
}
@ -782,7 +782,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
SearchParameterMap map = new SearchParameterMap();
map.setEverythingMode(EverythingModeEnum.PATIENT_INSTANCE);
IPrimitiveType<Integer> count = new IntegerType(1000);
IBundleProvider everything = myPatientDao.patientInstanceEverything(mySrd.getServletRequest(), new IdType("Patient/A161443"), count, null, null, null, null, null, mySrd);
IBundleProvider everything = myPatientDao.patientInstanceEverything(mySrd.getServletRequest(), new IdType("Patient/A161443"), count, null, null, null, null, null, null, mySrd);
TreeSet<String> ids = new TreeSet<>(toUnqualifiedVersionlessIdValues(everything));
assertThat(ids, hasItem("List/A161444"));
@ -4161,7 +4161,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
IBundleProvider values = myPatientDao.search(map);
assertEquals(5, values.size().intValue());
assertEquals(null, values.size());
assertEquals(5, values.getResources(0, 1000).size());
}

View File

@ -378,11 +378,11 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
IIdType moId = myMedicationRequestDao.create(mo, mySrd).getId().toUnqualifiedVersionless();
HttpServletRequest request = mock(HttpServletRequest.class);
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, mySrd);
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, null, mySrd);
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId, patId2));
request = mock(HttpServletRequest.class);
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, null, mySrd);
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, null, null, mySrd);
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId));
}
@ -410,7 +410,7 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
SearchParameterMap map = new SearchParameterMap();
map.setEverythingMode(EverythingModeEnum.PATIENT_INSTANCE);
IPrimitiveType<Integer> count = new IntegerType(1000);
IBundleProvider everything = myPatientDao.patientInstanceEverything(mySrd.getServletRequest(), new IdType("Patient/A161443"), count, null, null, null, null, null, mySrd);
IBundleProvider everything = myPatientDao.patientInstanceEverything(mySrd.getServletRequest(), new IdType("Patient/A161443"), count, null, null, null, null, null, null, mySrd);
TreeSet<String> ids = new TreeSet<>(toUnqualifiedVersionlessIdValues(everything));
assertThat(ids, hasItem("List/A161444"));
@ -2667,7 +2667,7 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
IBundleProvider values = myPatientDao.search(map);
assertEquals(5, values.size().intValue());
assertEquals(null, values.size());
assertEquals(5, values.getResources(0, 1000).size());
}

View File

@ -2689,7 +2689,7 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest {
ourLog.info("Updated patient, got id: {}", idv2);
Bundle request = new Bundle();
request.addEntry().getRequest().setMethod(HTTPVerb.GET).setUrl("Patient?" + Constants.PARAM_COUNT + "=1");
request.addEntry().getRequest().setMethod(HTTPVerb.GET).setUrl("Patient?" + Constants.PARAM_COUNT + "=1&_total=accurate");
Bundle resp = mySystemDao.transaction(mySrd, request);
assertEquals(1, resp.getEntry().size());

View File

@ -39,6 +39,7 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.dstu2.resource.SearchParameter;
import ca.uhn.fhir.model.dstu2.valueset.XPathUsageTypeEnum;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.gclient.NumberClientParam;
import org.apache.commons.io.IOUtils;
@ -1694,7 +1695,19 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
.useHttpGet()
.execute();
assertEquals(21, response.getEntry().size());
assertEquals(10, response.getEntry().size());
assertEquals(null, response.getTotalElement().getValue());
assertThat(response.getLink("next").getUrl(), not(emptyString()));
response = ourClient.fetchResourceFromUrl(ca.uhn.fhir.model.dstu2.resource.Bundle.class, response.getLink("next").getUrl());
assertEquals(10, response.getEntry().size());
assertEquals(null, response.getTotalElement().getValue());
assertThat(response.getLink("next").getUrl(), not(emptyString()));
response = ourClient.fetchResourceFromUrl(ca.uhn.fhir.model.dstu2.resource.Bundle.class, response.getLink("next").getUrl());
assertEquals(1, response.getEntry().size());
assertEquals(21, response.getTotalElement().getValue().intValue());
assertEquals(null, response.getLink("next"));

View File

@ -2347,7 +2347,19 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
.useHttpGet()
.execute();
assertEquals(21, response.getEntry().size());
assertEquals(10, response.getEntry().size());
assertEquals(null, response.getTotalElement().getValue());
assertThat(response.getLink("next").getUrl(), not(emptyString()));
response = ourClient.fetchResourceFromUrl(Bundle.class, response.getLink("next").getUrl());
assertEquals(10, response.getEntry().size());
assertEquals(null, response.getTotalElement().getValue());
assertThat(response.getLink("next").getUrl(), not(emptyString()));
response = ourClient.fetchResourceFromUrl(Bundle.class, response.getLink("next").getUrl());
assertEquals(1, response.getEntry().size());
assertEquals(21, response.getTotalElement().getValue().intValue());
assertEquals(null, response.getLink("next"));

View File

@ -2946,7 +2946,19 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
.useHttpGet()
.execute();
assertEquals(21, response.getEntry().size());
assertEquals(10, response.getEntry().size());
assertEquals(null, response.getTotalElement().getValue());
assertThat(response.getLink("next").getUrl(), not(emptyString()));
response = myClient.fetchResourceFromUrl(Bundle.class, response.getLink("next").getUrl());
assertEquals(10, response.getEntry().size());
assertEquals(null, response.getTotalElement().getValue());
assertThat(response.getLink("next").getUrl(), not(emptyString()));
response = myClient.fetchResourceFromUrl(Bundle.class, response.getLink("next").getUrl());
assertEquals(1, response.getEntry().size());
assertEquals(21, response.getTotalElement().getValue().intValue());
assertEquals(null, response.getLink("next"));

View File

@ -11,6 +11,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.test.util.AopTestUtils;
import static org.apache.commons.lang3.StringUtils.leftPad;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
@ -89,5 +90,55 @@ public class PagingMultinodeProviderDstu3Test extends BaseResourceProviderDstu3T
assertThat(toUnqualifiedVersionlessIdValues(found), contains("Patient/A030", "Patient/A031", "Patient/A032", "Patient/A033", "Patient/A034", "Patient/A035", "Patient/A036", "Patient/A037", "Patient/A038", "Patient/A039"));
}
@Test
public void testSearchWithOffset() {
{
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)
.offset(0)
.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"));
assertThat(found.getLink().stream().filter(l -> l.getRelation().equals("next")).map(l -> l.getUrl()).findAny()
.orElseThrow(() -> new IllegalStateException("No next page link")).contains("_offset=10"), is(true));
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"));
}
}

View File

@ -22,12 +22,14 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.BaseIterator;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import com.google.common.collect.Lists;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@ -552,6 +554,32 @@ public class SearchCoordinatorSvcImplTest {
assertEquals("799", resources.get(789).getIdElement().getValueAsString());
}
@Test
public void testSynchronousSearchWithOffset() {
when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())).thenReturn(mySearchBuilder);
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronous(true);
params.add("name", new StringParam("ANAME"));
params.setCount(10);
params.setOffset(10);
params.setSearchTotalMode(SearchTotalModeEnum.ACCURATE);
List<ResourcePersistentId> pids = createPidSequence(30);
when(mySearchBuilder.createCountQuery(same(params), any(String.class), nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(Lists.newArrayList(Long.valueOf(20L)).iterator());
when(mySearchBuilder.createQuery(same(params), any(), nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(new ResultIterator(pids.subList(10, 20).iterator()));
doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any());
IBundleProvider result = mySvc.registerSearch(myCallingDao, params, "Patient", new CacheControlDirective(), null);
assertNull(result.getUuid());
assertEquals(20, result.size().intValue());
List<IBaseResource> resources = result.getResources(0, 10);
assertEquals(10, resources.size());
assertEquals("20", resources.get(0).getIdElement().getValueAsString());
}
@Test
public void testSynchronousSearchUpTo() {
when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())).thenReturn(mySearchBuilder);

View File

@ -101,7 +101,7 @@ public class MatchUrlService {
IQueryParameterAnd<?> param = ParameterUtil.parseQueryParams(myContext, RestSearchParameterTypeEnum.HAS, nextParamName, paramList);
paramMap.add(nextParamName, param);
} else if (Constants.PARAM_COUNT.equals(nextParamName)) {
if (paramList.size() > 0 && paramList.get(0).size() > 0) {
if (paramList != null && paramList.size() > 0 && paramList.get(0).size() > 0) {
String intString = paramList.get(0).get(0);
try {
paramMap.setCount(Integer.parseInt(intString));
@ -109,6 +109,15 @@ public class MatchUrlService {
throw new InvalidRequestException("Invalid " + Constants.PARAM_COUNT + " value: " + intString);
}
}
} else if (Constants.PARAM_OFFSET.equals(nextParamName)) {
if (paramList != null && paramList.size() > 0 && paramList.get(0).size() > 0) {
String intString = paramList.get(0).get(0);
try {
paramMap.setOffset(Integer.parseInt(intString));
} catch (NumberFormatException e) {
throw new InvalidRequestException("Invalid " + Constants.PARAM_OFFSET + " value: " + intString);
}
}
} else if (ResourceMetaParams.RESOURCE_META_PARAMS.containsKey(nextParamName)) {
if (isNotBlank(paramList.get(0).getQualifier()) && paramList.get(0).getQualifier().startsWith(".")) {
throw new InvalidRequestException("Invalid parameter chain: " + nextParamName + paramList.get(0).getQualifier());

View File

@ -51,6 +51,7 @@ public class SearchParameterMap implements Serializable {
private static final long serialVersionUID = 1L;
private Integer myCount;
private Integer myOffset;
private EverythingModeEnum myEverythingMode = null;
private Set<Include> myIncludes;
private DateRangeParam myLastUpdated;
@ -198,6 +199,14 @@ public class SearchParameterMap implements Serializable {
myCount = theCount;
}
public Integer getOffset() {
return myOffset;
}
public void setOffset(Integer theOffset) {
myOffset = theOffset;
}
public EverythingModeEnum getEverythingMode() {
return myEverythingMode;
}
@ -461,6 +470,13 @@ public class SearchParameterMap implements Serializable {
b.append(getCount());
}
if (getOffset() != null) {
addUrlParamSeparator(b);
b.append(Constants.PARAM_OFFSET);
b.append('=');
b.append(getOffset());
}
// Summary mode (_summary)
if (getSummaryMode() != null) {
addUrlParamSeparator(b);
@ -652,7 +668,7 @@ public class SearchParameterMap implements Serializable {
return mySearchParameterMap.get(theName);
}
private void put(String theName, List<List<IQueryParameterType>> theParams) {
public void put(String theName, List<List<IQueryParameterType>> theParams) {
mySearchParameterMap.put(theName, theParams);
}

View File

@ -74,6 +74,20 @@ public interface IRestfulServerDefaults {
*/
IPagingProvider getPagingProvider();
/**
* Default page size for searches. Null means no limit (DaoConfig might have size limit however)
*/
default Integer getDefaultPageSize() {
return null;
}
/**
* Maximum page size for searches. Null means no upper limit.
*/
default Integer getMaximumPageSize() {
return null;
}
/**
* Should the server "pretty print" responses by default (requesting clients can always override this default by
* supplying an <code>Accept</code> header in the request, or a <code>_pretty</code>

View File

@ -140,6 +140,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
private boolean myIgnoreServerParsedRequestParameters = true;
private String myImplementationDescription;
private IPagingProvider myPagingProvider;
private Integer myDefaultPageSize;
private Integer myMaximumPageSize;
private boolean myStatelessPagingDefault = false;
private Lock myProviderRegistrationMutex = new ReentrantLock();
private Map<String, ResourceBinding> myResourceNameToBinding = new HashMap<>();
private IServerAddressStrategy myServerAddressStrategy = new IncomingRequestAddressStrategy();
@ -674,6 +677,30 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
myPagingProvider = thePagingProvider;
}
@Override
public Integer getDefaultPageSize() {
return myDefaultPageSize;
}
/**
* Sets the default page size to use, or <code>null</code> if no default page size
*/
public void setDefaultPageSize(Integer thePageSize) {
myDefaultPageSize = thePageSize;
}
@Override
public Integer getMaximumPageSize() {
return myMaximumPageSize;
}
/**
* Sets the maximum page size to use, or <code>null</code> if no maximum page size
*/
public void setMaximumPageSize(Integer theMaximumPageSize) {
myMaximumPageSize = theMaximumPageSize;
}
/**
* Provides the non-resource specific providers which implement method calls on this server
*

View File

@ -49,6 +49,7 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.BinaryUtil;
import ca.uhn.fhir.util.DateUtils;
import ca.uhn.fhir.util.UrlUtil;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseBinary;
@ -233,6 +234,76 @@ public class RestfulServerUtils {
}
}
public static String createLinkSelf(String theServerBase, RequestDetails theRequest) {
StringBuilder b = new StringBuilder();
b.append(theServerBase);
if (isNotBlank(theRequest.getRequestPath())) {
b.append('/');
if (isNotBlank(theRequest.getTenantId()) && theRequest.getRequestPath().startsWith(theRequest.getTenantId() + "/")) {
b.append(theRequest.getRequestPath().substring(theRequest.getTenantId().length() + 1));
} else {
b.append(theRequest.getRequestPath());
}
}
// For POST the URL parameters get jumbled with the post body parameters so don't include them, they might be huge
if (theRequest.getRequestType() == RequestTypeEnum.GET) {
boolean first = true;
Map<String, String[]> parameters = theRequest.getParameters();
for (String nextParamName : new TreeSet<>(parameters.keySet())) {
for (String nextParamValue : parameters.get(nextParamName)) {
if (first) {
b.append('?');
first = false;
} else {
b.append('&');
}
b.append(UrlUtil.escapeUrlParam(nextParamName));
b.append('=');
b.append(UrlUtil.escapeUrlParam(nextParamValue));
}
}
}
return b.toString();
}
public static String createOffsetPagingLink(String theServerBase, String requestPath, String tenantId, Integer theOffset, Integer theCount, Map<String, String[]> theRequestParameters) {
StringBuilder b = new StringBuilder();
b.append(theServerBase);
if (isNotBlank(requestPath)) {
b.append('/');
if (isNotBlank(tenantId) && requestPath.startsWith(tenantId + "/")) {
b.append(requestPath.substring(tenantId.length() + 1));
} else {
b.append(requestPath);
}
}
Map<String, String[]> params = Maps.newLinkedHashMap(theRequestParameters);
params.put(Constants.PARAM_OFFSET, new String[]{String.valueOf(theOffset)});
params.put(Constants.PARAM_COUNT, new String[]{String.valueOf(theCount)});
boolean first = true;
for (String nextParamName : new TreeSet<>(params.keySet())) {
for (String nextParamValue : params.get(nextParamName)) {
if (first) {
b.append('?');
first = false;
} else {
b.append('&');
}
b.append(UrlUtil.escapeUrlParam(nextParamName));
b.append('=');
b.append(UrlUtil.escapeUrlParam(nextParamValue));
}
}
return b.toString();
}
public static String createPagingLink(Set<Include> theIncludes, RequestDetails theRequestDetails, String theSearchId, int theOffset, int theCount, Map<String, String[]> theRequestParameters, boolean thePrettyPrint,
BundleTypeEnum theBundleType) {
return createPagingLink(theIncludes, theRequestDetails, theSearchId, theOffset, theCount, theRequestParameters, thePrettyPrint,
@ -573,6 +644,10 @@ public class RestfulServerUtils {
return RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_COUNT);
}
public static Integer extractOffsetParameter(RequestDetails theRequest) {
return RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_OFFSET);
}
public static IPrimitiveType<Date> extractLastUpdatedFromResource(IBaseResource theResource) {
IPrimitiveType<Date> lastUpdated = null;
if (theResource instanceof IResource) {

View File

@ -117,13 +117,23 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
IBaseResource createBundleFromBundleProvider(IRestfulServer<?> theServer, RequestDetails theRequest, Integer theLimit, String theLinkSelf, Set<Include> theIncludes,
IBundleProvider theResult, int theOffset, BundleTypeEnum theBundleType, EncodingEnum theLinkEncoding, String theSearchId) {
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory();
final Integer requestOffset = RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_OFFSET);
int numToReturn;
String searchId = null;
List<IBaseResource> resourceList;
Integer numTotalResults = theResult.size();
if (theServer.getPagingProvider() == null) {
numToReturn = numTotalResults;
if (requestOffset != null || theServer.getPagingProvider() == null) {
if (theLimit != null) {
numToReturn = theLimit;
} else {
if (theServer.getDefaultPageSize() != null) {
numToReturn = theServer.getDefaultPageSize();
} else {
numToReturn = numTotalResults != null ? numTotalResults : Integer.MAX_VALUE;
}
}
if (numToReturn > 0) {
resourceList = theResult.getResources(0, numToReturn);
} else {
@ -200,7 +210,18 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
String linkPrev = null;
String linkNext = null;
if (isNotBlank(theResult.getCurrentPageId())) {
if (theServer.getPagingProvider() == null || requestOffset != null) {
int myOffset = requestOffset != null ? requestOffset : 0;
// Paging without caching
// We're doing requestOffset pages
if (numTotalResults == null || myOffset + numToReturn < numTotalResults) {
linkNext = (RestfulServerUtils.createOffsetPagingLink(serverBase, theRequest.getRequestPath(), theRequest.getTenantId(), myOffset + numToReturn, numToReturn, theRequest.getParameters()));
}
if (myOffset > 0) {
int start = Math.max(0, myOffset - numToReturn);
linkPrev = RestfulServerUtils.createOffsetPagingLink(serverBase, theRequest.getRequestPath(), theRequest.getTenantId(), start, numToReturn, theRequest.getParameters());
}
} else if (isNotBlank(theResult.getCurrentPageId())) {
// We're doing named pages
searchId = theResult.getUuid();
if (isNotBlank(theResult.getNextPageId())) {
@ -221,8 +242,8 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
linkNext = (RestfulServerUtils.createPagingLink(theIncludes, theRequest, searchId, theOffset + numToReturn, numToReturn, theRequest.getParameters(), prettyPrint, theBundleType));
}
if (theOffset > 0) {
int start = Math.max(0, theOffset - theLimit);
linkPrev = RestfulServerUtils.createPagingLink(theIncludes, theRequest, searchId, start, theLimit, theRequest.getParameters(), prettyPrint, theBundleType);
int start = Math.max(0, theOffset - numToReturn);
linkPrev = RestfulServerUtils.createPagingLink(theIncludes, theRequest, searchId, start, numToReturn, theRequest.getParameters(), prettyPrint, theBundleType);
}
}
}
@ -260,37 +281,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
* Figure out the self-link for this request
*/
String serverBase = theRequest.getServerBaseForRequest();
String linkSelf;
StringBuilder b = new StringBuilder();
b.append(serverBase);
if (isNotBlank(theRequest.getRequestPath())) {
b.append('/');
if (isNotBlank(theRequest.getTenantId()) && theRequest.getRequestPath().startsWith(theRequest.getTenantId() + "/")) {
b.append(theRequest.getRequestPath().substring(theRequest.getTenantId().length() + 1));
} else {
b.append(theRequest.getRequestPath());
}
}
// For POST the URL parameters get jumbled with the post body parameters so don't include them, they might be huge
if (theRequest.getRequestType() == RequestTypeEnum.GET) {
boolean first = true;
Map<String, String[]> parameters = theRequest.getParameters();
for (String nextParamName : new TreeSet<>(parameters.keySet())) {
for (String nextParamValue : parameters.get(nextParamName)) {
if (first) {
b.append('?');
first = false;
} else {
b.append('&');
}
b.append(UrlUtil.escapeUrlParam(nextParamName));
b.append('=');
b.append(UrlUtil.escapeUrlParam(nextParamValue));
}
}
}
linkSelf = b.toString();
String linkSelf = RestfulServerUtils.createLinkSelf(theRequest.getFhirServerBase(), theRequest);
if (getMethodReturnType() == MethodReturnTypeEnum.BUNDLE_RESOURCE) {
IBaseResource resource;

View File

@ -213,6 +213,8 @@ public class MethodUtil {
((AtParameter) param).setType(theContext, parameterType, innerCollectionType, outerCollectionType);
} else if (nextAnnotation instanceof Count) {
param = new CountParameter();
} else if (nextAnnotation instanceof Offset) {
param = new OffsetParameter();
} else if (nextAnnotation instanceof GraphQLQueryUrl) {
param = new GraphQLQueryUrlParameter();
} else if (nextAnnotation instanceof GraphQLQueryBody) {

View File

@ -0,0 +1,72 @@
package ca.uhn.fhir.rest.server.method;
/*
* #%L
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2018 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 java.lang.reflect.Method;
import java.util.Collection;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Offset;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class OffsetParameter implements IParameter {
private Class<?> myType;
@Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
String[] sinceParams = theRequest.getParameters().get(Constants.PARAM_OFFSET);
if (sinceParams != null) {
if (sinceParams.length > 0) {
if (StringUtils.isNotBlank(sinceParams[0])) {
try {
IntegerDt since = new IntegerDt(sinceParams[0]);
return ParameterUtil.fromInteger(myType, since);
} catch (DataFormatException e) {
throw new InvalidRequestException("Invalid " + Constants.PARAM_OFFSET + " value: " + sinceParams[0]);
}
}
}
}
return ParameterUtil.fromInteger(myType, null);
}
@Override
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
if (theOuterCollectionType != null) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + Offset.class.getName() + " but can not be of collection type");
}
if (!ParameterUtil.isBindableIntegerType(theParameterType)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + Offset.class.getName() + " but type '" + theParameterType + "' is an invalid type, must be one of Integer or IntegerType");
}
myType = theParameterType;
}
}

View File

@ -133,6 +133,9 @@ public class ${className}ResourceProvider extends
@ca.uhn.fhir.rest.annotation.Count
Integer theCount,
@ca.uhn.fhir.rest.annotation.Offset
Integer theOffset,
SummaryEnum theSummaryMode,
SearchTotalModeEnum theSearchTotalMode
@ -159,6 +162,7 @@ public class ${className}ResourceProvider extends
paramMap.setIncludes(theIncludes);
paramMap.setSort(theSort);
paramMap.setCount(theCount);
paramMap.setOffset(theOffset);
paramMap.setSummaryMode(theSummaryMode);
paramMap.setSearchTotalMode(theSearchTotalMode);