From 1435540320979a55d94e00854edc9dcf75a9137a Mon Sep 17 00:00:00 2001 From: Tuomo Ala-Vannesluoma Date: Tue, 13 Oct 2020 00:42:03 +0300 Subject: [PATCH] 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 --- .../ca/uhn/fhir/rest/annotation/Offset.java | 38 ++++++++ .../java/ca/uhn/fhir/rest/api/Constants.java | 1 + .../java/ca/uhn/fhir/rest/gclient/IQuery.java | 10 ++ .../fhir/rest/client/impl/GenericClient.java | 15 +++ .../BaseResourceReturningMethodBinding.java | 1 + .../fhir/rest/client/method/MethodUtil.java | 2 + .../rest/client/method/OffsetParameter.java | 65 +++++++++++++ .../RestfulPatientResourceProviderMore.java | 37 ++++++++ .../uhn/hapi/fhir/docs/server_plain/paging.md | 13 +++ .../server_plain/rest_operations_search.md | 57 ++++++++++++ .../api/dao/IFhirResourceDaoComposition.java | 2 +- .../api/dao/IFhirResourceDaoEncounter.java | 4 +- .../jpa/api/dao/IFhirResourceDaoPatient.java | 4 +- .../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 19 +++- .../dao/FhirResourceDaoCompositionDstu2.java | 2 +- .../dao/FhirResourceDaoEncounterDstu2.java | 9 +- .../jpa/dao/FhirResourceDaoPatientDstu2.java | 13 ++- .../ca/uhn/fhir/jpa/dao/SearchBuilder.java | 46 +++++++--- .../FhirResourceDaoCompositionDstu3.java | 5 +- .../dstu3/FhirResourceDaoEncounterDstu3.java | 9 +- .../dstu3/FhirResourceDaoPatientDstu3.java | 13 ++- .../QueryRootEntryResourceTable.java | 4 +- .../dao/predicate/querystack/QueryStack.java | 16 +++- .../dao/r4/FhirResourceDaoCompositionR4.java | 5 +- .../dao/r4/FhirResourceDaoEncounterR4.java | 9 +- .../jpa/dao/r4/FhirResourceDaoPatientR4.java | 13 ++- .../dao/r5/FhirResourceDaoCompositionR5.java | 5 +- .../dao/r5/FhirResourceDaoEncounterR5.java | 9 +- .../jpa/dao/r5/FhirResourceDaoPatientR5.java | 13 ++- ...seJpaResourceProviderCompositionDstu2.java | 2 +- ...BaseJpaResourceProviderEncounterDstu2.java | 12 ++- .../BaseJpaResourceProviderPatientDstu2.java | 12 ++- ...seJpaResourceProviderCompositionDstu3.java | 6 +- ...BaseJpaResourceProviderEncounterDstu3.java | 12 ++- .../BaseJpaResourceProviderPatientDstu3.java | 12 ++- .../BaseJpaResourceProviderCompositionR4.java | 6 +- .../BaseJpaResourceProviderEncounterR4.java | 12 ++- .../r4/BaseJpaResourceProviderPatientR4.java | 12 ++- .../BaseJpaResourceProviderCompositionR5.java | 6 +- .../BaseJpaResourceProviderEncounterR5.java | 12 ++- .../r5/BaseJpaResourceProviderPatientR5.java | 12 ++- .../jpa/search/SearchCoordinatorSvcImpl.java | 92 +++++++++++++++++-- .../java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java | 6 +- .../FhirResourceDaoDstu2SearchFtTest.java | 18 ++-- .../FhirResourceDaoDstu2SearchNoFtTest.java | 4 +- .../jpa/dao/dstu2/FhirSystemDaoDstu2Test.java | 2 +- .../FhirResourceDaoDstu3SearchFtTest.java | 18 ++-- .../FhirResourceDaoDstu3SearchNoFtTest.java | 8 +- .../jpa/dao/dstu3/FhirSystemDaoDstu3Test.java | 2 +- .../dao/r4/FhirResourceDaoR4SearchFtTest.java | 18 ++-- .../r4/FhirResourceDaoR4SearchNoFtTest.java | 8 +- .../FhirResourceDaoR4SearchNoHashesTest.java | 8 +- .../fhir/jpa/dao/r4/FhirSystemDaoR4Test.java | 2 +- .../provider/ResourceProviderDstu2Test.java | 15 ++- .../dstu3/ResourceProviderDstu3Test.java | 14 ++- .../provider/r4/ResourceProviderR4Test.java | 14 ++- .../PagingMultinodeProviderDstu3Test.java | 51 ++++++++++ .../search/SearchCoordinatorSvcImplTest.java | 28 ++++++ .../fhir/jpa/searchparam/MatchUrlService.java | 11 ++- .../jpa/searchparam/SearchParameterMap.java | 18 +++- .../rest/server/IRestfulServerDefaults.java | 14 +++ .../uhn/fhir/rest/server/RestfulServer.java | 27 ++++++ .../fhir/rest/server/RestfulServerUtils.java | 75 +++++++++++++++ .../BaseResourceReturningMethodBinding.java | 63 ++++++------- .../fhir/rest/server/method/MethodUtil.java | 2 + .../rest/server/method/OffsetParameter.java | 72 +++++++++++++++ .../resources/vm/jpa_resource_provider.vm | 4 + 67 files changed, 975 insertions(+), 174 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Offset.java create mode 100644 hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OffsetParameter.java create mode 100644 hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OffsetParameter.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Offset.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Offset.java new file mode 100644 index 00000000000..1cb0d314607 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Offset.java @@ -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 +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java index 15b6186a55f..306ef8e0983 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java @@ -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"; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IQuery.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IQuery.java index 89e4a4e9041..5d0e4546fe7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IQuery.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IQuery.java @@ -49,6 +49,16 @@ public interface IQuery extends IBaseQuery>, IClientExecutable count(int theCount); + /** + * Specifies the _offset 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 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 diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java index a3f3f248d3c..dffee8b9771 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java @@ -1817,6 +1817,7 @@ public class GenericClient extends BaseClient implements IGenericClient { private List myInclude = new ArrayList<>(); private DateRangeParam myLastUpdated; private Integer myParamLimit; + private Integer myParamOffset; private List> 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)); diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseResourceReturningMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseResourceReturningMethodBinding.java index 7b0a9128a65..d4200a22d9f 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseResourceReturningMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseResourceReturningMethodBinding.java @@ -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); diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java index 064e2a070d5..9d57b59170c 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java @@ -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) { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OffsetParameter.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OffsetParameter.java new file mode 100644 index 00000000000..1ef5a28db19 --- /dev/null +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OffsetParameter.java @@ -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> 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> theOuterCollectionType, Class> 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"); + } + } + +} diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RestfulPatientResourceProviderMore.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RestfulPatientResourceProviderMore.java index f7538d05b11..f8ba8ebb553 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RestfulPatientResourceProviderMore.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RestfulPatientResourceProviderMore.java @@ -149,6 +149,43 @@ public List findPatients( } //END SNIPPET: sort +//START SNIPPET: count +@Search +public List findPatients( + @RequiredParam(name=Patient.SP_IDENTIFIER) StringParam theParameter, + @Count Integer theCount) { + List retVal=new ArrayList(); // 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 findPatients( + @RequiredParam(name=Patient.SP_IDENTIFIER) StringParam theParameter, + @Offset Integer theOffset, + @Count Integer theCount) { + List retVal=new ArrayList(); // 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 findPatients( diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_plain/paging.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_plain/paging.md index 3ab0de11cde..0679c5759c7 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_plain/paging.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_plain/paging.md @@ -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` return type in your provider methods. In other words, instead of returning *List*, your search method will return [IBundleProvider](/hapi-fhir/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/api/server/IBundleProvider.html). diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_plain/rest_operations_search.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_plain/rest_operations_search.md index 64299f669e9..0de9904e1ae 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_plain/rest_operations_search.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_plain/rest_operations_search.md @@ -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. diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoComposition.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoComposition.java index cabe7d62ef0..9375965db81 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoComposition.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoComposition.java @@ -32,6 +32,6 @@ import javax.servlet.http.HttpServletRequest; public interface IFhirResourceDaoComposition extends IFhirResourceDao { - IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails); + IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails); } diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoEncounter.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoEncounter.java index 65aea252217..dd1a8ac9069 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoEncounter.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoEncounter.java @@ -31,8 +31,8 @@ import javax.servlet.http.HttpServletRequest; public interface IFhirResourceDaoEncounter extends IFhirResourceDao { - IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdate, SortSpec theSort); + IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdate, SortSpec theSort); - IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSortSpec); + IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSortSpec); } diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoPatient.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoPatient.java index 57fbe535d24..231a163b346 100644 --- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoPatient.java +++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoPatient.java @@ -33,9 +33,9 @@ import javax.servlet.http.HttpServletRequest; public interface IFhirResourceDaoPatient extends IFhirResourceDao { - IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdate, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails); + IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdate, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails); - IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSortSpec, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails); + IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSortSpec, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 5c8e1111d07..b0df0d83e63 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -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 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()); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoCompositionDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoCompositionDstu2.java index d4a0461fe62..e981ef5b301 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoCompositionDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoCompositionDstu2.java @@ -35,7 +35,7 @@ import javax.servlet.http.HttpServletRequest; public class FhirResourceDaoCompositionDstu2 extends BaseHapiFhirResourceDaoimplements IFhirResourceDaoComposition { @Override - public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) { + public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) { throw new NotImplementedOperationException("$document not implemented in DSTU2"); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoEncounterDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoEncounterDstu2.java index a4fd3d11be4..f0717d35996 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoEncounterDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoEncounterDstu2.java @@ -38,11 +38,14 @@ import java.util.Collections; public class FhirResourceDaoEncounterDstu2 extends BaseHapiFhirResourceDaoimplements IFhirResourceDaoEncounter { @Override - public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSort) { + public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType 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 theCount, DateRangeParam theLastUpdated, SortSpec theSort) { - return encounterInstanceEverything(theServletRequest, null, theCount, theLastUpdated, theSort); + public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort) { + return encounterInstanceEverything(theServletRequest, null, theCount, theOffset, theLastUpdated, theSort); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java index f479846db4d..07d61457be7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoPatientDstu2.java @@ -47,11 +47,14 @@ public class FhirResourceDaoPatientDstu2 extends BaseHapiFhirResourceDao theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequest) { + private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType theCount, IPrimitiveType 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 theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) { + public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType 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 theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) { + public IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, IPrimitiveType 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); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index 71749d5e968..74129cef3fc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -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> queries = createQuery(null, null, true, theRequest, null); + List> 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> createQuery(SortSpec sort, Integer theMaximumResults, boolean theCount, RequestDetails theRequest, + private List> createQuery(SortSpec sort, Integer theOffset, Integer theMaximumResults, boolean theCount, RequestDetails theRequest, SearchRuntimeDetails theSearchRuntimeDetails) { List 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().chunk(ResourcePersistentId.toLongList(pids), t-> doCreateChunkedQueries(t, sort, theCount, theRequest, myQueries)); + new QueryChunker().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 thePids, SortSpec sort, boolean theCount, RequestDetails theRequest, ArrayList> theQueries) { + private void doCreateChunkedQueries(List thePids, SortSpec sort, Integer theOffset, boolean theCount, RequestDetails theRequest, ArrayList> 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 createChunkedQuery(SortSpec sort, Integer theMaximumResults, boolean theCount, RequestDetails theRequest, List thePidList) { + private TypedQuery createChunkedQuery(SortSpec sort, Integer theOffset, Integer theMaximumResults, boolean theCount, RequestDetails theRequest, List 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 outerQuery = (CriteriaQuery) myQueryStack.pop(); final TypedQuery 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 myPreResultsIterator; private ScrollableResultsIterator 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()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCompositionDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCompositionDstu3.java index 02e84d1aeee..4759ca6757b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCompositionDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCompositionDstu3.java @@ -39,11 +39,14 @@ import java.util.Collections; public class FhirResourceDaoCompositionDstu3 extends BaseHapiFhirResourceDao implements IFhirResourceDaoComposition { @Override - public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) { + public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType 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); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoEncounterDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoEncounterDstu3.java index 1f799baad55..258e5e25933 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoEncounterDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoEncounterDstu3.java @@ -39,11 +39,14 @@ import java.util.Collections; public class FhirResourceDaoEncounterDstu3 extends BaseHapiFhirResourceDao implements IFhirResourceDaoEncounter { @Override - public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSort) { + public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType 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 theCount, DateRangeParam theLastUpdated, SortSpec theSort) { - return encounterInstanceEverything(theServletRequest, null, theCount, theLastUpdated, theSort); + public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort) { + return encounterInstanceEverything(theServletRequest, null, theCount, theOffset, theLastUpdated, theSort); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java index 665ac5f2889..0481be84e17 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoPatientDstu3.java @@ -42,11 +42,14 @@ import java.util.Collections; public class FhirResourceDaoPatientDstu3 extends BaseHapiFhirResourceDaoimplements IFhirResourceDaoPatient { - private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequest) { + private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType theCount, IPrimitiveType 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 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 theCount, IPrimitiveType 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 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 theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) { + return doEverythingOperation(null, theCount, theOffset, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryRootEntryResourceTable.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryRootEntryResourceTable.java index 77b9f02792e..3f159876751 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryRootEntryResourceTable.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryRootEntryResourceTable.java @@ -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)); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryStack.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryStack.java index 635d1966acb..25c5155e8ae 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryStack.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryStack.java @@ -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 select DISTINCT RES_ID from HFJ_RESOURCE to the stack. All predicates added to the {@literal QueryRootStack} + * will be added to this select clause until {@link #pop()} is called. + *

+ * This method must only be called when the stack is empty. + *

+ */ + 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)); } /** diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCompositionR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCompositionR4.java index 66439ad9285..10b2b0d64d5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCompositionR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCompositionR4.java @@ -39,11 +39,14 @@ import java.util.Collections; public class FhirResourceDaoCompositionR4 extends BaseHapiFhirResourceDao implements IFhirResourceDaoComposition { @Override - public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) { + public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType 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); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoEncounterR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoEncounterR4.java index 13497fdb04e..657d5dbbf0e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoEncounterR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoEncounterR4.java @@ -39,11 +39,14 @@ import java.util.Collections; public class FhirResourceDaoEncounterR4 extends BaseHapiFhirResourceDao implements IFhirResourceDaoEncounter { @Override - public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSort) { + public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType 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 theCount, DateRangeParam theLastUpdated, SortSpec theSort) { - return encounterInstanceEverything(theServletRequest, null, theCount, theLastUpdated, theSort); + public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort) { + return encounterInstanceEverything(theServletRequest, null, theCount, theOffset, theLastUpdated, theSort); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoPatientR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoPatientR4.java index 1978a34be66..859a9f9395d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoPatientR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoPatientR4.java @@ -42,11 +42,14 @@ import java.util.Collections; public class FhirResourceDaoPatientR4 extends BaseHapiFhirResourceDaoimplements IFhirResourceDaoPatient { - private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequest) { + private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType theCount, IPrimitiveType 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 BaseHapiFhirResourceDaoim } @Override - public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType 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 theCount, IPrimitiveType 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 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 theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) { + return doEverythingOperation(null, theCount, theOffset, theLastUpdated, theSort, theContent, theNarrative, theFilter, theRequestDetails); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCompositionR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCompositionR5.java index 10cc3884a43..38dd3f6558d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCompositionR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoCompositionR5.java @@ -39,11 +39,14 @@ import java.util.Collections; public class FhirResourceDaoCompositionR5 extends BaseHapiFhirResourceDao implements IFhirResourceDaoComposition { @Override - public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) { + public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType 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); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoEncounterR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoEncounterR5.java index 31561bdaa5f..ee430b82d61 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoEncounterR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoEncounterR5.java @@ -39,11 +39,14 @@ import java.util.Collections; public class FhirResourceDaoEncounterR5 extends BaseHapiFhirResourceDao implements IFhirResourceDaoEncounter { @Override - public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSort) { + public IBundleProvider encounterInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType theCount, IPrimitiveType 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 theCount, DateRangeParam theLastUpdated, SortSpec theSort) { - return encounterInstanceEverything(theServletRequest, null, theCount, theLastUpdated, theSort); + public IBundleProvider encounterTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort) { + return encounterInstanceEverything(theServletRequest, null, theCount, theOffset, theLastUpdated, theSort); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoPatientR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoPatientR5.java index 90e2dc5d53a..107cb9ca02e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoPatientR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoPatientR5.java @@ -42,11 +42,14 @@ import java.util.Collections; public class FhirResourceDaoPatientR5 extends BaseHapiFhirResourceDao implements IFhirResourceDaoPatient { - private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequest) { + private IBundleProvider doEverythingOperation(IIdType theId, IPrimitiveType theCount, IPrimitiveType 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 i } @Override - public IBundleProvider patientInstanceEverything(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType 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 theCount, IPrimitiveType 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 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 theCount, IPrimitiveType theOffset, DateRangeParam theLastUpdated, SortSpec theSort, StringAndListParam theContent, StringAndListParam theNarrative, StringAndListParam theFilter, RequestDetails theRequestDetails) { + return doEverythingOperation(null, theCount, theOffset, theLastUpdated, theSort, theContent, theNarrative, theRequestDetails); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCompositionDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCompositionDstu2.java index b240ddf44bf..6337e13a66f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCompositionDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCompositionDstu2.java @@ -41,7 +41,7 @@ public class BaseJpaResourceProviderCompositionDstu2 extends JpaResourceProvider startRequest(theServletRequest); try { - ((IFhirResourceDaoComposition)getDao()).getDocumentForComposition(theServletRequest, null, null, null, null, null); + ((IFhirResourceDaoComposition)getDao()).getDocumentForComposition(theServletRequest, null, null, null, null, null, null); return null; } finally { endRequest(theServletRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounterDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounterDstu2.java index 9b68616caf6..23d8b3574bf 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounterDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounterDstu2.java @@ -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)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec); + return ((IFhirResourceDaoEncounter)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)getDao()).encounterTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec); + return ((IFhirResourceDaoEncounter)getDao()).encounterTypeEverything(theServletRequest, theCount, theOffset,theLastUpdated, theSortSpec); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderPatientDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderPatientDstu2.java index 4dd3307311e..cd399812591 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderPatientDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderPatientDstu2.java @@ -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) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails); + return ((IFhirResourceDaoPatient) 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) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails); + return ((IFhirResourceDaoPatient) getDao()).patientTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCompositionDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCompositionDstu3.java index e1dbbe14b26..decffca7b0b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCompositionDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCompositionDstu3.java @@ -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) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, theRequestDetails); + IBundleProvider bundleProvider = ((IFhirResourceDaoComposition) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec, theRequestDetails); List resourceList = bundleProvider.getResources(0, bundleProvider.size()); boolean foundCompositionResource = false; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderEncounterDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderEncounterDstu3.java index 93866e58668..232388d5815 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderEncounterDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderEncounterDstu3.java @@ -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)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec); + return ((IFhirResourceDaoEncounter)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)getDao()).encounterTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec); + return ((IFhirResourceDaoEncounter)getDao()).encounterTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderPatientDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderPatientDstu3.java index 4e507bc5c63..eecf67440f6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderPatientDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderPatientDstu3.java @@ -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) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails); + return ((IFhirResourceDaoPatient) 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) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails); + return ((IFhirResourceDaoPatient) getDao()).patientTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCompositionR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCompositionR4.java index ab5a61be160..c133919c62f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCompositionR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCompositionR4.java @@ -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) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, theRequestDetails); + IBundleProvider bundleProvider = ((IFhirResourceDaoComposition) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec, theRequestDetails); List resourceList = bundleProvider.getResources(0, bundleProvider.size()); boolean foundCompositionResource = false; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderEncounterR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderEncounterR4.java index fd468b9aad8..cb16afbb8e5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderEncounterR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderEncounterR4.java @@ -52,6 +52,10 @@ public class BaseJpaResourceProviderEncounterR4 extends JpaResourceProviderR4)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec); + return ((IFhirResourceDaoEncounter)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec); } finally { endRequest(theServletRequest); }} @@ -79,6 +83,10 @@ public class BaseJpaResourceProviderEncounterR4 extends JpaResourceProviderR4)getDao()).encounterTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec); + return ((IFhirResourceDaoEncounter)getDao()).encounterTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderPatientR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderPatientR4.java index 1d3e1c0843a..949c4201121 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderPatientR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderPatientR4.java @@ -62,6 +62,10 @@ public class BaseJpaResourceProviderPatientR4 extends JpaResourceProviderR4) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails); + return ((IFhirResourceDaoPatient) 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) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails); + return ((IFhirResourceDaoPatient) getDao()).patientTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCompositionR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCompositionR5.java index 43efc1f74d4..3c526f0803a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCompositionR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderCompositionR5.java @@ -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) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, theRequestDetails); + IBundleProvider bundleProvider = ((IFhirResourceDaoComposition) getDao()).getDocumentForComposition(theServletRequest, theId, theCount, theOffset,theLastUpdated, theSortSpec, theRequestDetails); List resourceList = bundleProvider.getResources(0, bundleProvider.size()); boolean foundCompositionResource = false; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderEncounterR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderEncounterR5.java index 0c2812a1146..4483edd0ddd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderEncounterR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderEncounterR5.java @@ -52,6 +52,10 @@ public class BaseJpaResourceProviderEncounterR5 extends JpaResourceProviderR5)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec); + return ((IFhirResourceDaoEncounter)getDao()).encounterInstanceEverything(theServletRequest, theId, theCount, theOffset, theLastUpdated, theSortSpec); } finally { endRequest(theServletRequest); }} @@ -79,6 +83,10 @@ public class BaseJpaResourceProviderEncounterR5 extends JpaResourceProviderR5)getDao()).encounterTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec); + return ((IFhirResourceDaoEncounter)getDao()).encounterTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderPatientR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderPatientR5.java index 047560078fe..754c2adbd86 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderPatientR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderPatientR5.java @@ -62,6 +62,10 @@ public class BaseJpaResourceProviderPatientR5 extends JpaResourceProviderR5) getDao()).patientInstanceEverything(theServletRequest, theId, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails); + return ((IFhirResourceDaoPatient) 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) getDao()).patientTypeEverything(theServletRequest, theCount, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails); + return ((IFhirResourceDaoPatient) getDao()).patientTypeEverything(theServletRequest, theCount, theOffset, theLastUpdated, theSortSpec, toStringAndList(theContent), toStringAndList(theNarrative), toStringAndList(theFilter), theRequestDetails); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java index eafcd442ce8..37e2aa4d432 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java @@ -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 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> contentAndTerms = theParams.get(Constants.PARAM_CONTENT); + List> textAndTerms = theParams.get(Constants.PARAM_TEXT); + + Iterator 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 { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java index 03d79468ca9..f51067c407c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java @@ -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 resources = theProvider.getResources(0, size); + List resources = theProvider instanceof PersistedJpaBundleProvider ? + theProvider.getResources(0, size) : + theProvider.getResources(0, Integer.MAX_VALUE); for (IBaseResource next : resources) { retVal.add(next.getIdElement().toUnqualifiedVersionless()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java index 5849c50da2c..87a75d96507 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java @@ -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)); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java index 3cb1c79718c..44a7eab19c7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java @@ -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)); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java index 9b50b569762..f09ecfca9af 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java @@ -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()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java index 36e0c16279f..b8c14632961 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java @@ -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))); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java index c186adee868..f784ec2f1b3 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java @@ -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 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 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()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java index fd5d1938250..d3c227983f9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java @@ -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()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java index 853de6ee8a8..d25b444430f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java @@ -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))); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index 9b96d5e2878..9184f3f12bf 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -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 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 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()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java index 9c5648fb41e..eff8d266963 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoHashesTest.java @@ -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 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 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()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java index f7330219930..6635ec8b5b2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java @@ -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()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java index 76c30864d4a..c4a34b2d102 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java @@ -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")); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java index 986931fffda..5c000b1e89a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java @@ -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")); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index 5298e159403..b4a4cb2e71a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -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")); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/PagingMultinodeProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/PagingMultinodeProviderDstu3Test.java index 968bb5b0193..0ff5cd6e84d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/PagingMultinodeProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/PagingMultinodeProviderDstu3Test.java @@ -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")); + } + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java index 9c3665dd9d1..abff0f4d114 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java @@ -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 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 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); diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java index b9894d2e6ff..8d32f71942c 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java @@ -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()); diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java index b099d4dc6c5..ffeba9ceaaa 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java @@ -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 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> theParams) { + public void put(String theName, List> theParams) { mySearchParameterMap.put(theName, theParams); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IRestfulServerDefaults.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IRestfulServerDefaults.java index 7ab2330d080..952bb2be6cc 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IRestfulServerDefaults.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IRestfulServerDefaults.java @@ -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 Accept header in the request, or a _pretty diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index f80f77bed8b..1ca9bd36573 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -140,6 +140,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer myResourceNameToBinding = new HashMap<>(); private IServerAddressStrategy myServerAddressStrategy = new IncomingRequestAddressStrategy(); @@ -674,6 +677,30 @@ public class RestfulServer extends HttpServlet implements IRestfulServernull 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 null 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 * diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java index b7ff53a321b..0d8370d0cdd 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java @@ -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 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 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 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 theIncludes, RequestDetails theRequestDetails, String theSearchId, int theOffset, int theCount, Map 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 extractLastUpdatedFromResource(IBaseResource theResource) { IPrimitiveType lastUpdated = null; if (theResource instanceof IResource) { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java index 042040eb7e5..6ab3a349711 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java @@ -117,13 +117,23 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi IBaseResource createBundleFromBundleProvider(IRestfulServer theServer, RequestDetails theRequest, Integer theLimit, String theLinkSelf, Set 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 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 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; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java index f680e2bf8f4..82490c2b176 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java @@ -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) { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OffsetParameter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OffsetParameter.java new file mode 100644 index 00000000000..77f928749e0 --- /dev/null +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OffsetParameter.java @@ -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> theOuterCollectionType, Class> 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; + } + +} diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm index 737dfb1fb04..44323929224 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_resource_provider.vm @@ -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);