From 6035f78ddf765b6a759b9d538b0a8e48f4a918c4 Mon Sep 17 00:00:00 2001
From: Diederik Muylwyk
Date: Fri, 5 Mar 2021 18:46:42 -0500
Subject: [PATCH 1/6] Fix typos
---
...nce-targets-need-to-be-resolved-placeholder-extension.yaml | 2 +-
.../src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2446-issues-with-placeholder-reference-targets-need-to-be-resolved-placeholder-extension.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2446-issues-with-placeholder-reference-targets-need-to-be-resolved-placeholder-extension.yaml
index 849ebfd76a1..7b305d5f9a6 100644
--- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2446-issues-with-placeholder-reference-targets-need-to-be-resolved-placeholder-extension.yaml
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2446-issues-with-placeholder-reference-targets-need-to-be-resolved-placeholder-extension.yaml
@@ -2,4 +2,4 @@
type: add
issue: 2446
title: "Auto-created placeholder reference targets now have an extension with the URL
- `http://hapifhir.io/fhir/StructureDefinition/resource-meta-placeholder` and a value of `true`."
+ `http://hapifhir.io/fhir/StructureDefinition/resource-placeholder` and a value of `true`."
diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java
index def5d13846f..97847d2ac44 100644
--- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java
+++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java
@@ -1070,7 +1070,7 @@ public class DaoConfig {
* Note however that references containing purely numeric IDs will not be auto-created
* as they are never allowed to be client supplied in HAPI FHIR JPA.
*
- * All placeholder resources created in this way have a meta extension
+ * All placeholder resources created in this way have an extension
* with the URL {@link HapiExtensions#EXT_RESOURCE_PLACEHOLDER} and the value "true".
*
*/
@@ -1094,7 +1094,7 @@ public class DaoConfig {
* Note however that references containing purely numeric IDs will not be auto-created
* as they are never allowed to be client supplied in HAPI FHIR JPA.
*
- * All placeholder resources created in this way have a meta extension
+ * All placeholder resources created in this way have an extension
* with the URL {@link HapiExtensions#EXT_RESOURCE_PLACEHOLDER} and the value "true".
*
*/
From 11ee6dedd3378a041067a8ecf5922ea522abeb91 Mon Sep 17 00:00:00 2001
From: Diederik Muylwyk
Date: Sat, 6 Mar 2021 16:33:05 -0500
Subject: [PATCH 2/6] Address CVE-2020-27223; bumped minor version of Jetty in
root POM
---
.../changelog/5_4_0/2454-address-cve-2020-27223.yaml | 9 +++++++++
pom.xml | 2 +-
2 files changed, 10 insertions(+), 1 deletion(-)
create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2454-address-cve-2020-27223.yaml
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2454-address-cve-2020-27223.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2454-address-cve-2020-27223.yaml
new file mode 100644
index 00000000000..a67999c435f
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2454-address-cve-2020-27223.yaml
@@ -0,0 +1,9 @@
+---
+type: security
+issue: 2454
+title: "Addressed the following CVE report by bumping the minor version for Jetty in the root POM:
+
+
+ [CVE-2020-27223](https://nvd.nist.gov/vuln/detail/CVE-2020-27223)
+
+ "
diff --git a/pom.xml b/pom.xml
index e65670c96e1..8672a79a738 100644
--- a/pom.xml
+++ b/pom.xml
@@ -782,7 +782,7 @@
3.16.0
3.0.0
- 9.4.35.v20201120
+ 9.4.38.v20210224
3.0.2
5.7.0
6.5.4
From 62df320cebe9992f07f9106ed5f6bd563d69bd1c Mon Sep 17 00:00:00 2001
From: James Agnew
Date: Sun, 7 Mar 2021 10:16:30 -0500
Subject: [PATCH 3/6] Avoid a crash reading an unknown ID on partitioned server
(#2451)
* Avoid a crash reading an unknown ID on partitioned server. Fix #1953.
* Attempt 2 at fixing #1953
---
...rming-partitioned-read-for-unknown-id.yaml | 5 ++
.../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 2 +-
.../jpa/dao/r4/PartitioningSqlR4Test.java | 52 +++++++++++++++++++
3 files changed, 58 insertions(+), 1 deletion(-)
create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/1953-avoid-npe-performing-partitioned-read-for-unknown-id.yaml
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/1953-avoid-npe-performing-partitioned-read-for-unknown-id.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/1953-avoid-npe-performing-partitioned-read-for-unknown-id.yaml
new file mode 100644
index 00000000000..14413564566
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/1953-avoid-npe-performing-partitioned-read-for-unknown-id.yaml
@@ -0,0 +1,5 @@
+---
+type: fix
+issue: 1953
+title: "A crash was fixed when performing a FHIR read on a partitioned server, where the requested ID is not known. Thanks
+ to Umberto Cappellini for reporting!"
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 e140bef77d6..19dae05746c 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
@@ -1149,7 +1149,7 @@ public abstract class BaseHapiFhirResourceDao extends B
BaseHasResource entity = myEntityManager.find(ResourceTable.class, pid.getIdAsLong());
// Verify that the resource is for the correct partition
- if (!requestPartitionId.isAllPartitions()) {
+ if (entity != null && !requestPartitionId.isAllPartitions()) {
if (entity.getPartitionId() != null && entity.getPartitionId().getPartitionId() != null) {
if (!requestPartitionId.hasPartitionId(entity.getPartitionId().getPartitionId())) {
ourLog.debug("Performing a read for PartitionId={} but entity has partition: {}", requestPartitionId, entity.getPartitionId());
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java
index 50390a69aa3..66d5a610083 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java
@@ -975,6 +975,58 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
}
}
+
+ @Test
+ public void testRead_PidId_UnknownResourceId() {
+ // Read in specific Partition
+ {
+ addReadPartition(1);
+ try {
+ myPatientDao.read(new IdType("Patient/1"), mySrd);
+ fail();
+ } catch (ResourceNotFoundException e) {
+ // expected
+ }
+ }
+
+ // Read in null Partition
+ {
+ addReadDefaultPartition();
+ try {
+ myPatientDao.read(new IdType("Patient/1"), mySrd);
+ fail();
+ } catch (ResourceNotFoundException e) {
+ // expected
+ }
+ }
+ }
+
+ @Test
+ public void testRead_PidId_ResourceIdOnlyExistsInDifferentPartition() {
+ IIdType id = createPatient(withPartition(2), withActiveTrue());
+ // Read in specific Partition
+ {
+ addReadPartition(1);
+ try {
+ myPatientDao.read(id, mySrd);
+ fail();
+ } catch (ResourceNotFoundException e) {
+ // expected
+ }
+ }
+
+ // Read in null Partition
+ {
+ addReadDefaultPartition();
+ try {
+ myPatientDao.read(id, mySrd);
+ fail();
+ } catch (ResourceNotFoundException e) {
+ // expected
+ }
+ }
+ }
+
@Test
public void testRead_ForcedId_SpecificPartition() {
IIdType patientIdNull = createPatient(withPutPartition(null), withActiveTrue(), withId("NULL"));
From 6cc8a5ce4dce108b238bc85baa2f22addbb8238a Mon Sep 17 00:00:00 2001
From: Diederik Muylwyk
Date: Tue, 9 Mar 2021 06:28:40 -0500
Subject: [PATCH 4/6] Fix HasParam#doGetQueryParameterQualifier() (#2460)
* Fix HasParam#doGetQueryParameterQualifier(); add changelog entry
* Fix bad test in SearchParameterMapTest
---
.../main/java/ca/uhn/fhir/rest/param/HasParam.java | 12 ++++++------
...arameterqualifier-returns-malformed-modifier.yaml | 6 ++++++
.../uhn/fhir/jpa/dao/r4/SearchParameterMapTest.java | 3 +--
.../uhn/fhir/jpa/searchparam/SearchParameterMap.java | 4 ----
4 files changed, 13 insertions(+), 12 deletions(-)
create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2458-hasparam-dogetqueryparameterqualifier-returns-malformed-modifier.yaml
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/HasParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/HasParam.java
index f85cb4e1ebf..3234103f141 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/HasParam.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/HasParam.java
@@ -1,5 +1,10 @@
package ca.uhn.fhir.rest.param;
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.model.api.IQueryParameterType;
+import ca.uhn.fhir.rest.api.Constants;
+import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
+
import static org.apache.commons.lang3.StringUtils.defaultString;
/*
@@ -22,11 +27,6 @@ import static org.apache.commons.lang3.StringUtils.defaultString;
* #L%
*/
-import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.model.api.IQueryParameterType;
-import ca.uhn.fhir.rest.api.Constants;
-import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
-
/**
* Implementation of the _has method parameter
*/
@@ -55,7 +55,7 @@ public class HasParam extends BaseParam implements IQueryParameterType {
@Override
String doGetQueryParameterQualifier() {
- return myTargetResourceType + ':' + myParameterName + ':' + myParameterValue;
+ return ':' + myTargetResourceType + ':' + myReferenceFieldName + ':' + myParameterName;
}
@Override
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2458-hasparam-dogetqueryparameterqualifier-returns-malformed-modifier.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2458-hasparam-dogetqueryparameterqualifier-returns-malformed-modifier.yaml
new file mode 100644
index 00000000000..3512633c2e5
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2458-hasparam-dogetqueryparameterqualifier-returns-malformed-modifier.yaml
@@ -0,0 +1,6 @@
+---
+type: fix
+issue: 2458
+title: "`HasParam#doGetQueryParameterQualifier()` returned a malformed modifier. For example, the modifier for
+ `_has:Observation:patient:code=123` was returned as `Observation:code:123` when it should be
+ `:Observation:patient:code`. This has been corrected."
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchParameterMapTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchParameterMapTest.java
index af4c2e9bfd6..fcb37e54fe9 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchParameterMapTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchParameterMapTest.java
@@ -3,7 +3,6 @@ package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
-import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.param.HasParam;
import ca.uhn.fhir.test.BaseTest;
import org.junit.jupiter.api.Test;
@@ -19,6 +18,6 @@ public class SearchParameterMapTest extends BaseTest {
SearchParameterMap params = new SearchParameterMap();
params.add("_has", new HasParam("Observation", "subject", "identifier", "urn:system|FOO"));
String criteria = params.toNormalizedQueryString(myContext);
- assertEquals(criteria, "?_has:Observation:identifier:urn:system|FOO=urn%3Asystem%7CFOO");
+ assertEquals(criteria, "?_has:Observation:subject:identifier=urn%3Asystem%7CFOO");
}
}
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 6a2dbbc3ce0..4e6a31a8063 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
@@ -413,10 +413,6 @@ public class SearchParameterMap implements Serializable {
IQueryParameterType firstValue = nextValuesAnd.get(0);
b.append(UrlUtil.escapeUrlParam(nextKey));
- if (nextKey.equals(Constants.PARAM_HAS)) {
- b.append(':');
- }
-
if (firstValue.getMissing() != null) {
b.append(Constants.PARAMQUALIFIER_MISSING);
b.append('=');
From bf8e890a0c15d0e03e76a8a7a2c82f5f04a7e287 Mon Sep 17 00:00:00 2001
From: Frank Tao <38163583+frankjtao@users.noreply.github.com>
Date: Tue, 9 Mar 2021 06:50:23 -0500
Subject: [PATCH 5/6] Supported contained resource search (#2441)
* POC for indexing on contained resource - test case may failed
* Test contained url
* Add a spt to handle contained flag
* Added search option for contained resource
* Impl contained resource search
* fixed typo
* Reworked on creating index based on the review comments
* Added changelog
* Added more test cases
Co-authored-by: jamesagnew
---
.../5_4_0/2403-support-contained-search.yaml | 4 +
.../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 13 +
.../fhir/jpa/search/builder/QueryStack.java | 297 ++++--
.../jpa/search/builder/SearchBuilder.java | 6 +-
.../ResourceLinkPredicateBuilder.java | 3 +-
.../predicate/StringPredicateBuilder.java | 5 +-
.../predicate/TokenPredicateBuilder.java | 13 +-
.../r4/FhirResourceDaoR4ContainedTest.java | 325 +++++-
.../dao/r4/SearchParamExtractorR4Test.java | 6 +-
.../r4/ResourceProviderHasParamR4Test.java | 4 +-
...ResourceProviderR4SearchContainedTest.java | 978 ++++++++++++++++++
.../fhir/jpa/model/entity/ModelConfig.java | 23 +
.../jpa/searchparam/SearchContainedEnum.java | 40 +
.../jpa/searchparam/SearchParameterMap.java | 12 +-
.../extractor/BaseSearchParamExtractor.java | 79 +-
.../extractor/ISearchParamExtractor.java | 2 +-
.../ResourceIndexedSearchParams.java | 25 +-
.../SearchParamExtractorService.java | 87 +-
18 files changed, 1749 insertions(+), 173 deletions(-)
create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2403-support-contained-search.yaml
create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4SearchContainedTest.java
create mode 100644 hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchContainedEnum.java
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2403-support-contained-search.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2403-support-contained-search.yaml
new file mode 100644
index 00000000000..a12b091e98f
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/2403-support-contained-search.yaml
@@ -0,0 +1,4 @@
+---
+type: add
+issue: 2403
+title: "Optionally support '_contained' resource search by enabling the indexing on the contained resources in the ModelConfig."
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 19dae05746c..f4288f05d8c 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
@@ -54,6 +54,7 @@ import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
import ca.uhn.fhir.jpa.search.cache.SearchCacheStatusEnum;
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
+import ca.uhn.fhir.jpa.searchparam.SearchContainedEnum;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.model.api.IQueryParameterType;
@@ -1291,6 +1292,18 @@ public abstract class BaseHapiFhirResourceDao extends B
@Override
public IBundleProvider search(final SearchParameterMap theParams, RequestDetails theRequest, HttpServletResponse theServletResponse) {
+ if (theRequest != null) {
+ String[] contained = theRequest.getParameters().get(Constants.PARAM_CONTAINED);
+ if (contained != null && contained.length > 0) {
+ if (contained[0].equals("true")) {
+ theParams.setSearchContainedMode(SearchContainedEnum.TRUE);
+ ourLog.info("Search on contained resources only");
+ } else if (contained[0].equals("both")) {
+ ourLog.warn("Search on both normal resources and contained resources are not support. set to default search on normal resources");
+ }
+ }
+ }
+
if (myDaoConfig.getIndexMissingFields() == DaoConfig.IndexEnabledEnum.DISABLED) {
for (List> nextAnds : theParams.values()) {
for (List extends IQueryParameterType> nextOrs : nextAnds) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java
index a84d44bf5d4..a63f8b38c40 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java
@@ -52,6 +52,7 @@ import ca.uhn.fhir.jpa.search.builder.predicate.TokenPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.UriPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
+import ca.uhn.fhir.jpa.searchparam.SearchContainedEnum;
import ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.searchparam.util.SourceParam;
@@ -109,6 +110,7 @@ import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -286,7 +288,7 @@ public class QueryStack {
return new PredicateBuilderCacheLookupResult<>(cacheHit, (T) retVal);
}
- private Condition createPredicateComposite(@Nullable DbColumn theSourceJoinColumn, String theResourceName, RuntimeSearchParam theParamDef, List extends IQueryParameterType> theNextAnd, RequestPartitionId theRequestPartitionId) {
+ private Condition createPredicateComposite(@Nullable DbColumn theSourceJoinColumn, String theResourceName, String theSpnamePrefix, RuntimeSearchParam theParamDef, List extends IQueryParameterType> theNextAnd, RequestPartitionId theRequestPartitionId) {
Condition orCondidtion = null;
for (IQueryParameterType next : theNextAnd) {
@@ -298,11 +300,11 @@ public class QueryStack {
RuntimeSearchParam left = theParamDef.getCompositeOf().get(0);
IQueryParameterType leftValue = cp.getLeftValue();
- Condition leftPredicate = createPredicateCompositePart(theSourceJoinColumn, theResourceName, left, leftValue, theRequestPartitionId);
+ Condition leftPredicate = createPredicateCompositePart(theSourceJoinColumn, theResourceName, theSpnamePrefix, left, leftValue, theRequestPartitionId);
RuntimeSearchParam right = theParamDef.getCompositeOf().get(1);
IQueryParameterType rightValue = cp.getRightValue();
- Condition rightPredicate = createPredicateCompositePart(theSourceJoinColumn, theResourceName, right, rightValue, theRequestPartitionId);
+ Condition rightPredicate = createPredicateCompositePart(theSourceJoinColumn, theResourceName, theSpnamePrefix, right, rightValue, theRequestPartitionId);
Condition andCondition = toAndPredicate(leftPredicate, rightPredicate);
@@ -316,20 +318,20 @@ public class QueryStack {
return orCondidtion;
}
- private Condition createPredicateCompositePart(@Nullable DbColumn theSourceJoinColumn, String theResourceName, RuntimeSearchParam theParam, IQueryParameterType theParamValue, RequestPartitionId theRequestPartitionId) {
+ private Condition createPredicateCompositePart(@Nullable DbColumn theSourceJoinColumn, String theResourceName, String theSpnamePrefix, RuntimeSearchParam theParam, IQueryParameterType theParamValue, RequestPartitionId theRequestPartitionId) {
switch (theParam.getParamType()) {
case STRING: {
- return createPredicateString(theSourceJoinColumn, theResourceName, theParam, Collections.singletonList(theParamValue), null, theRequestPartitionId);
+ return createPredicateString(theSourceJoinColumn, theResourceName, theSpnamePrefix, theParam, Collections.singletonList(theParamValue), null, theRequestPartitionId);
}
case TOKEN: {
- return createPredicateToken(theSourceJoinColumn, theResourceName, theParam, Collections.singletonList(theParamValue), null, theRequestPartitionId);
+ return createPredicateToken(theSourceJoinColumn, theResourceName, theSpnamePrefix, theParam, Collections.singletonList(theParamValue), null, theRequestPartitionId);
}
case DATE: {
- return createPredicateDate(theSourceJoinColumn, theResourceName, theParam, Collections.singletonList(theParamValue), toOperation(((DateParam) theParamValue).getPrefix()), theRequestPartitionId);
+ return createPredicateDate(theSourceJoinColumn, theResourceName, theSpnamePrefix, theParam, Collections.singletonList(theParamValue), toOperation(((DateParam) theParamValue).getPrefix()), theRequestPartitionId);
}
case QUANTITY: {
- return createPredicateQuantity(theSourceJoinColumn, theResourceName, theParam, Collections.singletonList(theParamValue), null, theRequestPartitionId);
+ return createPredicateQuantity(theSourceJoinColumn, theResourceName, theSpnamePrefix, theParam, Collections.singletonList(theParamValue), null, theRequestPartitionId);
}
}
@@ -357,15 +359,12 @@ public class QueryStack {
return predicateBuilder.combineWithRequestPartitionIdPredicate(theRequestPartitionId, ComboCondition.or(codePredicates.toArray(new Condition[0])));
}
- public Condition createPredicateDate(@Nullable DbColumn theSourceJoinColumn,
- String theResourceName,
- RuntimeSearchParam theSearchParam,
- List extends IQueryParameterType> theList,
- SearchFilterParser.CompareOperation theOperation,
- RequestPartitionId theRequestPartitionId) {
-
- String paramName = theSearchParam.getName();
+ public Condition createPredicateDate(@Nullable DbColumn theSourceJoinColumn, String theResourceName,
+ String theSpnamePrefix, RuntimeSearchParam theSearchParam, List extends IQueryParameterType> theList,
+ SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
+ String paramName = getParamNameWithPrefix(theSpnamePrefix, theSearchParam.getName());
+
PredicateBuilderCacheLookupResult predicateBuilderLookupResult = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.DATE, theSourceJoinColumn, paramName, () -> mySqlBuilder.addDatePredicateBuilder(theSourceJoinColumn));
DatePredicateBuilder predicateBuilder = predicateBuilderLookupResult.getResult();
boolean cacheHit = predicateBuilderLookupResult.isCacheHit();
@@ -440,13 +439,13 @@ public class QueryStack {
}
RestSearchParameterTypeEnum typeEnum = searchParam.getParamType();
if (typeEnum == RestSearchParameterTypeEnum.URI) {
- return theQueryStack3.createPredicateUri(null, theResourceName, searchParam, Collections.singletonList(new UriParam(theFilter.getValue())), theFilter.getOperation(), theRequest, theRequestPartitionId);
+ return theQueryStack3.createPredicateUri(null, theResourceName, null, searchParam, Collections.singletonList(new UriParam(theFilter.getValue())), theFilter.getOperation(), theRequest, theRequestPartitionId);
} else if (typeEnum == RestSearchParameterTypeEnum.STRING) {
- return theQueryStack3.createPredicateString(null, theResourceName, searchParam, Collections.singletonList(new StringParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
+ return theQueryStack3.createPredicateString(null, theResourceName, null, searchParam, Collections.singletonList(new StringParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
} else if (typeEnum == RestSearchParameterTypeEnum.DATE) {
- return theQueryStack3.createPredicateDate(null, theResourceName, searchParam, Collections.singletonList(new DateParam(fromOperation(theFilter.getOperation()), theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
+ return theQueryStack3.createPredicateDate(null, theResourceName, null, searchParam, Collections.singletonList(new DateParam(fromOperation(theFilter.getOperation()), theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
} else if (typeEnum == RestSearchParameterTypeEnum.NUMBER) {
- return theQueryStack3.createPredicateNumber(null, theResourceName, searchParam, Collections.singletonList(new NumberParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
+ return theQueryStack3.createPredicateNumber(null, theResourceName, null, searchParam, Collections.singletonList(new NumberParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
} else if (typeEnum == RestSearchParameterTypeEnum.REFERENCE) {
SearchFilterParser.CompareOperation operation = theFilter.getOperation();
String resourceType = null; // The value can either have (Patient/123) or not have (123) a resource type, either way it's not needed here
@@ -455,7 +454,7 @@ public class QueryStack {
ReferenceParam referenceParam = new ReferenceParam(resourceType, chain, value);
return theQueryStack3.createPredicateReference(null, theResourceName, paramName, Collections.singletonList(referenceParam), operation, theRequest, theRequestPartitionId);
} else if (typeEnum == RestSearchParameterTypeEnum.QUANTITY) {
- return theQueryStack3.createPredicateQuantity(null, theResourceName, searchParam, Collections.singletonList(new QuantityParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
+ return theQueryStack3.createPredicateQuantity(null, theResourceName, null, searchParam, Collections.singletonList(new QuantityParam(theFilter.getValue())), theFilter.getOperation(), theRequestPartitionId);
} else if (typeEnum == RestSearchParameterTypeEnum.COMPOSITE) {
throw new InvalidRequestException("Composite search parameters not currently supported with _filter clauses");
} else if (typeEnum == RestSearchParameterTypeEnum.TOKEN) {
@@ -464,7 +463,7 @@ public class QueryStack {
null,
null,
theFilter.getValue());
- return theQueryStack3.createPredicateToken(null, theResourceName, searchParam, Collections.singletonList(param), theFilter.getOperation(), theRequestPartitionId);
+ return theQueryStack3.createPredicateToken(null, theResourceName, null, searchParam, Collections.singletonList(param), theFilter.getOperation(), theRequestPartitionId);
}
}
return null;
@@ -562,7 +561,7 @@ public class QueryStack {
List paths = join.createResourceLinkPaths(targetResourceType, paramReference);
Condition typePredicate = BinaryCondition.equalTo(join.getColumnTargetResourceType(), mySqlBuilder.generatePlaceholder(theResourceType));
Condition pathPredicate = toEqualToOrInPredicate(join.getColumnSourcePath(), mySqlBuilder.generatePlaceholders(paths));
- Condition linkedPredicate = searchForIdsWithAndOr(join.getColumnSrcResourceId(), targetResourceType, parameterName, Collections.singletonList(orValues), theRequest, theRequestPartitionId);
+ Condition linkedPredicate = searchForIdsWithAndOr(join.getColumnSrcResourceId(), targetResourceType, parameterName, Collections.singletonList(orValues), theRequest, theRequestPartitionId, SearchContainedEnum.FALSE);
andPredicates.add(toAndPredicate(partitionPredicate, pathPredicate, typePredicate, linkedPredicate));
}
@@ -607,17 +606,16 @@ public class QueryStack {
return toAndPredicate(predicates);
}
- public Condition createPredicateNumber(@Nullable DbColumn theSourceJoinColumn,
- String theResourceName,
- RuntimeSearchParam theSearchParam,
- List extends IQueryParameterType> theList,
- SearchFilterParser.CompareOperation theOperation,
- RequestPartitionId theRequestPartitionId) {
+ public Condition createPredicateNumber(@Nullable DbColumn theSourceJoinColumn, String theResourceName,
+ String theSpnamePrefix, RuntimeSearchParam theSearchParam, List extends IQueryParameterType> theList,
+ SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
- NumberPredicateBuilder join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.NUMBER, theSourceJoinColumn, theSearchParam.getName(), () -> mySqlBuilder.addNumberPredicateBuilder(theSourceJoinColumn)).getResult();
+ String paramName = getParamNameWithPrefix(theSpnamePrefix, theSearchParam.getName());
+
+ NumberPredicateBuilder join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.NUMBER, theSourceJoinColumn, paramName, () -> mySqlBuilder.addNumberPredicateBuilder(theSourceJoinColumn)).getResult();
if (theList.get(0).getMissing() != null) {
- return join.createPredicateParamMissingForNonReference(theResourceName, theSearchParam.getName(), theList.get(0).getMissing(), theRequestPartitionId);
+ return join.createPredicateParamMissingForNonReference(theResourceName, paramName, theList.get(0).getMissing(), theRequestPartitionId);
}
List codePredicates = new ArrayList<>();
@@ -636,7 +634,8 @@ public class QueryStack {
operation = toOperation(param.getPrefix());
}
- Condition predicate = join.createPredicateNumeric(theResourceName, theSearchParam.getName(), operation, value, theRequestPartitionId, nextOr);
+
+ Condition predicate = join.createPredicateNumeric(theResourceName, paramName, operation, value, theRequestPartitionId, nextOr);
codePredicates.add(predicate);
} else {
@@ -648,16 +647,15 @@ public class QueryStack {
return join.combineWithRequestPartitionIdPredicate(theRequestPartitionId, ComboCondition.or(codePredicates.toArray(new Condition[0])));
}
- public Condition createPredicateQuantity(@Nullable DbColumn theSourceJoinColumn,
- String theResourceName,
- RuntimeSearchParam theSearchParam,
- List extends IQueryParameterType> theList,
- SearchFilterParser.CompareOperation theOperation,
- RequestPartitionId theRequestPartitionId) {
+ public Condition createPredicateQuantity(@Nullable DbColumn theSourceJoinColumn, String theResourceName,
+ String theSpnamePrefix, RuntimeSearchParam theSearchParam, List extends IQueryParameterType> theList,
+ SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
+
+ String paramName = getParamNameWithPrefix(theSpnamePrefix, theSearchParam.getName());
if (theList.get(0).getMissing() != null) {
QuantityBasePredicateBuilder join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.QUANTITY, theSourceJoinColumn, theSearchParam.getName(), () -> mySqlBuilder.addQuantityPredicateBuilder(theSourceJoinColumn)).getResult();
- return join.createPredicateParamMissingForNonReference(theResourceName, theSearchParam.getName(), theList.get(0).getMissing(), theRequestPartitionId);
+ return join.createPredicateParamMissingForNonReference(theResourceName, paramName, theList.get(0).getMissing(), theRequestPartitionId);
}
List quantityParams = theList
@@ -675,18 +673,18 @@ public class QueryStack {
.collect(Collectors.toList());
if (normalizedQuantityParams.size() == quantityParams.size()) {
- join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.QUANTITY, theSourceJoinColumn, theSearchParam.getName(), () -> mySqlBuilder.addQuantityNormalizedPredicateBuilder(theSourceJoinColumn)).getResult();
+ join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.QUANTITY, theSourceJoinColumn, paramName, () -> mySqlBuilder.addQuantityNormalizedPredicateBuilder(theSourceJoinColumn)).getResult();
quantityParams = normalizedQuantityParams;
}
}
if (join == null) {
- join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.QUANTITY, theSourceJoinColumn, theSearchParam.getName(), () -> mySqlBuilder.addQuantityPredicateBuilder(theSourceJoinColumn)).getResult();
+ join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.QUANTITY, theSourceJoinColumn, paramName, () -> mySqlBuilder.addQuantityPredicateBuilder(theSourceJoinColumn)).getResult();
}
List codePredicates = new ArrayList<>();
for (QuantityParam nextOr : quantityParams) {
- Condition singleCode = join.createPredicateQuantity(nextOr, theResourceName, theSearchParam.getName(), null, join, theOperation, theRequestPartitionId);
+ Condition singleCode = join.createPredicateQuantity(nextOr, theResourceName, paramName, null, join, theOperation, theRequestPartitionId);
codePredicates.add(singleCode);
}
@@ -720,6 +718,106 @@ public class QueryStack {
return predicateBuilder.createPredicate(theRequest, theResourceName, theParamName, theList, theOperation, theRequestPartitionId);
}
+ private Condition createPredicateReferenceForContainedResource(@Nullable DbColumn theSourceJoinColumn,
+ String theResourceName, String theParamName, RuntimeSearchParam theSearchParam,
+ List extends IQueryParameterType> theList, SearchFilterParser.CompareOperation theOperation,
+ RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
+
+ String spnamePrefix = theParamName;
+
+ String targetChain = null;
+ String targetParamName = null;
+ String targetQualifier = null;
+ String targetValue = null;
+
+ RuntimeSearchParam targetParamDefinition = null;
+
+ ArrayList orValues = Lists.newArrayList();
+ IQueryParameterType qp = null;
+
+ for (int orIdx = 0; orIdx < theList.size(); orIdx++) {
+
+ IQueryParameterType nextOr = theList.get(orIdx);
+
+ if (nextOr instanceof ReferenceParam) {
+
+ ReferenceParam referenceParam = (ReferenceParam) nextOr;
+
+ // 1. Find out the parameter, qualifier and the value
+ targetChain = referenceParam.getChain();
+ targetParamName = targetChain;
+ targetValue = nextOr.getValueAsQueryToken(myFhirContext);
+
+ int qualifierIndex = targetChain.indexOf(':');
+ if (qualifierIndex != -1) {
+ targetParamName = targetChain.substring(0, qualifierIndex);
+ targetQualifier = targetChain.substring(qualifierIndex);
+ }
+
+ // 2. find out the data type
+ if (targetParamDefinition == null) {
+ Iterator it = theSearchParam.getTargets().iterator();
+ while (it.hasNext()) {
+ targetParamDefinition = mySearchParamRegistry.getActiveSearchParam(it.next(), targetParamName);
+ if (targetParamDefinition != null)
+ break;
+ }
+ }
+
+ if (targetParamDefinition == null) {
+ throw new InvalidRequestException("Unknown search parameter name: " + theSearchParam.getName() + '.' + targetParamName + ".");
+ }
+
+ qp = toParameterType(targetParamDefinition);
+ qp.setValueAsQueryToken(myFhirContext, targetParamName, targetQualifier, targetValue);
+ orValues.add(qp);
+ }
+ }
+
+ if (targetParamDefinition == null) {
+ throw new InvalidRequestException("Unknown search parameter name: " + theSearchParam.getName() + ".");
+ }
+
+ // 3. create the query
+ Condition containedCondition = null;
+
+ switch (targetParamDefinition.getParamType()) {
+ case DATE:
+ containedCondition = createPredicateDate(null, theResourceName, spnamePrefix, targetParamDefinition,
+ orValues, theOperation, theRequestPartitionId);
+ break;
+ case NUMBER:
+ containedCondition = createPredicateNumber(null, theResourceName, spnamePrefix, targetParamDefinition,
+ orValues, theOperation, theRequestPartitionId);
+ break;
+ case QUANTITY:
+ containedCondition = createPredicateQuantity(null, theResourceName, spnamePrefix, targetParamDefinition,
+ orValues, theOperation, theRequestPartitionId);
+ break;
+ case STRING:
+ containedCondition = createPredicateString(null, theResourceName, spnamePrefix, targetParamDefinition,
+ orValues, theOperation, theRequestPartitionId);
+ break;
+ case TOKEN:
+ containedCondition = createPredicateToken(null, theResourceName, spnamePrefix, targetParamDefinition,
+ orValues, theOperation, theRequestPartitionId);
+ break;
+ case COMPOSITE:
+ containedCondition = createPredicateComposite(null, theResourceName, spnamePrefix, targetParamDefinition,
+ orValues, theRequestPartitionId);
+ break;
+ case URI:
+ containedCondition = createPredicateUri(null, theResourceName, spnamePrefix, targetParamDefinition,
+ orValues, theOperation, theRequest, theRequestPartitionId);
+ break;
+ default:
+ throw new InvalidRequestException(
+ "The search type:" + targetParamDefinition.getParamType() + " is not supported.");
+ }
+
+ return containedCondition;
+ }
+
@Nullable
public Condition createPredicateResourceId(@Nullable DbColumn theSourceJoinColumn, List> theValues, String theResourceName, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
ResourceIdPredicateBuilder builder = mySqlBuilder.newResourceIdBuilder();
@@ -762,22 +860,21 @@ public class QueryStack {
return toOrPredicate(orPredicates);
}
- public Condition createPredicateString(@Nullable DbColumn theSourceJoinColumn,
- String theResourceName,
- RuntimeSearchParam theSearchParam,
- List extends IQueryParameterType> theList,
- SearchFilterParser.CompareOperation theOperation,
- RequestPartitionId theRequestPartitionId) {
+ public Condition createPredicateString(@Nullable DbColumn theSourceJoinColumn, String theResourceName,
+ String theSpnamePrefix, RuntimeSearchParam theSearchParam, List extends IQueryParameterType> theList,
+ SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
- StringPredicateBuilder join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.STRING, theSourceJoinColumn, theSearchParam.getName(), () -> mySqlBuilder.addStringPredicateBuilder(theSourceJoinColumn)).getResult();
+ String paramName = getParamNameWithPrefix(theSpnamePrefix, theSearchParam.getName());
+
+ StringPredicateBuilder join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.STRING, theSourceJoinColumn, paramName, () -> mySqlBuilder.addStringPredicateBuilder(theSourceJoinColumn)).getResult();
if (theList.get(0).getMissing() != null) {
- return join.createPredicateParamMissingForNonReference(theResourceName, theSearchParam.getName(), theList.get(0).getMissing(), theRequestPartitionId);
+ return join.createPredicateParamMissingForNonReference(theResourceName, paramName, theList.get(0).getMissing(), theRequestPartitionId);
}
List codePredicates = new ArrayList<>();
for (IQueryParameterType nextOr : theList) {
- Condition singleCode = join.createPredicateString(nextOr, theResourceName, theSearchParam, join, theOperation);
+ Condition singleCode = join.createPredicateString(nextOr, theResourceName, theSpnamePrefix, theSearchParam, join, theOperation);
codePredicates.add(singleCode);
}
@@ -873,12 +970,9 @@ public class QueryStack {
return toAndPredicate(andPredicates);
}
- public Condition createPredicateToken(@Nullable DbColumn theSourceJoinColumn,
- String theResourceName,
- RuntimeSearchParam theSearchParam,
- List extends IQueryParameterType> theList,
- SearchFilterParser.CompareOperation theOperation,
- RequestPartitionId theRequestPartitionId) {
+ public Condition createPredicateToken(@Nullable DbColumn theSourceJoinColumn, String theResourceName,
+ String theSpnamePrefix, RuntimeSearchParam theSearchParam, List extends IQueryParameterType> theList,
+ SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
List tokens = new ArrayList<>();
for (IQueryParameterType nextOr : theList) {
@@ -900,7 +994,7 @@ public class QueryStack {
throw new MethodNotAllowedException(msg);
}
- return createPredicateString(theSourceJoinColumn, theResourceName, theSearchParam, theList, null, theRequestPartitionId);
+ return createPredicateString(theSourceJoinColumn, theResourceName, theSpnamePrefix, theSearchParam, theList, null, theRequestPartitionId);
}
tokens.add(nextOr);
@@ -917,26 +1011,26 @@ public class QueryStack {
if (tokens.isEmpty()) {
return null;
}
+
+ String paramName = getParamNameWithPrefix(theSpnamePrefix, theSearchParam.getName());
- TokenPredicateBuilder join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.TOKEN, theSourceJoinColumn, theSearchParam.getName(), () -> mySqlBuilder.addTokenPredicateBuilder(theSourceJoinColumn)).getResult();
+ TokenPredicateBuilder join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.TOKEN, theSourceJoinColumn, paramName, () -> mySqlBuilder.addTokenPredicateBuilder(theSourceJoinColumn)).getResult();
if (theList.get(0).getMissing() != null) {
- return join.createPredicateParamMissingForNonReference(theResourceName, theSearchParam.getName(), theList.get(0).getMissing(), theRequestPartitionId);
+ return join.createPredicateParamMissingForNonReference(theResourceName, paramName, theList.get(0).getMissing(), theRequestPartitionId);
}
- Condition predicate = join.createPredicateToken(tokens, theResourceName, theSearchParam, theOperation, theRequestPartitionId);
+ Condition predicate = join.createPredicateToken(tokens, theResourceName, theSpnamePrefix, theSearchParam, theOperation, theRequestPartitionId);
return join.combineWithRequestPartitionIdPredicate(theRequestPartitionId, predicate);
}
- public Condition createPredicateUri(@Nullable DbColumn theSourceJoinColumn,
- String theResourceName,
- RuntimeSearchParam theSearchParam,
- List extends IQueryParameterType> theList,
- SearchFilterParser.CompareOperation theOperation,
- RequestDetails theRequestDetails,
- RequestPartitionId theRequestPartitionId) {
+ public Condition createPredicateUri(@Nullable DbColumn theSourceJoinColumn, String theResourceName,
+ String theSpnamePrefix, RuntimeSearchParam theSearchParam, List extends IQueryParameterType> theList,
+ SearchFilterParser.CompareOperation theOperation, RequestDetails theRequestDetails,
+ RequestPartitionId theRequestPartitionId) {
- String paramName = theSearchParam.getName();
+ String paramName = getParamNameWithPrefix(theSpnamePrefix, theSearchParam.getName());
+
UriPredicateBuilder join = mySqlBuilder.addUriPredicateBuilder(theSourceJoinColumn);
if (theList.get(0).getMissing() != null) {
@@ -952,7 +1046,7 @@ public class QueryStack {
}
@Nullable
- public Condition searchForIdsWithAndOr(@Nullable DbColumn theSourceJoinColumn, String theResourceName, String theParamName, List> theAndOrParams, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
+ public Condition searchForIdsWithAndOr(@Nullable DbColumn theSourceJoinColumn, String theResourceName, String theParamName, List> theAndOrParams, RequestDetails theRequest, RequestPartitionId theRequestPartitionId, SearchContainedEnum theSearchContainedMode) {
if (theAndOrParams.isEmpty()) {
return null;
@@ -998,7 +1092,7 @@ public class QueryStack {
DateParam param = (DateParam) nextAnd.get(0);
operation = toOperation(param.getPrefix());
}
- andPredicates.add(createPredicateDate(theSourceJoinColumn, theResourceName, nextParamDef, nextAnd, operation, theRequestPartitionId));
+ andPredicates.add(createPredicateDate(theSourceJoinColumn, theResourceName, null, nextParamDef, nextAnd, operation, theRequestPartitionId));
//andPredicates.add(createPredicateDate(theSourceJoinColumn, theResourceName, nextParamDef, nextAnd, null, theRequestPartitionId));
}
break;
@@ -1009,17 +1103,20 @@ public class QueryStack {
QuantityParam param = (QuantityParam) nextAnd.get(0);
operation = toOperation(param.getPrefix());
}
- andPredicates.add(createPredicateQuantity(theSourceJoinColumn, theResourceName, nextParamDef, nextAnd, operation, theRequestPartitionId));
+ andPredicates.add(createPredicateQuantity(theSourceJoinColumn, theResourceName, null, nextParamDef, nextAnd, operation, theRequestPartitionId));
}
break;
case REFERENCE:
for (List extends IQueryParameterType> nextAnd : theAndOrParams) {
- andPredicates.add(createPredicateReference(theSourceJoinColumn, theResourceName, theParamName, nextAnd, null, theRequest, theRequestPartitionId));
+ if (theSearchContainedMode.equals(SearchContainedEnum.TRUE))
+ andPredicates.add(createPredicateReferenceForContainedResource(theSourceJoinColumn, theResourceName, theParamName, nextParamDef, nextAnd, null, theRequest, theRequestPartitionId));
+ else
+ andPredicates.add(createPredicateReference(theSourceJoinColumn, theResourceName, theParamName, nextAnd, null, theRequest, theRequestPartitionId));
}
break;
case STRING:
for (List extends IQueryParameterType> nextAnd : theAndOrParams) {
- andPredicates.add(createPredicateString(theSourceJoinColumn, theResourceName, nextParamDef, nextAnd, SearchFilterParser.CompareOperation.sw, theRequestPartitionId));
+ andPredicates.add(createPredicateString(theSourceJoinColumn, theResourceName, null, nextParamDef, nextAnd, SearchFilterParser.CompareOperation.sw, theRequestPartitionId));
}
break;
case TOKEN:
@@ -1027,23 +1124,23 @@ public class QueryStack {
if ("Location.position".equals(nextParamDef.getPath())) {
andPredicates.add(createPredicateCoords(theSourceJoinColumn, theResourceName, nextParamDef, nextAnd, theRequestPartitionId));
} else {
- andPredicates.add(createPredicateToken(theSourceJoinColumn, theResourceName, nextParamDef, nextAnd, null, theRequestPartitionId));
+ andPredicates.add(createPredicateToken(theSourceJoinColumn, theResourceName, null, nextParamDef, nextAnd, null, theRequestPartitionId));
}
}
break;
case NUMBER:
for (List extends IQueryParameterType> nextAnd : theAndOrParams) {
- andPredicates.add(createPredicateNumber(theSourceJoinColumn, theResourceName, nextParamDef, nextAnd, null, theRequestPartitionId));
+ andPredicates.add(createPredicateNumber(theSourceJoinColumn, theResourceName, null, nextParamDef, nextAnd, null, theRequestPartitionId));
}
break;
case COMPOSITE:
for (List extends IQueryParameterType> nextAnd : theAndOrParams) {
- andPredicates.add(createPredicateComposite(theSourceJoinColumn, theResourceName, nextParamDef, nextAnd, theRequestPartitionId));
+ andPredicates.add(createPredicateComposite(theSourceJoinColumn, theResourceName, null, nextParamDef, nextAnd, theRequestPartitionId));
}
break;
case URI:
for (List extends IQueryParameterType> nextAnd : theAndOrParams) {
- andPredicates.add(createPredicateUri(theSourceJoinColumn, theResourceName, nextParamDef, nextAnd, SearchFilterParser.CompareOperation.eq, theRequest, theRequestPartitionId));
+ andPredicates.add(createPredicateUri(theSourceJoinColumn, theResourceName, null, nextParamDef, nextAnd, SearchFilterParser.CompareOperation.eq, theRequest, theRequestPartitionId));
}
break;
case HAS:
@@ -1246,4 +1343,48 @@ public class QueryStack {
return parameter.substring(parameter.indexOf(".") + 1);
}
+ private IQueryParameterType toParameterType(RuntimeSearchParam theParam) {
+
+ IQueryParameterType qp;
+ switch (theParam.getParamType()) {
+ case DATE:
+ qp = new DateParam();
+ break;
+ case NUMBER:
+ qp = new NumberParam();
+ break;
+ case QUANTITY:
+ qp = new QuantityParam();
+ break;
+ case STRING:
+ qp = new StringParam();
+ break;
+ case TOKEN:
+ qp = new TokenParam();
+ break;
+ case COMPOSITE:
+ List compositeOf = theParam.getCompositeOf();
+ if (compositeOf.size() != 2) {
+ throw new InternalErrorException("Parameter " + theParam.getName() + " has " + compositeOf.size() + " composite parts. Don't know how handlt this.");
+ }
+ IQueryParameterType leftParam = toParameterType(compositeOf.get(0));
+ IQueryParameterType rightParam = toParameterType(compositeOf.get(1));
+ qp = new CompositeParam<>(leftParam, rightParam);
+ break;
+ case URI:
+ qp = new UriParam();
+ break;
+ default:
+ throw new InvalidRequestException("The search type: " + theParam.getParamType() + " is not supported.");
+ }
+ return qp;
+ }
+
+ public static String getParamNameWithPrefix(String theSpnamePrefix, String theParamName) {
+
+ if (isBlank(theSpnamePrefix))
+ return theParamName;
+
+ return theSpnamePrefix + "." + theParamName;
+ }
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java
index 4f3be1fecd0..0d5a086bea8 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java
@@ -60,6 +60,7 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.searchparam.util.Dstu3DistanceHelper;
import ca.uhn.fhir.jpa.searchparam.util.LastNParameterHelper;
+import ca.uhn.fhir.jpa.searchparam.SearchContainedEnum;
import ca.uhn.fhir.jpa.util.BaseIterator;
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
@@ -91,7 +92,6 @@ import com.healthmarketscience.sqlbuilder.Condition;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.hl7.fhir.r4.model.IdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -216,6 +216,8 @@ public class SearchBuilder implements ISearchBuilder {
attemptCompositeUniqueSpProcessing(theQueryStack, theParams, theRequest);
}
+ SearchContainedEnum searchContainedMode = theParams.getSearchContainedMode();
+
// Handle each parameter
List paramNames = new ArrayList<>(myParams.keySet());
for (String nextParamName : paramNames) {
@@ -224,7 +226,7 @@ public class SearchBuilder implements ISearchBuilder {
continue;
}
List> andOrParams = myParams.get(nextParamName);
- Condition predicate = theQueryStack.searchForIdsWithAndOr(null, myResourceName, nextParamName, andOrParams, theRequest, myRequestPartitionId);
+ Condition predicate = theQueryStack.searchForIdsWithAndOr(null, myResourceName, nextParamName, andOrParams, theRequest, myRequestPartitionId, searchContainedMode);
if (predicate != null) {
theSearchSqlBuilder.addPredicate(predicate);
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java
index 7be7feb2077..c64e43ae503 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java
@@ -43,6 +43,7 @@ import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
+import ca.uhn.fhir.jpa.searchparam.SearchContainedEnum;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.model.api.IQueryParameterType;
@@ -394,7 +395,7 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder {
List andPredicates = new ArrayList<>();
List> chainParamValues = Collections.singletonList(orValues);
- andPredicates.add(childQueryFactory.searchForIdsWithAndOr(myColumnTargetResourceId, subResourceName, chain, chainParamValues, theRequest, theRequestPartitionId));
+ andPredicates.add(childQueryFactory.searchForIdsWithAndOr(myColumnTargetResourceId, subResourceName, chain, chainParamValues, theRequest, theRequestPartitionId, SearchContainedEnum.FALSE));
orPredicates.add(toAndPredicate(andPredicates));
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/StringPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/StringPredicateBuilder.java
index 7a00148a86d..88fd7cdae18 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/StringPredicateBuilder.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/StringPredicateBuilder.java
@@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
+import ca.uhn.fhir.jpa.search.builder.QueryStack;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.param.StringParam;
@@ -75,11 +76,13 @@ public class StringPredicateBuilder extends BaseSearchParamPredicateBuilder {
public Condition createPredicateString(IQueryParameterType theParameter,
String theResourceName,
+ String theSpnamePrefix,
RuntimeSearchParam theSearchParam,
StringPredicateBuilder theFrom,
SearchFilterParser.CompareOperation operation) {
String rawSearchTerm;
- String paramName = theSearchParam.getName();
+ String paramName = QueryStack.getParamNameWithPrefix(theSpnamePrefix, theSearchParam.getName());
+
if (theParameter instanceof TokenParam) {
TokenParam id = (TokenParam) theParameter;
if (!id.isText()) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/TokenPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/TokenPredicateBuilder.java
index c183a401547..58282b1794b 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/TokenPredicateBuilder.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/TokenPredicateBuilder.java
@@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
+import ca.uhn.fhir.jpa.search.builder.QueryStack;
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.model.api.IQueryParameterType;
@@ -93,11 +94,13 @@ public class TokenPredicateBuilder extends BaseSearchParamPredicateBuilder {
public Condition createPredicateToken(Collection theParameters,
String theResourceName,
+ String theSpnamePrefix,
RuntimeSearchParam theSearchParam,
RequestPartitionId theRequestPartitionId) {
return createPredicateToken(
theParameters,
theResourceName,
+ theSpnamePrefix,
theSearchParam,
null,
theRequestPartitionId);
@@ -105,11 +108,15 @@ public class TokenPredicateBuilder extends BaseSearchParamPredicateBuilder {
public Condition createPredicateToken(Collection theParameters,
String theResourceName,
+ String theSpnamePrefix,
RuntimeSearchParam theSearchParam,
SearchFilterParser.CompareOperation theOperation,
RequestPartitionId theRequestPartitionId) {
+
+
final List codes = new ArrayList<>();
- String paramName = theSearchParam.getName();
+
+ String paramName = QueryStack.getParamNameWithPrefix(theSpnamePrefix, theSearchParam.getName());
SearchFilterParser.CompareOperation operation = theOperation;
@@ -197,12 +204,12 @@ public class TokenPredicateBuilder extends BaseSearchParamPredicateBuilder {
long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(getPartitionSettings(), theRequestPartitionId, theResourceName, paramName);
Condition hashIdentityPredicate = BinaryCondition.equalTo(getColumnHashIdentity(), generatePlaceholder(hashIdentity));
- Condition hashValuePredicate = createPredicateOrList(theResourceName, theSearchParam.getName(), sortedCodesList, false);
+ Condition hashValuePredicate = createPredicateOrList(theResourceName, paramName, sortedCodesList, false);
predicate = toAndPredicate(hashIdentityPredicate, hashValuePredicate);
} else {
- predicate = createPredicateOrList(theResourceName, theSearchParam.getName(), sortedCodesList, true);
+ predicate = createPredicateOrList(theResourceName, paramName, sortedCodesList, true);
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ContainedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ContainedTest.java
index f425ae6ca7b..17f0cf30afc 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ContainedTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ContainedTest.java
@@ -1,57 +1,310 @@
package ca.uhn.fhir.jpa.dao.r4;
-import org.hl7.fhir.r4.model.Observation;
-import org.hl7.fhir.r4.model.Patient;
-import org.hl7.fhir.r4.model.Reference;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
-import org.junit.jupiter.api.AfterAll;
+import org.hl7.fhir.r4.model.Address;
+import org.hl7.fhir.r4.model.Address.AddressUse;
+import org.hl7.fhir.r4.model.CodeableConcept;
+import org.hl7.fhir.r4.model.DateTimeType;
+import org.hl7.fhir.r4.model.Encounter;
+import org.hl7.fhir.r4.model.Encounter.EncounterParticipantComponent;
+import org.hl7.fhir.r4.model.Encounter.EncounterStatus;
+import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
+import org.hl7.fhir.r4.model.Observation;
+import org.hl7.fhir.r4.model.Organization;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Practitioner;
+import org.hl7.fhir.r4.model.Reference;
+import org.hl7.fhir.r4.model.ServiceRequest;
+import org.hl7.fhir.r4.model.ServiceRequest.ServiceRequestIntent;
+import org.hl7.fhir.r4.model.ServiceRequest.ServiceRequestStatus;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import ca.uhn.fhir.jpa.searchparam.SearchContainedEnum;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
-import ca.uhn.fhir.util.TestUtil;
+import ca.uhn.fhir.rest.api.server.IBundleProvider;
+import ca.uhn.fhir.rest.param.ReferenceParam;
+import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ContainedTest.class);
-
-
- @Test
- public void before() {
- myDaoConfig.setIndexContainedResources(true);
+ @BeforeEach
+ public void before() throws Exception {
+ myModelConfig.setIndexOnContainedResources(true);
}
-
- @Test
- public void testIndexContained() {
- Patient p = new Patient();
- p.setId("#some_patient");
- p.addName().setFamily("MYFAMILY").addGiven("MYGIVEN");
-
- Observation o1 = new Observation();
- o1.getCode().setText("Some Observation");
- o1.setSubject(new Reference(p));
- IIdType oid1 = myObservationDao.create(o1, mySrd).getId().toUnqualifiedVersionless();
-
- Observation o2 = new Observation();
- o2.getCode().setText("Some Observation");
- o2.setSubject(new Reference(p));
- IIdType oid2 = myObservationDao.create(o2, mySrd).getId().toUnqualifiedVersionless();
- Patient p2 = new Patient();
- p2.addName().setFamily("MYFAMILY").addGiven("MYGIVEN");
- IIdType pid2 = myPatientDao.create(p2, mySrd).getId().toUnqualifiedVersionless();
+ @AfterEach
+ public void after() throws Exception {
+ myModelConfig.setIndexOnContainedResources(false);
+ }
+
+ @Test
+ public void testCreateSimpleContainedResourceIndexWithGeneratedId() {
+
+ Patient p = new Patient();
+ p.addName().setFamily("Smith").addGiven("John");
- ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(o2));
+ Observation obs = new Observation();
+ obs.getCode().setText("Some Observation");
+ obs.setSubject(new Reference(p));
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+
+ IIdType id = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ Observation createdObs = myObservationDao.read(id);
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdObs));
+
+ runInTransaction(()->{
+ Long i = myEntityManager
+ .createQuery("SELECT count(s) FROM ResourceIndexedSearchParamString s WHERE s.myParamName = 'subject.family' AND s.myResourceType = 'Observation'", Long.class)
+ .getSingleResult();
+ assertEquals(1L, i.longValue());
+ });
SearchParameterMap map;
+
+ map = new SearchParameterMap();
+ map.add("subject", new ReferenceParam("name", "Smith"));
+ map.setSearchContainedMode(SearchContainedEnum.TRUE);
-// map = new SearchParameterMap();
-// map.add(Observation.SP_CODE, new TokenParam(null, "some observation").setModifier(TokenParamModifier.TEXT));
-// assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1, id2)));
-
+ assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id)));
}
+
+ @Test
+ public void testCreateSimpleContainedResourceIndexUserDefinedId() {
+ Patient p = new Patient();
+ p.setId("fooId");
+ p.addName().setFamily("Smith").addGiven("John");
+
+ Observation obs = new Observation();
+ obs.getCode().setText("Some Observation");
+ obs.getContained().add(p);
+ obs.getSubject().setReference("#fooId");
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+
+ IIdType id = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ Observation createdObs = myObservationDao.read(id);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdObs));
+
+ runInTransaction(()->{
+ Long i = myEntityManager
+ .createQuery("SELECT count(s) FROM ResourceIndexedSearchParamString s WHERE s.myParamName = 'subject.family' AND s.myResourceType = 'Observation'", Long.class)
+ .getSingleResult();
+ assertEquals(1L, i.longValue());
+ });
+
+ SearchParameterMap map;
+
+ map = new SearchParameterMap();
+ map.add("subject", new ReferenceParam("name", "Smith"));
+ map.setSearchContainedMode(SearchContainedEnum.TRUE);
+
+ assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id)));
+ }
- // TODO: make sure match URLs don't delete
-}
+ @Test
+ public void testCreateMultipleContainedResourceIndex() {
+
+ Practitioner prac1 = new Practitioner();
+ prac1.setId("prac1");
+ prac1.setActive(true);
+ prac1.setGender(AdministrativeGender.FEMALE);
+ prac1.addName().setFamily("Smith").addGiven("John");
+ Address address = prac1.addAddress();
+ address.setUse(AddressUse.WORK);
+ address.addLine("534 Erewhon St");
+ address.setCity("PleasantVille");
+ address.setState("NY");
+ address.setPostalCode("12345");
+
+ Organization org1 = new Organization();
+ org1.setId("org1");
+ org1.setActive(true);
+ org1.setName("org name 1");
+
+ Organization org2 = new Organization();
+ org2.setId("org2");
+ org2.setActive(false);
+ org2.setName("org name 2");
+
+ Patient patient = new Patient();
+ patient.getContained().add(prac1);
+ patient.getContained().add(org1);
+ patient.getContained().add(org2);
+ patient.addName().setFamily("Doe").addGiven("Jane");
+ patient.addGeneralPractitioner().setReference("#prac1");
+ patient.addGeneralPractitioner().setReference("#org1");
+ patient.getManagingOrganization().setReference("#org2");
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
+
+ IIdType id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
+
+ Patient createdPatient = myPatientDao.read(id);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdPatient));
+
+ runInTransaction(()->{
+ Long i = myEntityManager
+ .createQuery("SELECT count(s) FROM ResourceIndexedSearchParamString s WHERE s.myParamName = 'general-practitioner.family' AND s.myResourceType = 'Patient'", Long.class)
+ .getSingleResult();
+ assertEquals(1L, i.longValue());
+
+ i = myEntityManager
+ .createQuery("SELECT count(s) FROM ResourceIndexedSearchParamString s WHERE s.myParamName = 'general-practitioner.name' AND s.myResourceType = 'Patient'", Long.class)
+ .getSingleResult();
+ assertEquals(3L, i.longValue());
+
+ i = myEntityManager
+ .createQuery("SELECT count(s) FROM ResourceIndexedSearchParamString s WHERE s.myParamName = 'organization.name' AND s.myResourceType = 'Patient'", Long.class)
+ .getSingleResult();
+ assertEquals(1L, i.longValue());
+ });
+
+ SearchParameterMap map;
+
+ map = new SearchParameterMap();
+ map.add("general-practitioner", new ReferenceParam("family", "Smith"));
+ map.setSearchContainedMode(SearchContainedEnum.TRUE);
+
+ assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), containsInAnyOrder(toValues(id)));
+ }
+
+ @Test
+ public void testCreateComplexContainedResourceIndex() {
+
+ Encounter encounter = new Encounter();
+ encounter.setStatus(EncounterStatus.ARRIVED);
+
+ Patient patient = new Patient();
+ patient.setId("patient1");
+ patient.addName().setFamily("Doe").addGiven("Jane");
+ encounter.getSubject().setReference("#patient1");
+ encounter.getContained().add(patient);
+
+ ServiceRequest serviceRequest = new ServiceRequest();
+ serviceRequest.setId("serviceRequest1");
+ serviceRequest.setStatus(ServiceRequestStatus.ACTIVE);
+ serviceRequest.setIntent(ServiceRequestIntent.ORDER);
+ serviceRequest.setAuthoredOnElement(new DateTimeType("2021-02-23"));
+ encounter.addBasedOn().setReference("#serviceRequest1");
+ encounter.getContained().add(serviceRequest);
+
+ Practitioner prac1 = new Practitioner();
+ prac1.setId("prac1");
+ prac1.setActive(true);
+ prac1.setGender(AdministrativeGender.FEMALE);
+ prac1.addName().setFamily("Smith").addGiven("John");
+ EncounterParticipantComponent participient = encounter.addParticipant();
+ participient.getIndividual().setReference("#prac1");
+ encounter.getContained().add(prac1);
+
+ Observation obs = new Observation();
+ obs.setId("obs1");
+ obs.addIdentifier().setSystem("urn:system").setValue("FOO");
+ obs.getSubject().setReference("#patient1");
+ CodeableConcept cc = obs.getCode();
+ cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
+ encounter.addReasonReference().setReference("#obs1");
+ encounter.getContained().add(obs);
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
+
+ IIdType id = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
+
+ Encounter createdEncounter = myEncounterDao.read(id);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
+
+ runInTransaction(()->{
+ // The practitioner
+ Long i = myEntityManager
+ .createQuery("SELECT count(s) FROM ResourceIndexedSearchParamString s WHERE s.myParamName = 'participant.family' AND s.myResourceType = 'Encounter'", Long.class)
+ .getSingleResult();
+ assertEquals(1L, i.longValue());
+
+ // The Patient
+ i = myEntityManager
+ .createQuery("SELECT count(s) FROM ResourceIndexedSearchParamString s WHERE s.myParamName = 'subject.family' AND s.myResourceType = 'Encounter'", Long.class)
+ .getSingleResult();
+ assertEquals(1L, i.longValue());
+
+ // The Observation
+ i = myEntityManager
+ .createQuery("SELECT count(s) FROM ResourceIndexedSearchParamToken s WHERE s.myParamName = 'reason-reference.code' AND s.myResourceType = 'Encounter'", Long.class)
+ .getSingleResult();
+ assertEquals(1L, i.longValue());
+ i = myEntityManager
+ .createQuery("SELECT count(s) FROM ResourceIndexedSearchParamToken s WHERE s.myParamName = 'reason-reference.combo-code' AND s.myResourceType = 'Encounter'", Long.class)
+ .getSingleResult();
+ assertEquals(1L, i.longValue());
+
+ // The ServiceRequest
+ i = myEntityManager
+ .createQuery("SELECT count(s) FROM ResourceIndexedSearchParamDate s WHERE s.myParamName = 'based-on.authored' AND s.myResourceType = 'Encounter'", Long.class)
+ .getSingleResult();
+ assertEquals(1L, i.longValue());
+ });
+
+ SearchParameterMap map;
+
+ map = new SearchParameterMap();
+ map.add("based-on", new ReferenceParam("authored", "2021-02-23"));
+ map.setSearchContainedMode(SearchContainedEnum.TRUE);
+
+ assertThat(toUnqualifiedVersionlessIdValues(myEncounterDao.search(map)), containsInAnyOrder(toValues(id)));
+ }
+
+ @Test
+ public void testSearchWithNotSupportedSearchType() {
+
+ SearchParameterMap map;
+
+ map = new SearchParameterMap();
+ map.add("subject", new ReferenceParam("near", "toronto"));
+ map.setSearchContainedMode(SearchContainedEnum.TRUE);
+
+ try {
+ IBundleProvider outcome = myObservationDao.search(map);
+ outcome.getResources(0, 1).get(0);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertEquals(e.getMessage(), "The search type: SPECIAL is not supported.");
+ }
+
+ }
+
+ @Test
+ public void testSearchWithNotSupportedSearchParameter() {
+
+ SearchParameterMap map;
+
+ map = new SearchParameterMap();
+ map.add("subject", new ReferenceParam("marital-status", "M"));
+ map.setSearchContainedMode(SearchContainedEnum.TRUE);
+
+ try {
+ IBundleProvider outcome = myObservationDao.search(map);
+ outcome.getResources(0, 1).get(0);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertEquals(e.getMessage(), "Unknown search parameter name: subject.marital-status.");
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchParamExtractorR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchParamExtractorR4Test.java
index fa5c7a5c567..926a58716dd 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchParamExtractorR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchParamExtractorR4Test.java
@@ -284,7 +284,7 @@ public class SearchParamExtractorR4Test {
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, mySearchParamRegistry);
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam("Encounter", "location");
assertNotNull(param);
- ISearchParamExtractor.SearchParamSet links = extractor.extractResourceLinks(enc);
+ ISearchParamExtractor.SearchParamSet links = extractor.extractResourceLinks(enc, false);
assertEquals(1, links.size());
assertEquals("location", links.iterator().next().getSearchParamName());
assertEquals("Encounter.location.location", links.iterator().next().getPath());
@@ -299,7 +299,7 @@ public class SearchParamExtractorR4Test {
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, mySearchParamRegistry);
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam("Consent", Consent.SP_SOURCE_REFERENCE);
assertNotNull(param);
- ISearchParamExtractor.SearchParamSet links = extractor.extractResourceLinks(consent);
+ ISearchParamExtractor.SearchParamSet links = extractor.extractResourceLinks(consent, false);
assertEquals(1, links.size());
assertEquals("Consent.source", links.iterator().next().getPath());
assertEquals("Consent/999", ((Reference) links.iterator().next().getRef()).getReference());
@@ -334,7 +334,7 @@ public class SearchParamExtractorR4Test {
patient.addExtension("http://patext", new Reference("Organization/AAA"));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, mySearchParamRegistry);
- ISearchParamExtractor.SearchParamSet links = extractor.extractResourceLinks(patient);
+ ISearchParamExtractor.SearchParamSet links = extractor.extractResourceLinks(patient, false);
assertEquals(1, links.size());
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderHasParamR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderHasParamR4Test.java
index 30d6fb3d93c..36e6ad17a72 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderHasParamR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderHasParamR4Test.java
@@ -108,12 +108,14 @@ public class ResourceProviderHasParamR4Test extends BaseResourceProviderR4Test {
obs.addIdentifier().setSystem("urn:system").setValue("NOLINK");
obs.setDevice(new Reference(devId));
myObservationDao.create(obs, mySrd);
+
+ ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
+
String uri = ourServerBase + "/Patient?_has:Observation:subject:identifier=" + UrlUtil.escapeUrlParam("urn:system|FOO");
List ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
assertThat(ids, contains(pid0.getValue()));
-
}
@Test
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4SearchContainedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4SearchContainedTest.java
new file mode 100644
index 00000000000..217a97b4a34
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4SearchContainedTest.java
@@ -0,0 +1,978 @@
+package ca.uhn.fhir.jpa.provider.r4;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.hl7.fhir.r4.model.Bundle;
+import org.hl7.fhir.r4.model.CarePlan;
+import org.hl7.fhir.r4.model.CarePlan.CarePlanIntent;
+import org.hl7.fhir.r4.model.CarePlan.CarePlanStatus;
+import org.hl7.fhir.r4.model.ClinicalImpression;
+import org.hl7.fhir.r4.model.ClinicalImpression.ClinicalImpressionStatus;
+import org.hl7.fhir.r4.model.CodeableConcept;
+import org.hl7.fhir.r4.model.DecimalType;
+import org.hl7.fhir.r4.model.Encounter;
+import org.hl7.fhir.r4.model.Encounter.EncounterStatus;
+import org.hl7.fhir.r4.model.HumanName;
+import org.hl7.fhir.r4.model.Observation;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Quantity;
+import org.hl7.fhir.r4.model.Resource;
+import org.hl7.fhir.r4.model.RiskAssessment;
+import org.hl7.fhir.r4.model.RiskAssessment.RiskAssessmentStatus;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+
+import ca.uhn.fhir.jpa.api.config.DaoConfig;
+import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
+import ca.uhn.fhir.parser.StrictErrorHandler;
+import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
+import ca.uhn.fhir.util.UrlUtil;
+
+
+public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR4Test {
+
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4SearchContainedTest.class);
+ private CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor();
+
+ @Autowired
+ @Qualifier("myClinicalImpressionDaoR4")
+ protected IFhirResourceDao myClinicalImpressionDao;
+
+ @Override
+ @AfterEach
+ public void after() throws Exception {
+ super.after();
+
+ myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
+ myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
+ myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
+ myDaoConfig.setCountSearchResultsUpTo(new DaoConfig().getCountSearchResultsUpTo());
+ myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
+ myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches());
+ myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
+
+ myClient.unregisterInterceptor(myCapturingInterceptor);
+ myModelConfig.setIndexOnContainedResources(false);
+ }
+
+ @BeforeEach
+ @Override
+ public void before() throws Exception {
+ super.before();
+ myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
+
+ myDaoConfig.setAllowMultipleDelete(true);
+ myClient.registerInterceptor(myCapturingInterceptor);
+ myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
+ myModelConfig.setIndexOnContainedResources(true);
+ }
+
+ @BeforeEach
+ public void beforeDisableResultReuse() {
+ myDaoConfig.setReuseCachedSearchResultsForMillis(null);
+ }
+
+
+ @Test
+ public void testContainedSearchByName() throws Exception {
+
+ IIdType oid1;
+
+ {
+ Patient p = new Patient();
+ p.setId("patient1");
+ p.addName().setFamily("Smith").addGiven("John");
+
+ Observation obs = new Observation();
+ obs.getCode().setText("Observation 1");
+ obs.getContained().add(p);
+ obs.getSubject().setReference("#patient1");
+
+ oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+ }
+
+ {
+ Patient p = new Patient();
+ p.setId("patient1");
+ p.addName().setFamily("Doe").addGiven("Jane");
+
+ Observation obs = new Observation();
+ obs.getCode().setText("Observation 2");
+ obs.getContained().add(p);
+ obs.getSubject().setReference("#patient1");
+
+ myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+ }
+
+ {
+ Patient p = new Patient();
+ p.setId("patient1");
+ p.addName().setFamily("Jones").addGiven("Peter");
+
+ Observation obs = new Observation();
+ obs.getCode().setText("Observation 2");
+ obs.getContained().add(p);
+ obs.getSubject().setReference("#patient1");
+
+ myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+ }
+
+
+ //-- Simple name match
+ String uri = ourServerBase + "/Observation?subject.name=Smith&_contained=true";
+ List oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, oids.size());
+ assertThat(oids, contains(oid1.getValue()));
+
+ //-- Simple name match with or
+ uri = ourServerBase + "/Observation?subject.name=Smith,Jane&_contained=true";
+ oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(2L, oids.size());
+ //assertEquals(oids.toString(), "[Observation/1, Observation/2]");
+
+ //-- Simple name match with qualifier
+ uri = ourServerBase + "/Observation?subject.name:exact=Smith&_contained=true";
+ oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, oids.size());
+ assertThat(oids, contains(oid1.getValue()));
+
+ //-- Simple name match with and
+ uri = ourServerBase + "/Observation?subject.family=Smith&subject.given=John&_contained=true";
+ oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, oids.size());
+ assertThat(oids, contains(oid1.getValue()));
+
+ //-- Simple name match with both, default to normal search, found 0
+ uri = ourServerBase + "/Observation?subject.name=Smith&_contained=both";
+ oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(0L, oids.size());
+
+ }
+
+ @Test
+ public void testContainedSearchByDate() throws Exception {
+
+ IIdType oid1;
+ IIdType oid3;
+
+ {
+ Patient p = new Patient();
+ p.setId("patient1");
+ p.addName().setFamily("Smith").addGiven("John");
+ p.getBirthDateElement().setValueAsString("2000-01-01");
+
+ Observation obs = new Observation();
+ obs.getCode().setText("Observation 1");
+ obs.getContained().add(p);
+ obs.getSubject().setReference("#patient1");
+
+ oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+ }
+
+ {
+ Patient p = new Patient();
+ p.setId("patient1");
+ p.addName().setFamily("Doe").addGiven("Jane");
+ p.getBirthDateElement().setValueAsString("2000-02-01");
+
+ Observation obs = new Observation();
+ obs.getCode().setText("Observation 2");
+ obs.getContained().add(p);
+ obs.getSubject().setReference("#patient1");
+
+ myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+ }
+
+ {
+ Patient p = new Patient();
+ p.setId("patient1");
+ p.addName().setFamily("Jones").addGiven("Peter");
+ p.getBirthDateElement().setValueAsString("2000-03-01");
+
+ Observation obs = new Observation();
+ obs.getCode().setText("Observation 2");
+ obs.getContained().add(p);
+ obs.getSubject().setReference("#patient1");
+
+ oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+ }
+
+ //-- Search by date default op
+ String uri = ourServerBase + "/Observation?subject.birthdate=2000-01-01&_contained=true";
+ List oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, oids.size());
+ assertThat(oids, contains(oid1.getValue()));
+
+ //-- Search by date op=eq
+ uri = ourServerBase + "/Observation?subject.birthdate=eq2000-01-01&_contained=true";
+ oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, oids.size());
+ assertThat(oids, contains(oid1.getValue()));
+
+ //-- Search by date op=eq, with or
+ uri = ourServerBase + "/Observation?subject.birthdate=2000-01-01,2000-02-01&_contained=true";
+ oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(2L, oids.size());
+ //assertEquals(oids.toString(), "[Observation/1, Observation/2]");
+
+ //-- Simple name match with op = gt
+ uri = ourServerBase + "/Observation?subject.birthdate=gt2000-02-10&_contained=true";
+ oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, oids.size());
+ assertThat(oids, contains(oid3.getValue()));
+
+ //-- Simple name match with AND
+ uri = ourServerBase + "/Observation?subject.family=Smith&subject.birthdate=eq2000-01-01&_contained=true";
+ oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, oids.size());
+ assertThat(oids, contains(oid1.getValue()));
+
+ //-- Simple name match with AND - not found
+ uri = ourServerBase + "/Observation?subject.family=Smith&subject.birthdate=eq2000-02-01&_contained=true";
+ oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(0L, oids.size());
+ }
+
+
+ @Test
+ public void testContainedSearchByNumber() throws Exception {
+
+ IIdType cid1;
+
+ {
+ Patient p = new Patient();
+ p.setId("patient1");
+ p.addName().setFamily("Smith").addGiven("John");
+ p.getBirthDateElement().setValueAsString("2000-01-01");
+
+
+ RiskAssessment risk = new RiskAssessment();
+ risk.setId("risk1");
+ risk.setStatus(RiskAssessmentStatus.CORRECTED);
+ risk.getSubject().setReference("#patient1");
+ risk.getPredictionFirstRep().setProbability(new DecimalType(2));
+
+ ClinicalImpression imp = new ClinicalImpression();
+ imp.setStatus(ClinicalImpressionStatus.COMPLETED);
+
+ imp.getContained().add(p);
+ imp.getSubject().setReference("#patient1");
+
+ imp.getContained().add(risk);
+ imp.getInvestigationFirstRep().getItemFirstRep().setReference("#risk1");
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(imp));
+
+ cid1 = myClinicalImpressionDao.create(imp, mySrd).getId().toUnqualifiedVersionless();
+
+ ClinicalImpression createdImp = myClinicalImpressionDao.read(cid1);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdImp));
+ }
+
+ {
+ Patient p = new Patient();
+ p.setId("patient1");
+ p.addName().setFamily("Smith").addGiven("John");
+ p.getBirthDateElement().setValueAsString("2000-01-01");
+
+
+ RiskAssessment risk = new RiskAssessment();
+ risk.setId("risk1");
+ risk.setStatus(RiskAssessmentStatus.CORRECTED);
+ risk.getSubject().setReference("#patient1");
+ risk.getPredictionFirstRep().setProbability(new DecimalType(5));
+
+ ClinicalImpression imp = new ClinicalImpression();
+ imp.setStatus(ClinicalImpressionStatus.COMPLETED);
+
+ imp.getContained().add(p);
+ imp.getSubject().setReference("#patient1");
+
+ imp.getContained().add(risk);
+ imp.getInvestigationFirstRep().getItemFirstRep().setReference("#risk1");
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(imp));
+
+ IIdType cid2 = myClinicalImpressionDao.create(imp, mySrd).getId().toUnqualifiedVersionless();
+
+ ClinicalImpression createdImp = myClinicalImpressionDao.read(cid2);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdImp));
+ }
+
+ {
+ Patient p = new Patient();
+ p.setId("patient1");
+ p.addName().setFamily("Smith").addGiven("John");
+ p.getBirthDateElement().setValueAsString("2000-01-01");
+
+
+ RiskAssessment risk = new RiskAssessment();
+ risk.setId("risk1");
+ risk.setStatus(RiskAssessmentStatus.CORRECTED);
+ risk.getSubject().setReference("#patient1");
+ risk.getPredictionFirstRep().setProbability(new DecimalType(10));
+
+ ClinicalImpression imp = new ClinicalImpression();
+ imp.setStatus(ClinicalImpressionStatus.COMPLETED);
+
+ imp.getContained().add(p);
+ imp.getSubject().setReference("#patient1");
+
+ imp.getContained().add(risk);
+ imp.getInvestigationFirstRep().getItemFirstRep().setReference("#risk1");
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(imp));
+
+ IIdType cid3 = myClinicalImpressionDao.create(imp, mySrd).getId().toUnqualifiedVersionless();
+
+ ClinicalImpression createdImp = myClinicalImpressionDao.read(cid3);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdImp));
+ }
+
+ //-- Search by number
+ String uri = ourServerBase + "/ClinicalImpression?investigation.probability=2&_contained=true";
+ List cids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, cids.size());
+ assertThat(cids, contains(cid1.getValue()));
+
+
+ //-- Search by number with op = eq
+ uri = ourServerBase + "/ClinicalImpression?investigation.probability=eq2&_contained=true";
+ cids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, cids.size());
+ assertThat(cids, contains(cid1.getValue()));
+
+
+ //-- Search by number with op = eq and or
+ uri = ourServerBase + "/ClinicalImpression?investigation.probability=eq2,10&_contained=true";
+ cids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+ assertEquals(2L, cids.size());
+
+ //-- Search by number with op = lt
+ uri = ourServerBase + "/ClinicalImpression?investigation.probability=lt4&_contained=true";
+ cids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, cids.size());
+ assertThat(cids, contains(cid1.getValue()));
+ }
+
+ @Test
+ public void testContainedSearchByQuantity() throws Exception {
+
+ IIdType eid1;
+ {
+ Encounter encounter = new Encounter();
+ encounter.setStatus(EncounterStatus.ARRIVED);
+
+ Patient patient = new Patient();
+ patient.setId("patient1");
+ patient.addName().setFamily("Doe").addGiven("Jane");
+ encounter.getSubject().setReference("#patient1");
+ encounter.getContained().add(patient);
+
+ Observation obs = new Observation();
+ obs.setId("obs1");
+ obs.addIdentifier().setSystem("urn:system").setValue("FOO");
+ obs.getSubject().setReference("#patient1");
+ CodeableConcept cc = obs.getCode();
+ cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
+ Quantity quantity = obs.getValueQuantity();
+ quantity.setValue(200);
+ encounter.addReasonReference().setReference("#obs1");
+ encounter.getContained().add(obs);
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
+
+ eid1 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
+
+ Encounter createdEncounter = myEncounterDao.read(eid1);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
+ }
+
+
+ {
+ Encounter encounter = new Encounter();
+ encounter.setStatus(EncounterStatus.ARRIVED);
+
+ Patient patient = new Patient();
+ patient.setId("patient1");
+ patient.addName().setFamily("Doe").addGiven("Jane");
+ encounter.getSubject().setReference("#patient1");
+ encounter.getContained().add(patient);
+
+ Observation obs = new Observation();
+ obs.setId("obs1");
+ obs.addIdentifier().setSystem("urn:system").setValue("FOO");
+ obs.getSubject().setReference("#patient1");
+ CodeableConcept cc = obs.getCode();
+ cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
+ Quantity quantity = obs.getValueQuantity();
+ quantity.setValue(300);
+ encounter.addReasonReference().setReference("#obs1");
+ encounter.getContained().add(obs);
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
+
+ IIdType eid2 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
+
+ Encounter createdEncounter = myEncounterDao.read(eid2);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
+ }
+
+ {
+ Encounter encounter = new Encounter();
+ encounter.setStatus(EncounterStatus.ARRIVED);
+
+ Patient patient = new Patient();
+ patient.setId("patient1");
+ patient.addName().setFamily("Doe").addGiven("Jane");
+ encounter.getSubject().setReference("#patient1");
+ encounter.getContained().add(patient);
+
+ Observation obs = new Observation();
+ obs.setId("obs1");
+ obs.addIdentifier().setSystem("urn:system").setValue("FOO");
+ obs.getSubject().setReference("#patient1");
+ CodeableConcept cc = obs.getCode();
+ cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
+ Quantity quantity = obs.getValueQuantity();
+ quantity.setValue(400);
+ encounter.addReasonReference().setReference("#obs1");
+ encounter.getContained().add(obs);
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
+
+ IIdType eid3 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
+
+ Encounter createdEncounter = myEncounterDao.read(eid3);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
+ }
+
+ //-- Search by quantity
+ String uri = ourServerBase + "/Encounter?reason-reference.combo-value-quantity=200&_contained=true";
+ List eids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, eids.size());
+ assertThat(eids, contains(eid1.getValue()));
+
+
+ //-- Search by quantity
+ uri = ourServerBase + "/Encounter?reason-reference.combo-value-quantity=le400&_contained=true";
+ eids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(3L, eids.size());
+
+ }
+
+ @Test
+ public void testContainedSearchByToken() throws Exception {
+
+ IIdType eid1;
+ {
+ Encounter encounter = new Encounter();
+ encounter.setStatus(EncounterStatus.ARRIVED);
+
+ Patient patient = new Patient();
+ patient.setId("patient1");
+ patient.addName().setFamily("Doe").addGiven("Jane");
+ encounter.getSubject().setReference("#patient1");
+ encounter.getContained().add(patient);
+
+ Observation obs = new Observation();
+ obs.setId("obs1");
+ obs.addIdentifier().setSystem("urn:system").setValue("FOO");
+ obs.getSubject().setReference("#patient1");
+ CodeableConcept cc = obs.getCode();
+ cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
+ Quantity quantity = obs.getValueQuantity();
+ quantity.setValue(200);
+ encounter.addReasonReference().setReference("#obs1");
+ encounter.getContained().add(obs);
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
+
+ eid1 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
+
+ Encounter createdEncounter = myEncounterDao.read(eid1);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
+ }
+
+
+ {
+ Encounter encounter = new Encounter();
+ encounter.setStatus(EncounterStatus.ARRIVED);
+
+ Patient patient = new Patient();
+ patient.setId("patient1");
+ patient.addName().setFamily("Doe").addGiven("Jane");
+ encounter.getSubject().setReference("#patient1");
+ encounter.getContained().add(patient);
+
+ Observation obs = new Observation();
+ obs.setId("obs1");
+ obs.addIdentifier().setSystem("urn:system").setValue("FOO");
+ obs.getSubject().setReference("#patient1");
+ CodeableConcept cc = obs.getCode();
+ cc.addCoding().setCode("2345-8").setSystem("http://loinc.org");
+ Quantity quantity = obs.getValueQuantity();
+ quantity.setValue(300);
+ encounter.addReasonReference().setReference("#obs1");
+ encounter.getContained().add(obs);
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
+
+ IIdType eid2 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
+
+ Encounter createdEncounter = myEncounterDao.read(eid2);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
+ }
+
+ {
+ Encounter encounter = new Encounter();
+ encounter.setStatus(EncounterStatus.ARRIVED);
+
+ Patient patient = new Patient();
+ patient.setId("patient1");
+ patient.addName().setFamily("Doe").addGiven("Jane");
+ encounter.getSubject().setReference("#patient1");
+ encounter.getContained().add(patient);
+
+ Observation obs = new Observation();
+ obs.setId("obs1");
+ obs.addIdentifier().setSystem("urn:system").setValue("FOO");
+ obs.getSubject().setReference("#patient1");
+ CodeableConcept cc = obs.getCode();
+ cc.addCoding().setCode("2345-9").setSystem("http://loinc.org");
+ Quantity quantity = obs.getValueQuantity();
+ quantity.setValue(400);
+ encounter.addReasonReference().setReference("#obs1");
+ encounter.getContained().add(obs);
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
+
+ IIdType eid3 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
+
+ Encounter createdEncounter = myEncounterDao.read(eid3);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
+ }
+
+ //-- Search by code
+ String uri = ourServerBase + "/Encounter?reason-reference.code=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7") + "&_contained=true";
+ List eids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, eids.size());
+ assertThat(eids, contains(eid1.getValue()));
+
+ }
+
+ @Test
+ public void testContainedSearchByComposite() throws Exception {
+
+ IIdType eid2;
+ {
+ Encounter encounter = new Encounter();
+ encounter.setStatus(EncounterStatus.ARRIVED);
+
+ Patient patient = new Patient();
+ patient.setId("patient1");
+ patient.addName().setFamily("Doe").addGiven("Jane");
+ encounter.getSubject().setReference("#patient1");
+ encounter.getContained().add(patient);
+
+ Observation obs = new Observation();
+ obs.setId("obs1");
+ obs.addIdentifier().setSystem("urn:system").setValue("FOO");
+ obs.getSubject().setReference("#patient1");
+ CodeableConcept cc = obs.getCode();
+ cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
+ Quantity quantity = obs.getValueQuantity();
+ quantity.setValue(200);
+ encounter.addReasonReference().setReference("#obs1");
+ encounter.getContained().add(obs);
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
+
+ IIdType eid1 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
+
+ Encounter createdEncounter = myEncounterDao.read(eid1);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
+ }
+
+
+ {
+ Encounter encounter = new Encounter();
+ encounter.setStatus(EncounterStatus.ARRIVED);
+
+ Patient patient = new Patient();
+ patient.setId("patient1");
+ patient.addName().setFamily("Doe").addGiven("Jane");
+ encounter.getSubject().setReference("#patient1");
+ encounter.getContained().add(patient);
+
+ Observation obs = new Observation();
+ obs.setId("obs1");
+ obs.addIdentifier().setSystem("urn:system").setValue("FOO");
+ obs.getSubject().setReference("#patient1");
+ CodeableConcept cc = obs.getCode();
+ cc.addCoding().setCode("2345-8").setSystem("http://loinc.org");
+ Quantity quantity = obs.getValueQuantity();
+ quantity.setValue(300);
+ encounter.addReasonReference().setReference("#obs1");
+ encounter.getContained().add(obs);
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
+
+ eid2 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
+
+ Encounter createdEncounter = myEncounterDao.read(eid2);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
+ }
+
+ {
+ Encounter encounter = new Encounter();
+ encounter.setStatus(EncounterStatus.ARRIVED);
+
+ Patient patient = new Patient();
+ patient.setId("patient1");
+ patient.addName().setFamily("Doe").addGiven("Jane");
+ encounter.getSubject().setReference("#patient1");
+ encounter.getContained().add(patient);
+
+ Observation obs = new Observation();
+ obs.setId("obs1");
+ obs.addIdentifier().setSystem("urn:system").setValue("FOO");
+ obs.getSubject().setReference("#patient1");
+ CodeableConcept cc = obs.getCode();
+ cc.addCoding().setCode("2345-9").setSystem("http://loinc.org");
+ Quantity quantity = obs.getValueQuantity();
+ quantity.setValue(400);
+ encounter.addReasonReference().setReference("#obs1");
+ encounter.getContained().add(obs);
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
+
+ IIdType eid3 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
+
+ Encounter createdEncounter = myEncounterDao.read(eid3);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
+ }
+
+ //-- Search by composite
+ String uri = ourServerBase + "/Encounter?reason-reference.combo-code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-8$300") + "&_contained=true";
+ List eids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, eids.size());
+ assertThat(eids, contains(eid2.getValue()));
+
+ //-- Search by composite - not found
+ uri = ourServerBase + "/Encounter?reason-reference.combo-code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$300") + "&_contained=true";
+ eids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(0L, eids.size());
+
+ }
+
+
+ @Test
+ public void testContainedSearchByUri() throws Exception {
+
+ IIdType oid1;
+
+ {
+ Patient p = new Patient();
+ p.setId("patient1");
+ p.addName().setFamily("Smith").addGiven("John");
+ p.getBirthDateElement().setValueAsString("2000-01-01");
+
+ CarePlan carePlan = new CarePlan();
+ carePlan.setId("carePlan1");
+ carePlan.setStatus(CarePlanStatus.ACTIVE);
+ carePlan.setIntent(CarePlanIntent.ORDER);
+ carePlan.getSubject().setReference("#patient1");
+ carePlan.addInstantiatesUri("http://www.hl7.com");
+
+ Observation obs = new Observation();
+ obs.getCode().setText("Observation 1");
+ obs.getContained().add(p);
+ obs.getSubject().setReference("#patient1");
+ obs.getContained().add(carePlan);
+ obs.getBasedOnFirstRep().setReference("#carePlan1");
+
+
+ oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+
+ Observation createdObs = myObservationDao.read(oid1);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdObs));
+ }
+
+ {
+ Patient p = new Patient();
+ p.setId("patient2");
+ p.addName().setFamily("Smith").addGiven("John");
+ p.getBirthDateElement().setValueAsString("2000-01-01");
+
+ CarePlan carePlan = new CarePlan();
+ carePlan.setId("carePlan2");
+ carePlan.setStatus(CarePlanStatus.ACTIVE);
+ carePlan.setIntent(CarePlanIntent.ORDER);
+ carePlan.getSubject().setReference("#patient2");
+ carePlan.addInstantiatesUri("http://www2.hl7.com");
+
+ Observation obs = new Observation();
+ obs.getCode().setText("Observation 2");
+ obs.getContained().add(p);
+ obs.getSubject().setReference("#patient2");
+ obs.getContained().add(carePlan);
+ obs.getBasedOnFirstRep().setReference("#carePlan2");
+
+ myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+ }
+
+ {
+ Patient p = new Patient();
+ p.setId("patient3");
+ p.addName().setFamily("Smith").addGiven("John");
+ p.getBirthDateElement().setValueAsString("2000-01-01");
+
+ CarePlan carePlan = new CarePlan();
+ carePlan.setId("carePlan3");
+ carePlan.setStatus(CarePlanStatus.ACTIVE);
+ carePlan.setIntent(CarePlanIntent.ORDER);
+ carePlan.getSubject().setReference("#patient3");
+ carePlan.addInstantiatesUri("http://www2.hl7.com");
+
+ Observation obs = new Observation();
+ obs.getCode().setText("Observation 3");
+ obs.getContained().add(p);
+ obs.getSubject().setReference("#patient3");
+ obs.getContained().add(carePlan);
+ obs.getBasedOnFirstRep().setReference("#carePlan3");
+
+ myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+ }
+
+ //-- Search by uri
+ String uri = ourServerBase + "/Observation?based-on.instantiates-uri=http://www.hl7.com&_contained=true";
+ List oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, oids.size());
+ assertThat(oids, contains(oid1.getValue()));
+
+ //-- Search by uri more than 1 results
+ uri = ourServerBase + "/Observation?based-on.instantiates-uri=http://www2.hl7.com&_contained=true";
+ oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(2L, oids.size());
+
+ //-- Search by uri with 'or'
+ uri = ourServerBase + "/Observation?based-on.instantiates-uri=http://www.hl7.com,http://www2.hl7.com&_contained=true";
+ oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(3L, oids.size());
+
+ }
+
+ @Test
+ public void testUpdateContainedResource() throws Exception {
+
+ IIdType oid1;
+
+ {
+ Patient p = new Patient();
+ p.setId("patient1");
+ p.addName().setFamily("Smith").addGiven("John");
+
+ Observation obs = new Observation();
+ obs.getCode().setText("Observation 1");
+ obs.getContained().add(p);
+ obs.getSubject().setReference("#patient1");
+
+ oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+
+ Observation createdObs = myObservationDao.read(oid1);
+
+ //-- changed the last name to Doe
+ List containedResources = createdObs.getContained();
+
+ for (Resource res : containedResources) {
+ if (res instanceof Patient) {
+ Patient p1 = (Patient)res;
+ HumanName name = p1.getNameFirstRep();
+ name.setFamily("Doe");
+ break;
+ }
+ }
+
+ // -- update
+ oid1 = myObservationDao.update(createdObs, mySrd).getId().toUnqualifiedVersionless();
+ }
+
+ {
+ Patient p = new Patient();
+ p.setId("patient1");
+ p.addName().setFamily("Doe").addGiven("Jane");
+
+ Observation obs = new Observation();
+ obs.getCode().setText("Observation 2");
+ obs.getContained().add(p);
+ obs.getSubject().setReference("#patient1");
+
+ myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+ }
+
+ {
+ Patient p = new Patient();
+ p.setId("patient1");
+ p.addName().setFamily("Jones").addGiven("Peter");
+
+ Observation obs = new Observation();
+ obs.getCode().setText("Observation 2");
+ obs.getContained().add(p);
+ obs.getSubject().setReference("#patient1");
+
+ myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+ }
+
+
+ //-- No Obs with Patient Smith
+ String uri = ourServerBase + "/Observation?subject.family=Smith&_contained=true";
+ List oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(0L, oids.size());
+
+ //-- Two Obs with Patient Doe
+ uri = ourServerBase + "/Observation?subject.family=Doe&_contained=true";
+ oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(2L, oids.size());
+ }
+
+
+ @Test
+ public void testDeleteContainedResource() throws Exception {
+
+ IIdType oid1;
+
+ {
+ Patient p1 = new Patient();
+ p1.setId("patient1");
+ p1.addName().setFamily("Smith").addGiven("John");
+
+ Patient p2 = new Patient();
+ p2.setId("patient2");
+ p2.addName().setFamily("Doe").addGiven("Jane");
+
+ Observation obs = new Observation();
+ obs.getCode().setText("Observation 1");
+ obs.getContained().add(p1);
+ obs.getSubject().setReference("#patient1");
+
+ oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ // -- remove contained resource
+ obs.getContained().remove(p1);
+ // -- add new contained resource
+ obs.getContained().add(p2);
+ obs.getSubject().setReference("#patient2");
+
+ ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
+
+ // -- update
+ oid1 = myObservationDao.update(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ Observation updatedObs = myObservationDao.read(oid1);
+
+ ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedObs));
+ }
+
+ //-- No Obs with Patient Smith
+ String uri = ourServerBase + "/Observation?subject.family=Smith&_contained=true";
+ List oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(0L, oids.size());
+
+ //-- 1 Obs with Patient Doe
+ uri = ourServerBase + "/Observation?subject.family=Doe&_contained=true";
+ oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
+
+ assertEquals(1L, oids.size());
+ assertThat(oids, contains(oid1.getValue()));
+
+ }
+ private List searchAndReturnUnqualifiedVersionlessIdValues(String uri) throws IOException {
+ List ids;
+ HttpGet get = new HttpGet(uri);
+
+ try (CloseableHttpResponse response = ourHttpClient.execute(get)) {
+ String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
+ ourLog.info(resp);
+ Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, resp);
+ ids = toUnqualifiedVersionlessIdValues(bundle);
+ }
+ return ids;
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ModelConfig.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ModelConfig.java
index 43f98bdc209..4e06fd1d4f5 100644
--- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ModelConfig.java
+++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ModelConfig.java
@@ -98,6 +98,8 @@ public class ModelConfig {
private Set myAutoVersionReferenceAtPaths = Collections.emptySet();
private Map> myTypeToAutoVersionReferenceAtPaths = Collections.emptyMap();
private boolean myRespectVersionsForSearchIncludes;
+
+ private boolean myIndexOnContainedResources = false;
/**
* Constructor
@@ -730,6 +732,27 @@ public class ModelConfig {
myRespectVersionsForSearchIncludes = theRespectVersionsForSearchIncludes;
}
+
+ /**
+ * Should indexed on the contained resources, it could be searched by _contained=true
+ * This may have performance impacts
+ *
+ * @since 5.4.0
+ */
+ public boolean isIndexOnContainedResources() {
+ return myIndexOnContainedResources;
+ }
+
+ /**
+ * Should indexed on the contained resources, it could be searched by _contained=true
+ * This may have performance impacts
+ *
+ * @since 5.4.0
+ */
+ public void setIndexOnContainedResources(boolean theIndexOnContainedResources) {
+ myIndexOnContainedResources = theIndexOnContainedResources;
+ }
+
private static void validateTreatBaseUrlsAsLocal(String theUrl) {
Validate.notBlank(theUrl, "Base URL must not be null or empty");
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchContainedEnum.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchContainedEnum.java
new file mode 100644
index 00000000000..1755b09b85d
--- /dev/null
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchContainedEnum.java
@@ -0,0 +1,40 @@
+package ca.uhn.fhir.jpa.searchparam;
+
+/*
+ * #%L
+ * HAPI FHIR Search Parameters
+ * %%
+ * Copyright (C) 2014 - 2021 Smile CDR, Inc.
+ * %%
+ * 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%
+ */
+
+public enum SearchContainedEnum {
+
+ /**
+ * default, search on the non-contained (normal) resources
+ */
+ FALSE,
+
+ /**
+ * search on the contained resources only
+ */
+ TRUE,
+
+ /**
+ * Search on the normal resources and contained resources.
+ * This option is not supported yet.
+ */
+ BOTH,
+}
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 4e6a31a8063..e6434a8cf5e 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
@@ -79,7 +79,8 @@ public class SearchParameterMap implements Serializable {
private boolean myLastN;
private Integer myLastNMax;
private boolean myDeleteExpunge;
-
+ private SearchContainedEnum mySearchContainedMode = SearchContainedEnum.FALSE;
+
/**
* Constructor
*/
@@ -734,4 +735,13 @@ public class SearchParameterMap implements Serializable {
return retVal;
}
+ public SearchContainedEnum getSearchContainedMode() {
+ return mySearchContainedMode;
+ }
+
+ public void setSearchContainedMode(SearchContainedEnum theSearchContainedMode) {
+ this.mySearchContainedMode = theSearchContainedMode;
+ }
+
+
}
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java
index 2ef20a396b2..20319c80c74 100644
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java
@@ -171,9 +171,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
@Override
- public SearchParamSet extractResourceLinks(IBaseResource theResource) {
+ public SearchParamSet extractResourceLinks(IBaseResource theResource, boolean theWantLocalReferences) {
IExtractor extractor = createReferenceExtractor();
- return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.REFERENCE);
+ return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.REFERENCE, theWantLocalReferences);
}
private IExtractor createReferenceExtractor() {
@@ -231,7 +231,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
private List extractReferenceParamsAsQueryTokens(RuntimeSearchParam theSearchParam, IBaseResource theResource, IExtractor theExtractor) {
SearchParamSet params = new SearchParamSet<>();
- extractSearchParam(theSearchParam, theResource, theExtractor, params);
+ extractSearchParam(theSearchParam, theResource, theExtractor, params, false);
return refsToStringList(params);
}
@@ -244,7 +244,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
private List extractParamsAsQueryTokens(RuntimeSearchParam theSearchParam, IBaseResource theResource, IExtractor theExtractor) {
SearchParamSet params = new SearchParamSet<>();
- extractSearchParam(theSearchParam, theResource, theExtractor, params);
+ extractSearchParam(theSearchParam, theResource, theExtractor, params, false);
return toStringList(params);
}
@@ -257,14 +257,14 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
@Override
public SearchParamSet extractSearchParamTokens(IBaseResource theResource) {
IExtractor extractor = createTokenExtractor(theResource);
- return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.TOKEN);
+ return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.TOKEN, false);
}
@Override
public SearchParamSet extractSearchParamTokens(IBaseResource theResource, RuntimeSearchParam theSearchParam) {
IExtractor extractor = createTokenExtractor(theResource);
SearchParamSet setToPopulate = new SearchParamSet<>();
- extractSearchParam(theSearchParam, theResource, extractor, setToPopulate);
+ extractSearchParam(theSearchParam, theResource, extractor, setToPopulate, false);
return setToPopulate;
}
@@ -293,11 +293,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
public SearchParamSet extractSearchParamSpecial(IBaseResource theResource) {
String resourceTypeName = toRootTypeName(theResource);
IExtractor extractor = createSpecialExtractor(resourceTypeName);
- return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.SPECIAL);
+ return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.SPECIAL, false);
}
private IExtractor createSpecialExtractor(String theResourceTypeName) {
- return (params, searchParam, value, path) -> {
+ return (params, searchParam, value, path, theWantLocalReferences) -> {
if (COORDS_INDEX_PATHS.contains(path)) {
addCoords_Position(theResourceTypeName, params, searchParam, value);
}
@@ -311,11 +311,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
@Override
public SearchParamSet extractSearchParamUri(IBaseResource theResource) {
IExtractor extractor = createUriExtractor(theResource);
- return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.URI);
+ return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.URI, false);
}
private IExtractor createUriExtractor(IBaseResource theResource) {
- return (params, searchParam, value, path) -> {
+ return (params, searchParam, value, path, theWantLocalReferences) -> {
String nextType = toRootTypeName(value);
String resourceType = toRootTypeName(theResource);
switch (nextType) {
@@ -336,7 +336,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
@Override
public SearchParamSet extractSearchParamDates(IBaseResource theResource) {
IExtractor extractor = createDateExtractor(theResource);
- return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.DATE);
+ return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.DATE, false);
}
private IExtractor createDateExtractor(IBaseResource theResource) {
@@ -346,17 +346,17 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
@Override
public Date extractDateFromResource(IBase theValue, String thePath) {
DateExtractor extractor = new DateExtractor("DateType");
- return extractor.get(theValue, thePath).getValueHigh();
+ return extractor.get(theValue, thePath, false).getValueHigh();
}
@Override
public SearchParamSet extractSearchParamNumber(IBaseResource theResource) {
IExtractor extractor = createNumberExtractor(theResource);
- return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.NUMBER);
+ return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.NUMBER, false);
}
private IExtractor createNumberExtractor(IBaseResource theResource) {
- return (params, searchParam, value, path) -> {
+ return (params, searchParam, value, path, theWantLocalReferences) -> {
String nextType = toRootTypeName(value);
String resourceType = toRootTypeName(theResource);
switch (nextType) {
@@ -384,18 +384,18 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
@Override
public SearchParamSet extractSearchParamQuantity(IBaseResource theResource) {
IExtractor extractor = createQuantityExtractor(theResource);
- return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.QUANTITY);
+ return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.QUANTITY, false);
}
@Override
public SearchParamSet extractSearchParamQuantityNormalized(IBaseResource theResource) {
IExtractor extractor = createQuantityNormalizedExtractor(theResource);
- return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.QUANTITY);
+ return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.QUANTITY, false);
}
private IExtractor createQuantityExtractor(IBaseResource theResource) {
- return (params, searchParam, value, path) -> {
+ return (params, searchParam, value, path, theWantLocalReferences) -> {
if (value.getClass().equals(myLocationPositionDefinition.getImplementingClass())) {
return;
}
@@ -421,7 +421,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
private IExtractor createQuantityNormalizedExtractor(IBaseResource theResource) {
- return (params, searchParam, value, path) -> {
+ return (params, searchParam, value, path, theWantLocalReferences) -> {
if (value.getClass().equals(myLocationPositionDefinition.getImplementingClass())) {
return;
}
@@ -449,11 +449,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
public SearchParamSet extractSearchParamStrings(IBaseResource theResource) {
IExtractor extractor = createStringExtractor(theResource);
- return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.STRING);
+ return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.STRING, false);
}
private IExtractor createStringExtractor(IBaseResource theResource) {
- return (params, searchParam, value, path) -> {
+ return (params, searchParam, value, path, theWantLocalReferences) -> {
String resourceType = toRootTypeName(theResource);
if (value instanceof IPrimitiveType) {
@@ -934,7 +934,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
- private SearchParamSet extractSearchParams(IBaseResource theResource, IExtractor theExtractor, RestSearchParameterTypeEnum theSearchParamType) {
+ private SearchParamSet extractSearchParams(IBaseResource theResource, IExtractor theExtractor, RestSearchParameterTypeEnum theSearchParamType, boolean theWantLocalReferences) {
SearchParamSet retVal = new SearchParamSet<>();
Collection searchParams = getSearchParams(theResource);
@@ -943,12 +943,12 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
continue;
}
- extractSearchParam(nextSpDef, theResource, theExtractor, retVal);
+ extractSearchParam(nextSpDef, theResource, theExtractor, retVal, theWantLocalReferences);
}
return retVal;
}
- private void extractSearchParam(RuntimeSearchParam theSearchParameterDef, IBaseResource theResource, IExtractor theExtractor, SearchParamSet theSetToPopulate) {
+ private void extractSearchParam(RuntimeSearchParam theSearchParameterDef, IBaseResource theResource, IExtractor theExtractor, SearchParamSet theSetToPopulate, boolean theWantLocalReferences) {
String nextPathUnsplit = theSearchParameterDef.getPath();
if (isBlank(nextPathUnsplit)) {
return;
@@ -961,7 +961,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
if (nextObject != null) {
String typeName = toRootTypeName(nextObject);
if (!myIgnoredForSearchDatatypes.contains(typeName)) {
- theExtractor.extract(theSetToPopulate, theSearchParameterDef, nextObject, nextPath);
+ theExtractor.extract(theSetToPopulate, theSearchParameterDef, nextObject, nextPath, theWantLocalReferences);
}
}
}
@@ -1181,7 +1181,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
@FunctionalInterface
private interface IExtractor {
- void extract(SearchParamSet theParams, RuntimeSearchParam theSearchParam, IBase theValue, String thePath);
+ void extract(SearchParamSet theParams, RuntimeSearchParam theSearchParam, IBase theValue, String thePath, boolean theWantLocalReferences);
}
@@ -1196,9 +1196,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
@Override
- public void extract(SearchParamSet theParams, RuntimeSearchParam theSearchParam, IBase theValue, String thePath) {
- myExtractor0.extract(theParams, theSearchParam, theValue, thePath);
- myExtractor1.extract(theParams, theSearchParam, theValue, thePath);
+ public void extract(SearchParamSet theParams, RuntimeSearchParam theSearchParam, IBase theValue, String thePath, boolean theWantLocalReferences) {
+ myExtractor0.extract(theParams, theSearchParam, theValue, thePath, theWantLocalReferences);
+ myExtractor1.extract(theParams, theSearchParam, theValue, thePath, theWantLocalReferences);
}
}
@@ -1207,7 +1207,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
private PathAndRef myPathAndRef = null;
@Override
- public void extract(SearchParamSet theParams, RuntimeSearchParam theSearchParam, IBase theValue, String thePath) {
+ public void extract(SearchParamSet theParams, RuntimeSearchParam theSearchParam, IBase theValue, String thePath, boolean theWantLocalReferences) {
if (theValue instanceof IBaseResource) {
return;
}
@@ -1257,10 +1257,13 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
if (nextId == null ||
- nextId.isEmpty() ||
- nextId.getValue().startsWith("#") ||
- nextId.getValue().startsWith("urn:")) {
- return;
+ nextId.isEmpty() ||
+ nextId.getValue().startsWith("urn:")) {
+ return;
+ }
+ if (!theWantLocalReferences) {
+ if (nextId.getValue().startsWith("#"))
+ return;
}
myPathAndRef = new PathAndRef(theSearchParam.getName(), thePath, valueRef, false);
@@ -1275,7 +1278,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
public PathAndRef get(IBase theValue, String thePath) {
extract(new SearchParamSet<>(),
new RuntimeSearchParam(null, null, "Reference", null, null, null, null, null, null, null),
- theValue, thePath);
+ theValue, thePath, false);
return myPathAndRef;
}
}
@@ -1294,7 +1297,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
@Override
- public void extract(SearchParamSet theParams, RuntimeSearchParam theSearchParam, IBase theValue, String thePath) {
+ public void extract(SearchParamSet theParams, RuntimeSearchParam theSearchParam, IBase theValue, String thePath, boolean theWantLocalReferences) {
String nextType = toRootTypeName(theValue);
switch (nextType) {
case "date":
@@ -1389,10 +1392,10 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
}
- public ResourceIndexedSearchParamDate get(IBase theValue, String thePath) {
+ public ResourceIndexedSearchParamDate get(IBase theValue, String thePath, boolean theWantLocalReferences) {
extract(new SearchParamSet<>(),
new RuntimeSearchParam(null, null, "date", null, null, null, null, null, null, null),
- theValue, thePath);
+ theValue, thePath, theWantLocalReferences);
return myIndexedSearchParamDate;
}
}
@@ -1407,7 +1410,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
@Override
- public void extract(SearchParamSet params, RuntimeSearchParam searchParam, IBase value, String path) {
+ public void extract(SearchParamSet params, RuntimeSearchParam searchParam, IBase value, String path, boolean theWantLocalReferences) {
// DSTU3+
if (value instanceof IBaseEnumeration>) {
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ISearchParamExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ISearchParamExtractor.java
index 891e2dd9202..2871503d11e 100644
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ISearchParamExtractor.java
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ISearchParamExtractor.java
@@ -61,7 +61,7 @@ public interface ISearchParamExtractor {
SearchParamSet extractSearchParamUri(IBaseResource theResource);
- SearchParamSet extractResourceLinks(IBaseResource theResource);
+ SearchParamSet extractResourceLinks(IBaseResource theResource, boolean theWantLocalReferences);
String[] split(String theExpression);
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParams.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParams.java
index 656a3cbb50a..bfaaa306b59 100644
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParams.java
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParams.java
@@ -42,9 +42,7 @@ import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.ReferenceParam;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import com.google.common.collect.Streams;
+
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
@@ -57,7 +55,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate;
-import java.util.stream.Stream;
+import javax.annotation.Nonnull;
import static org.apache.commons.lang3.StringUtils.compare;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@@ -144,6 +142,17 @@ public final class ResourceIndexedSearchParams {
theEntity.setResourceLinks(myLinks);
}
+ public void updateSpnamePrefixForIndexedOnContainedResource(String theSpnamePrefix) {
+ updateSpnamePrefixForIndexedOnContainedResource(myNumberParams, theSpnamePrefix);
+ updateSpnamePrefixForIndexedOnContainedResource(myQuantityParams, theSpnamePrefix);
+ updateSpnamePrefixForIndexedOnContainedResource(myQuantityNormalizedParams, theSpnamePrefix);
+ updateSpnamePrefixForIndexedOnContainedResource(myDateParams, theSpnamePrefix);
+ updateSpnamePrefixForIndexedOnContainedResource(myUriParams, theSpnamePrefix);
+ updateSpnamePrefixForIndexedOnContainedResource(myTokenParams, theSpnamePrefix);
+ updateSpnamePrefixForIndexedOnContainedResource(myStringParams, theSpnamePrefix);
+ updateSpnamePrefixForIndexedOnContainedResource(myCoordsParams, theSpnamePrefix);
+ }
+
void setUpdatedTime(Date theUpdateTime) {
setUpdatedTime(myStringParams, theUpdateTime);
setUpdatedTime(myNumberParams, theUpdateTime);
@@ -161,6 +170,14 @@ public final class ResourceIndexedSearchParams {
}
}
+ private void updateSpnamePrefixForIndexedOnContainedResource(Collection extends BaseResourceIndexedSearchParam> theParams, @Nonnull String theSpnamePrefix) {
+
+ for (BaseResourceIndexedSearchParam param : theParams) {
+ param.setParamName(theSpnamePrefix + "." + param.getParamName());
+ param.calculateHashes(); // re-calculuteHashes
+ }
+ }
+
public Set getPopulatedResourceLinkParameters() {
return myPopulatedResourceLinkParameters;
}
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java
index a3247d7e74b..cd0da9978b9 100644
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java
@@ -52,6 +52,10 @@ import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
+import ca.uhn.fhir.util.FhirTerser;
+import ca.uhn.fhir.util.ResourceReferenceInfo;
+import ca.uhn.fhir.util.StringUtil;
+
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseReference;
@@ -96,14 +100,87 @@ public class SearchParamExtractorService {
IBaseResource resource = normalizeResource(theResource);
// All search parameter types except Reference
- extractSearchIndexParameters(theRequestDetails, theParams, resource, theEntity);
+ ResourceIndexedSearchParams normalParams = new ResourceIndexedSearchParams();
+ extractSearchIndexParameters(theRequestDetails, normalParams, resource, theEntity);
+ mergeParams(normalParams, theParams);
+ if (myModelConfig.isIndexOnContainedResources()) {
+ ResourceIndexedSearchParams containedParams = new ResourceIndexedSearchParams();
+ extractSearchIndexParametersForContainedResources(theRequestDetails, containedParams, resource, theEntity);
+ mergeParams(containedParams, theParams);
+ }
+
+ // Do this after, because we add to strings during both string and token processing, and contained resource if any
+ populateResourceTables(theParams, theEntity);
+
// Reference search parameters
extractResourceLinks(theRequestPartitionId, theParams, theEntity, resource, theTransactionDetails, theFailOnInvalidReference, theRequestDetails);
theParams.setUpdatedTime(theTransactionDetails.getTransactionDate());
}
+ private void extractSearchIndexParametersForContainedResources(RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, IBaseResource theResource, ResourceTable theEntity) {
+
+ FhirTerser terser = myContext.newTerser();
+
+ // 1. get all contained resources
+ Collection containedResources = terser.getAllEmbeddedResources(theResource, false);
+
+ // 2. Find referenced search parameters
+ ISearchParamExtractor.SearchParamSet referencedSearchParamSet = mySearchParamExtractor.extractResourceLinks(theResource, true);
+
+ String spnamePrefix = null;
+ ResourceIndexedSearchParams currParams;
+ // 3. for each referenced search parameter, create an index
+ for (PathAndRef nextPathAndRef : referencedSearchParamSet) {
+
+ // 3.1 get the search parameter name as spname prefix
+ spnamePrefix = nextPathAndRef.getSearchParamName();
+
+ if (spnamePrefix == null || nextPathAndRef.getRef() == null)
+ continue;
+
+ // 3.2 find the contained resource
+ IBaseResource containedResource = findContainedResource(containedResources, nextPathAndRef.getRef());
+ if (containedResource == null)
+ continue;
+
+ currParams = new ResourceIndexedSearchParams();
+
+ // 3.3 create indexes for the current contained resource
+ extractSearchIndexParameters(theRequestDetails, currParams, containedResource, theEntity);
+
+ // 3.4 added reference name as a prefix for the contained resource if any
+ // e.g. for Observation.subject contained reference
+ // the SP_NAME = subject.family
+ currParams.updateSpnamePrefixForIndexedOnContainedResource(spnamePrefix);
+
+ // 3.5 merge to the mainParams
+ // NOTE: the spname prefix is different
+ mergeParams(currParams, theParams);
+ }
+ }
+
+ private IBaseResource findContainedResource(Collection resources, IBaseReference reference) {
+ for (IBaseResource resource : resources) {
+ if (resource.getIdElement().equals(reference.getReferenceElement()))
+ return resource;
+ }
+ return null;
+ }
+
+ private void mergeParams(ResourceIndexedSearchParams theSrcParams, ResourceIndexedSearchParams theTargetParams) {
+
+ theTargetParams.myNumberParams.addAll(theSrcParams.myNumberParams);
+ theTargetParams.myQuantityParams.addAll(theSrcParams.myQuantityParams);
+ theTargetParams.myQuantityNormalizedParams.addAll(theSrcParams.myQuantityNormalizedParams);
+ theTargetParams.myDateParams.addAll(theSrcParams.myDateParams);
+ theTargetParams.myUriParams.addAll(theSrcParams.myUriParams);
+ theTargetParams.myTokenParams.addAll(theSrcParams.myTokenParams);
+ theTargetParams.myStringParams.addAll(theSrcParams.myStringParams);
+ theTargetParams.myCoordsParams.addAll(theSrcParams.myCoordsParams);
+ }
+
private void extractSearchIndexParameters(RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, IBaseResource theResource, ResourceTable theEntity) {
// Strings
@@ -156,7 +233,10 @@ public class SearchParamExtractorService {
}
}
- // Do this after, because we add to strings during both string and token processing
+ }
+
+ private void populateResourceTables(ResourceIndexedSearchParams theParams, ResourceTable theEntity) {
+
populateResourceTable(theParams.myNumberParams, theEntity);
populateResourceTable(theParams.myQuantityParams, theEntity);
populateResourceTable(theParams.myQuantityNormalizedParams, theEntity);
@@ -165,7 +245,6 @@ public class SearchParamExtractorService {
populateResourceTable(theParams.myTokenParams, theEntity);
populateResourceTable(theParams.myStringParams, theEntity);
populateResourceTable(theParams.myCoordsParams, theEntity);
-
}
/**
@@ -186,7 +265,7 @@ public class SearchParamExtractorService {
private void extractResourceLinks(RequestPartitionId theRequestPartitionId, ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, TransactionDetails theTransactionDetails, boolean theFailOnInvalidReference, RequestDetails theRequest) {
String resourceName = myContext.getResourceType(theResource);
- ISearchParamExtractor.SearchParamSet refs = mySearchParamExtractor.extractResourceLinks(theResource);
+ ISearchParamExtractor.SearchParamSet refs = mySearchParamExtractor.extractResourceLinks(theResource, false);
SearchParamExtractorService.handleWarnings(theRequest, myInterceptorBroadcaster, refs);
for (PathAndRef nextPathAndRef : refs) {
From be50a46d7639b77aab4a1de27be59e550b4ea811 Mon Sep 17 00:00:00 2001
From: James Agnew
Date: Tue, 9 Mar 2021 11:51:30 -0500
Subject: [PATCH 6/6] Tweaks to contained searches (#2461)
* Tweaks to contained searches
* Add changelog
* Build fix
---
.../java/ca/uhn/fhir/rest/api/Constants.java | 1 +
.../rest/api/SearchContainedModeEnum.java | 76 +++
.../ca/uhn/fhir/jpa/demo/CommonConfig.java | 4 -
.../uhn/fhir/jpa/demo/FhirServerConfig.java | 4 -
.../5_4_0/support-contained-searches.yaml | 6 +
.../ca/uhn/fhir/jpa/api/config/DaoConfig.java | 94 ----
.../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 17 +-
.../fhir/jpa/search/builder/QueryStack.java | 8 +-
.../jpa/search/builder/SearchBuilder.java | 4 +-
.../ResourceLinkPredicateBuilder.java | 4 +-
.../fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java | 3 -
.../fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java | 3 -
.../FhirResourceDaoDstu3ContainedTest.java | 22 +-
.../ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java | 6 -
.../r4/FhirResourceDaoR4ContainedTest.java | 17 +-
...urceDaoR4SearchWithLuceneDisabledTest.java | 11 +-
.../ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java | 1 -
.../provider/ResourceProviderDstu2Test.java | 3 -
...ResourceProviderR4SearchContainedTest.java | 502 +++++++++---------
.../RestHookWithEventDefinitionR4Test.java | 17 +-
.../WebsocketWithSubscriptionIdDstu3Test.java | 12 +-
.../fhir/jpa/model/entity/ModelConfig.java | 10 +-
.../jpa/searchparam/SearchContainedEnum.java | 40 --
.../jpa/searchparam/SearchParameterMap.java | 13 +-
.../uhn/fhirtest/config/TestDstu2Config.java | 1 +
.../uhn/fhirtest/config/TestDstu3Config.java | 1 +
.../ca/uhn/fhirtest/config/TestR4Config.java | 1 +
.../ca/uhn/fhirtest/config/TestR5Config.java | 1 +
.../server/interceptor/auth/RuleBuilder.java | 5 +-
.../fhir/rest/server/method/MethodUtil.java | 2 +
.../method/SearchContainedModeParameter.java | 34 ++
.../resources/vm/jpa_resource_provider.vm | 6 +-
32 files changed, 448 insertions(+), 481 deletions(-)
create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/SearchContainedModeEnum.java
create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/support-contained-searches.yaml
delete mode 100644 hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchContainedEnum.java
create mode 100644 hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchContainedModeParameter.java
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 1961ded8c12..93d957299d2 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
@@ -267,6 +267,7 @@ public class Constants {
*/
public static final String RESOURCE_PARTITION_ID = Constants.class.getName() + "_RESOURCE_PARTITION_ID";
public static final String CT_APPLICATION_GZIP = "application/gzip";
+ public static final String[] EMPTY_STRING_ARRAY = new String[0];
static {
CHARSET_UTF8 = StandardCharsets.UTF_8;
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/SearchContainedModeEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/SearchContainedModeEnum.java
new file mode 100644
index 00000000000..526726f352a
--- /dev/null
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/SearchContainedModeEnum.java
@@ -0,0 +1,76 @@
+package ca.uhn.fhir.rest.api;
+
+/*
+ * #%L
+ * HAPI FHIR Search Parameters
+ * %%
+ * Copyright (C) 2014 - 2021 Smile CDR, Inc.
+ * %%
+ * 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 ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
+import ca.uhn.fhir.util.UrlUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum SearchContainedModeEnum {
+
+ /**
+ * default, search on the non-contained (normal) resources
+ */
+ FALSE("false"),
+
+ /**
+ * search on the contained resources only
+ */
+ TRUE("true"),
+
+ /**
+ * Search on the normal resources and contained resources.
+ * This option is not supported yet.
+ */
+ BOTH("both");
+
+ private static volatile Map ourCodeToEnum;
+ private final String myCode;
+
+ SearchContainedModeEnum(String theCode) {
+ myCode = theCode;
+ }
+
+ private String getCode() {
+ return myCode;
+ }
+
+ public static SearchContainedModeEnum fromCode(String theCode) {
+ Map codeToEnum = ourCodeToEnum;
+ if (codeToEnum == null) {
+ codeToEnum = new HashMap<>();
+ for (SearchContainedModeEnum next : values()) {
+ codeToEnum.put(next.getCode(), next);
+ }
+ ourCodeToEnum = codeToEnum;
+ }
+
+ SearchContainedModeEnum retVal = codeToEnum.get(theCode);
+ if (retVal == null) {
+ throw new InvalidRequestException("Invalid contained mode: " + UrlUtil.sanitizeUrlPart(theCode));
+ }
+
+ return retVal;
+ }
+
+}
diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/CommonConfig.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/CommonConfig.java
index b0668955161..9e3463aa160 100644
--- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/CommonConfig.java
+++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/CommonConfig.java
@@ -25,7 +25,6 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer;
import org.apache.commons.dbcp2.BasicDataSource;
-import org.apache.commons.lang3.time.DateUtils;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings;
import org.hibernate.search.backend.lucene.cfg.LuceneIndexSettings;
@@ -49,9 +48,6 @@ public class CommonConfig {
@Bean
public DaoConfig daoConfig() {
DaoConfig retVal = new DaoConfig();
- retVal.setSubscriptionEnabled(true);
- retVal.setSubscriptionPollDelay(5000);
- retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
retVal.setAllowMultipleDelete(true);
return retVal;
}
diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
index ec7b0546280..ee145f02902 100644
--- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
+++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
@@ -59,9 +59,6 @@ public class FhirServerConfig extends BaseJavaConfigDstu2 {
@Bean
public DaoConfig daoConfig() {
DaoConfig retVal = new DaoConfig();
- retVal.setSubscriptionEnabled(true);
- retVal.setSubscriptionPollDelay(5000);
- retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
retVal.setAllowMultipleDelete(true);
return retVal;
}
@@ -79,7 +76,6 @@ public class FhirServerConfig extends BaseJavaConfigDstu2 {
/**
* Do some fancy logging to create a nice access log that has details about each incoming request.
- * @return
*/
public LoggingInterceptor loggingInterceptor() {
LoggingInterceptor retVal = new LoggingInterceptor();
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/support-contained-searches.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/support-contained-searches.yaml
new file mode 100644
index 00000000000..d5d13a6c90e
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_4_0/support-contained-searches.yaml
@@ -0,0 +1,6 @@
+---
+type: add
+issue: 2441
+title: "Support has been added to the JPA server for indexing and searching using the `_contained` parameter, which
+ allows searching using chained parameters that chain into contained resources. This feature is disabled by default
+ but can be enabled via a setting on the ModelConfig object."
diff --git a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java
index 97847d2ac44..d45156245b0 100644
--- a/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java
+++ b/hapi-fhir-jpaserver-api/src/main/java/ca/uhn/fhir/jpa/api/config/DaoConfig.java
@@ -118,11 +118,6 @@ public class DaoConfig {
* update setter javadoc if default changes
*/
private Integer myFetchSizeDefaultMaximum = null;
- private int myHardTagListLimit = 1000;
- /**
- * update setter javadoc if default changes
- */
- private boolean myIndexContainedResources = true;
private int myMaximumExpansionSize = DEFAULT_MAX_EXPANSION_SIZE;
private Integer myMaximumSearchResultCountInTransaction = DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION;
@@ -209,9 +204,6 @@ public class DaoConfig {
* Constructor
*/
public DaoConfig() {
- setSubscriptionEnabled(true);
- setSubscriptionPollDelay(0);
- setSubscriptionPurgeInactiveAfterMillis(Long.MAX_VALUE);
setMarkResourcesForReindexingUponSearchParameterChange(true);
setReindexThreadCount(Runtime.getRuntime().availableProcessors());
setExpungeThreadCount(Runtime.getRuntime().availableProcessors());
@@ -580,20 +572,6 @@ public class DaoConfig {
myFetchSizeDefaultMaximum = theFetchSizeDefaultMaximum;
}
- /**
- * Gets the maximum number of results to return in a GetTags query (DSTU1 only)
- */
- public int getHardTagListLimit() {
- return myHardTagListLimit;
- }
-
- /**
- * Gets the maximum number of results to return in a GetTags query (DSTU1 only)
- */
- public void setHardTagListLimit(int theHardTagListLimit) {
- myHardTagListLimit = theHardTagListLimit;
- }
-
/**
* If set to {@link IndexEnabledEnum#DISABLED} (default is {@link IndexEnabledEnum#DISABLED})
* the server will not create search indexes for search parameters with no values in resources.
@@ -1442,22 +1420,6 @@ public class DaoConfig {
myExpungeBatchSize = theExpungeBatchSize;
}
- /**
- * Should contained IDs be indexed the same way that non-contained IDs are (default is
- * true
)
- */
- public boolean isIndexContainedResources() {
- return myIndexContainedResources;
- }
-
- /**
- * Should contained IDs be indexed the same way that non-contained IDs are (default is
- * true
)
- */
- public void setIndexContainedResources(boolean theIndexContainedResources) {
- myIndexContainedResources = theIndexContainedResources;
- }
-
/**
* Should resources be marked as needing reindexing when a
* SearchParameter resource is added or changed. This should generally
@@ -1591,62 +1553,6 @@ public class DaoConfig {
myValidateSearchParameterExpressionsOnSave = theValidateSearchParameterExpressionsOnSave;
}
- /**
- * Do not call this method, it exists only for legacy reasons. It
- * will be removed in a future version. Configure the page size on your
- * paging provider instead.
- *
- * @deprecated This method does not do anything. Configure the page size on your
- * paging provider instead. Deprecated in HAPI FHIR 2.3 (Jan 2017)
- */
- @Deprecated
- public void setHardSearchLimit(int theHardSearchLimit) {
- // this method does nothing
- }
-
- /**
- * This is the maximum number of resources that will be added to a single page of returned resources. Because of
- * includes with wildcards and other possibilities it is possible for a client to make requests that include very
- * large amounts of data, so this hard limit can be imposed to prevent runaway requests.
- *
- * @deprecated Deprecated in HAPI FHIR 3.2.0 as this method doesn't actually do anything
- */
- @Deprecated
- public void setIncludeLimit(@SuppressWarnings("unused") int theIncludeLimit) {
- // nothing
- }
-
- /**
- * @deprecated As of HAPI FHIR 3.0.0, subscriptions no longer use polling for
- * detecting changes, so this setting has no effect
- */
- @Deprecated
- public void setSubscriptionEnabled(boolean theSubscriptionEnabled) {
- // nothing
- }
-
- /**
- * @deprecated As of HAPI FHIR 3.0.0, subscriptions no longer use polling for
- * detecting changes, so this setting has no effect
- */
- @Deprecated
- public void setSubscriptionPollDelay(long theSubscriptionPollDelay) {
- // ignore
- }
-
- /**
- * @deprecated As of HAPI FHIR 3.0.0, subscriptions no longer use polling for
- * detecting changes, so this setting has no effect
- */
- @Deprecated
- public void setSubscriptionPurgeInactiveAfterMillis(Long theMillis) {
- // ignore
- }
-
- public void setSubscriptionPurgeInactiveAfterSeconds(int theSeconds) {
- setSubscriptionPurgeInactiveAfterMillis(theSeconds * DateUtils.MILLIS_PER_SECOND);
- }
-
/**
* This setting sets the number of search results to prefetch. For example, if this list
* is set to [100, 1000, -1] then the server will initially load 100 results and not
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 f4288f05d8c..2dcae91118e 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
@@ -54,7 +54,7 @@ import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
import ca.uhn.fhir.jpa.search.cache.SearchCacheStatusEnum;
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
-import ca.uhn.fhir.jpa.searchparam.SearchContainedEnum;
+import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.model.api.IQueryParameterType;
@@ -1292,16 +1292,11 @@ public abstract class BaseHapiFhirResourceDao extends B
@Override
public IBundleProvider search(final SearchParameterMap theParams, RequestDetails theRequest, HttpServletResponse theServletResponse) {
- if (theRequest != null) {
- String[] contained = theRequest.getParameters().get(Constants.PARAM_CONTAINED);
- if (contained != null && contained.length > 0) {
- if (contained[0].equals("true")) {
- theParams.setSearchContainedMode(SearchContainedEnum.TRUE);
- ourLog.info("Search on contained resources only");
- } else if (contained[0].equals("both")) {
- ourLog.warn("Search on both normal resources and contained resources are not support. set to default search on normal resources");
- }
- }
+ if (theParams.getSearchContainedMode() == SearchContainedModeEnum.BOTH) {
+ throw new MethodNotAllowedException("Contained mode 'both' is not currently supported");
+ }
+ if (theParams.getSearchContainedMode() != SearchContainedModeEnum.FALSE && !myModelConfig.isIndexOnContainedResources()) {
+ throw new MethodNotAllowedException("Searching with _contained mode enabled is not enabled on this server");
}
if (myDaoConfig.getIndexMissingFields() == DaoConfig.IndexEnabledEnum.DISABLED) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java
index a63f8b38c40..fb3489bb890 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java
@@ -52,7 +52,7 @@ import ca.uhn.fhir.jpa.search.builder.predicate.TokenPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.UriPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
-import ca.uhn.fhir.jpa.searchparam.SearchContainedEnum;
+import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
import ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.searchparam.util.SourceParam;
@@ -561,7 +561,7 @@ public class QueryStack {
List paths = join.createResourceLinkPaths(targetResourceType, paramReference);
Condition typePredicate = BinaryCondition.equalTo(join.getColumnTargetResourceType(), mySqlBuilder.generatePlaceholder(theResourceType));
Condition pathPredicate = toEqualToOrInPredicate(join.getColumnSourcePath(), mySqlBuilder.generatePlaceholders(paths));
- Condition linkedPredicate = searchForIdsWithAndOr(join.getColumnSrcResourceId(), targetResourceType, parameterName, Collections.singletonList(orValues), theRequest, theRequestPartitionId, SearchContainedEnum.FALSE);
+ Condition linkedPredicate = searchForIdsWithAndOr(join.getColumnSrcResourceId(), targetResourceType, parameterName, Collections.singletonList(orValues), theRequest, theRequestPartitionId, SearchContainedModeEnum.FALSE);
andPredicates.add(toAndPredicate(partitionPredicate, pathPredicate, typePredicate, linkedPredicate));
}
@@ -1046,7 +1046,7 @@ public class QueryStack {
}
@Nullable
- public Condition searchForIdsWithAndOr(@Nullable DbColumn theSourceJoinColumn, String theResourceName, String theParamName, List> theAndOrParams, RequestDetails theRequest, RequestPartitionId theRequestPartitionId, SearchContainedEnum theSearchContainedMode) {
+ public Condition searchForIdsWithAndOr(@Nullable DbColumn theSourceJoinColumn, String theResourceName, String theParamName, List> theAndOrParams, RequestDetails theRequest, RequestPartitionId theRequestPartitionId, SearchContainedModeEnum theSearchContainedMode) {
if (theAndOrParams.isEmpty()) {
return null;
@@ -1108,7 +1108,7 @@ public class QueryStack {
break;
case REFERENCE:
for (List extends IQueryParameterType> nextAnd : theAndOrParams) {
- if (theSearchContainedMode.equals(SearchContainedEnum.TRUE))
+ if (theSearchContainedMode.equals(SearchContainedModeEnum.TRUE))
andPredicates.add(createPredicateReferenceForContainedResource(theSourceJoinColumn, theResourceName, theParamName, nextParamDef, nextAnd, null, theRequest, theRequestPartitionId));
else
andPredicates.add(createPredicateReference(theSourceJoinColumn, theResourceName, theParamName, nextAnd, null, theRequest, theRequestPartitionId));
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java
index 0d5a086bea8..db28da9ce88 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java
@@ -60,7 +60,7 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.searchparam.util.Dstu3DistanceHelper;
import ca.uhn.fhir.jpa.searchparam.util.LastNParameterHelper;
-import ca.uhn.fhir.jpa.searchparam.SearchContainedEnum;
+import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
import ca.uhn.fhir.jpa.util.BaseIterator;
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
@@ -216,7 +216,7 @@ public class SearchBuilder implements ISearchBuilder {
attemptCompositeUniqueSpProcessing(theQueryStack, theParams, theRequest);
}
- SearchContainedEnum searchContainedMode = theParams.getSearchContainedMode();
+ SearchContainedModeEnum searchContainedMode = theParams.getSearchContainedMode();
// Handle each parameter
List paramNames = new ArrayList<>(myParams.keySet());
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java
index c64e43ae503..f37a3e428f8 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java
@@ -43,7 +43,7 @@ import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
-import ca.uhn.fhir.jpa.searchparam.SearchContainedEnum;
+import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
import ca.uhn.fhir.model.api.IQueryParameterType;
@@ -395,7 +395,7 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder {
List andPredicates = new ArrayList<>();
List> chainParamValues = Collections.singletonList(orValues);
- andPredicates.add(childQueryFactory.searchForIdsWithAndOr(myColumnTargetResourceId, subResourceName, chain, chainParamValues, theRequest, theRequestPartitionId, SearchContainedEnum.FALSE));
+ andPredicates.add(childQueryFactory.searchForIdsWithAndOr(myColumnTargetResourceId, subResourceName, chain, chainParamValues, theRequest, theRequestPartitionId, SearchContainedModeEnum.FALSE));
orPredicates.add(toAndPredicate(andPredicates));
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java
index f530dbaaabe..87ad9c1383b 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java
@@ -244,9 +244,6 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
@BeforeEach
public void beforeResetConfig() {
- myDaoConfig.setHardSearchLimit(1000);
- myDaoConfig.setHardTagListLimit(1000);
- myDaoConfig.setIncludeLimit(2000);
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java
index a33280e97ce..3680f0247a5 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java
@@ -396,9 +396,6 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
@BeforeEach
public void beforeResetConfig() {
- myDaoConfig.setHardSearchLimit(1000);
- myDaoConfig.setHardTagListLimit(1000);
- myDaoConfig.setIncludeLimit(2000);
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ContainedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ContainedTest.java
index c9a20d333fe..8319ec16bd6 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ContainedTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ContainedTest.java
@@ -3,12 +3,10 @@ package ca.uhn.fhir.jpa.dao.dstu3;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.TokenParamModifier;
-import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.dstu3.model.Observation;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Reference;
import org.hl7.fhir.instance.model.api.IIdType;
-import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -18,23 +16,17 @@ public class FhirResourceDaoDstu3ContainedTest extends BaseJpaDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3ContainedTest.class);
-
- @Test
- public void before() {
- myDaoConfig.setIndexContainedResources(true);
- }
-
@Test
public void testIndexContained() {
Patient p = new Patient();
p.setId("#some_patient");
p.addName().setFamily("MYFAMILY").addGiven("MYGIVEN");
-
+
Observation o1 = new Observation();
o1.getCode().setText("Some Observation");
o1.setSubject(new Reference(p));
IIdType oid1 = myObservationDao.create(o1, mySrd).getId().toUnqualifiedVersionless();
-
+
Observation o2 = new Observation();
o2.getCode().setText("Some Observation");
o2.setSubject(new Reference(p));
@@ -43,16 +35,16 @@ public class FhirResourceDaoDstu3ContainedTest extends BaseJpaDstu3Test {
Patient p2 = new Patient();
p2.addName().setFamily("MYFAMILY").addGiven("MYGIVEN");
IIdType pid2 = myPatientDao.create(p2, mySrd).getId().toUnqualifiedVersionless();
-
+
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(o2));
-
-
+
+
SearchParameterMap map = new SearchParameterMap();
map.add(Observation.SP_CODE, new TokenParam(null, "some observation").setModifier(TokenParamModifier.TEXT));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(oid1, oid2)));
}
-
+
// TODO: make sure match URLs don't delete
-
+
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java
index 6eaa3bf9027..bfbf2dfbe8f 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java
@@ -476,7 +476,6 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
@Autowired
private IValidationSupport myJpaValidationSupportChainR4;
private PerformanceTracingLoggingInterceptor myPerformanceTracingLoggingInterceptor;
- private List mySystemInterceptors;
@Autowired
private IBulkDataExportSvc myBulkDataExportSvc;
@Autowired
@@ -524,8 +523,6 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
@BeforeEach
public void beforeCreateInterceptor() {
- mySystemInterceptors = myInterceptorRegistry.getAllRegisteredInterceptors();
-
myInterceptor = mock(IServerInterceptor.class);
myPerformanceTracingLoggingInterceptor = new PerformanceTracingLoggingInterceptor();
@@ -558,9 +555,6 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
@BeforeEach
public void beforeResetConfig() {
- myDaoConfig.setHardSearchLimit(1000);
- myDaoConfig.setHardTagListLimit(1000);
- myDaoConfig.setIncludeLimit(2000);
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
myValidationSettings.setLocalReferenceValidationDefaultPolicy(new ValidationSettings().getLocalReferenceValidationDefaultPolicy());
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ContainedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ContainedTest.java
index 17f0cf30afc..363bf007ab3 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ContainedTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ContainedTest.java
@@ -5,7 +5,6 @@ import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
-import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Address;
import org.hl7.fhir.r4.model.Address.AddressUse;
@@ -27,7 +26,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import ca.uhn.fhir.jpa.searchparam.SearchContainedEnum;
+import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.ReferenceParam;
@@ -75,7 +74,7 @@ public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
map = new SearchParameterMap();
map.add("subject", new ReferenceParam("name", "Smith"));
- map.setSearchContainedMode(SearchContainedEnum.TRUE);
+ map.setSearchContainedMode(SearchContainedModeEnum.TRUE);
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id)));
}
@@ -111,7 +110,7 @@ public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
map = new SearchParameterMap();
map.add("subject", new ReferenceParam("name", "Smith"));
- map.setSearchContainedMode(SearchContainedEnum.TRUE);
+ map.setSearchContainedMode(SearchContainedModeEnum.TRUE);
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id)));
}
@@ -180,7 +179,7 @@ public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
map = new SearchParameterMap();
map.add("general-practitioner", new ReferenceParam("family", "Smith"));
- map.setSearchContainedMode(SearchContainedEnum.TRUE);
+ map.setSearchContainedMode(SearchContainedModeEnum.TRUE);
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(map)), containsInAnyOrder(toValues(id)));
}
@@ -265,7 +264,7 @@ public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
map = new SearchParameterMap();
map.add("based-on", new ReferenceParam("authored", "2021-02-23"));
- map.setSearchContainedMode(SearchContainedEnum.TRUE);
+ map.setSearchContainedMode(SearchContainedModeEnum.TRUE);
assertThat(toUnqualifiedVersionlessIdValues(myEncounterDao.search(map)), containsInAnyOrder(toValues(id)));
}
@@ -277,7 +276,7 @@ public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
map = new SearchParameterMap();
map.add("subject", new ReferenceParam("near", "toronto"));
- map.setSearchContainedMode(SearchContainedEnum.TRUE);
+ map.setSearchContainedMode(SearchContainedModeEnum.TRUE);
try {
IBundleProvider outcome = myObservationDao.search(map);
@@ -296,7 +295,7 @@ public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
map = new SearchParameterMap();
map.add("subject", new ReferenceParam("marital-status", "M"));
- map.setSearchContainedMode(SearchContainedEnum.TRUE);
+ map.setSearchContainedMode(SearchContainedModeEnum.TRUE);
try {
IBundleProvider outcome = myObservationDao.search(map);
@@ -307,4 +306,4 @@ public class FhirResourceDaoR4ContainedTest extends BaseJpaR4Test {
}
}
-}
\ No newline at end of file
+}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithLuceneDisabledTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithLuceneDisabledTest.java
index dd3fc3d3a49..8ac8333c6c0 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithLuceneDisabledTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithLuceneDisabledTest.java
@@ -148,13 +148,6 @@ public class FhirResourceDaoR4SearchWithLuceneDisabledTest extends BaseJpaTest {
purgeDatabase(myDaoConfig, mySystemDao, myResourceReindexingSvc, mySearchCoordinatorSvc, mySearchParamRegistry, myBulkDataExportSvc);
}
- @BeforeEach
- public void beforeResetConfig() {
- myDaoConfig.setHardSearchLimit(1000);
- myDaoConfig.setHardTagListLimit(1000);
- myDaoConfig.setIncludeLimit(2000);
- }
-
@Override
protected PlatformTransactionManager getTxManager() {
return myTxManager;
@@ -171,7 +164,7 @@ public class FhirResourceDaoR4SearchWithLuceneDisabledTest extends BaseJpaTest {
Organization org = new Organization();
org.setName(methodName);
- IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
+ myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap map = new SearchParameterMap();
map.add(ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT, new StringParam(methodName));
@@ -189,7 +182,7 @@ public class FhirResourceDaoR4SearchWithLuceneDisabledTest extends BaseJpaTest {
Organization org = new Organization();
org.setName(methodName);
- IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
+ myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap map = new SearchParameterMap();
map.add(Organization.SP_NAME, new StringParam(methodName));
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java
index 14283fa83d7..517c4915c11 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java
@@ -480,7 +480,6 @@ public abstract class BaseJpaR5Test extends BaseJpaTest {
@BeforeEach
public void beforeResetConfig() {
- myDaoConfig.setHardTagListLimit(1000);
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
}
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 ea04e937679..3e7b50b22d7 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
@@ -309,9 +309,6 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
@Test
public void testCountParam() {
- // NB this does not get used- The paging provider has its own limits built in
- myDaoConfig.setHardSearchLimit(100);
-
List resources = new ArrayList();
for (int i = 0; i < 100; i++) {
Organization org = new Organization();
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4SearchContainedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4SearchContainedTest.java
index 217a97b4a34..af91f8da02a 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4SearchContainedTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4SearchContainedTest.java
@@ -1,13 +1,12 @@
package ca.uhn.fhir.jpa.provider.r4;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.contains;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-
+import ca.uhn.fhir.jpa.api.config.DaoConfig;
+import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
+import ca.uhn.fhir.jpa.model.entity.ModelConfig;
+import ca.uhn.fhir.parser.StrictErrorHandler;
+import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
+import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
+import ca.uhn.fhir.util.UrlUtil;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
@@ -35,21 +34,23 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
-import ca.uhn.fhir.jpa.api.config.DaoConfig;
-import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
-import ca.uhn.fhir.parser.StrictErrorHandler;
-import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
-import ca.uhn.fhir.util.UrlUtil;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertEquals;
public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4SearchContainedTest.class);
- private CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor();
-
@Autowired
@Qualifier("myClinicalImpressionDaoR4")
protected IFhirResourceDao myClinicalImpressionDao;
+ private CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor();
@Override
@AfterEach
@@ -63,9 +64,10 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches());
myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
-
+
myClient.unregisterInterceptor(myCapturingInterceptor);
myModelConfig.setIndexOnContainedResources(false);
+ myModelConfig.setIndexOnContainedResources(new ModelConfig().isIndexOnContainedResources());
}
@BeforeEach
@@ -78,312 +80,325 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
myClient.registerInterceptor(myCapturingInterceptor);
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
myModelConfig.setIndexOnContainedResources(true);
- }
-
- @BeforeEach
- public void beforeDisableResultReuse() {
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
}
+ @Test
+ public void testContainedDisabled() throws Exception {
+ myModelConfig.setIndexOnContainedResources(false);
+
+ String uri = ourServerBase + "/Observation?subject.name=Smith&_contained=true";
+ try (CloseableHttpResponse response = ourHttpClient.execute(new HttpGet(uri))) {
+ String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
+ ourLog.info(resp);
+ assertEquals(MethodNotAllowedException.STATUS_CODE, response.getStatusLine().getStatusCode());
+ assertThat(resp, containsString(">Searching with _contained mode enabled is not enabled on this server"));
+ }
+ }
+
+ @Test
+ public void testContainedBoth() throws Exception {
+ String uri = ourServerBase + "/Observation?subject.name=Smith&_contained=both";
+ try (CloseableHttpResponse response = ourHttpClient.execute(new HttpGet(uri))) {
+ String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
+ ourLog.info(resp);
+ assertEquals(MethodNotAllowedException.STATUS_CODE, response.getStatusLine().getStatusCode());
+ assertThat(resp, containsString("Contained mode 'both' is not currently supported"));
+ }
+ }
@Test
public void testContainedSearchByName() throws Exception {
-
+
IIdType oid1;
-
+
{
Patient p = new Patient();
p.setId("patient1");
p.addName().setFamily("Smith").addGiven("John");
-
+
Observation obs = new Observation();
obs.getCode().setText("Observation 1");
obs.getContained().add(p);
obs.getSubject().setReference("#patient1");
-
+
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
-
+
{
Patient p = new Patient();
p.setId("patient1");
p.addName().setFamily("Doe").addGiven("Jane");
-
+
Observation obs = new Observation();
obs.getCode().setText("Observation 2");
obs.getContained().add(p);
obs.getSubject().setReference("#patient1");
-
+
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
-
+
{
Patient p = new Patient();
p.setId("patient1");
p.addName().setFamily("Jones").addGiven("Peter");
-
+
Observation obs = new Observation();
obs.getCode().setText("Observation 2");
obs.getContained().add(p);
obs.getSubject().setReference("#patient1");
-
+
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
-
-
+
+
//-- Simple name match
String uri = ourServerBase + "/Observation?subject.name=Smith&_contained=true";
List oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(1L, oids.size());
assertThat(oids, contains(oid1.getValue()));
-
+
//-- Simple name match with or
uri = ourServerBase + "/Observation?subject.name=Smith,Jane&_contained=true";
oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(2L, oids.size());
//assertEquals(oids.toString(), "[Observation/1, Observation/2]");
-
+
//-- Simple name match with qualifier
uri = ourServerBase + "/Observation?subject.name:exact=Smith&_contained=true";
oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(1L, oids.size());
assertThat(oids, contains(oid1.getValue()));
-
+
//-- Simple name match with and
uri = ourServerBase + "/Observation?subject.family=Smith&subject.given=John&_contained=true";
oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(1L, oids.size());
assertThat(oids, contains(oid1.getValue()));
-
- //-- Simple name match with both, default to normal search, found 0
- uri = ourServerBase + "/Observation?subject.name=Smith&_contained=both";
- oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
- assertEquals(0L, oids.size());
}
@Test
public void testContainedSearchByDate() throws Exception {
-
+
IIdType oid1;
IIdType oid3;
-
+
{
Patient p = new Patient();
p.setId("patient1");
p.addName().setFamily("Smith").addGiven("John");
p.getBirthDateElement().setValueAsString("2000-01-01");
-
+
Observation obs = new Observation();
obs.getCode().setText("Observation 1");
obs.getContained().add(p);
obs.getSubject().setReference("#patient1");
-
+
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
-
+
{
Patient p = new Patient();
p.setId("patient1");
p.addName().setFamily("Doe").addGiven("Jane");
p.getBirthDateElement().setValueAsString("2000-02-01");
-
+
Observation obs = new Observation();
obs.getCode().setText("Observation 2");
obs.getContained().add(p);
obs.getSubject().setReference("#patient1");
-
+
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
-
+
{
Patient p = new Patient();
p.setId("patient1");
p.addName().setFamily("Jones").addGiven("Peter");
p.getBirthDateElement().setValueAsString("2000-03-01");
-
+
Observation obs = new Observation();
obs.getCode().setText("Observation 2");
obs.getContained().add(p);
obs.getSubject().setReference("#patient1");
-
+
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
-
+
//-- Search by date default op
String uri = ourServerBase + "/Observation?subject.birthdate=2000-01-01&_contained=true";
List oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(1L, oids.size());
assertThat(oids, contains(oid1.getValue()));
-
+
//-- Search by date op=eq
uri = ourServerBase + "/Observation?subject.birthdate=eq2000-01-01&_contained=true";
oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(1L, oids.size());
assertThat(oids, contains(oid1.getValue()));
-
+
//-- Search by date op=eq, with or
uri = ourServerBase + "/Observation?subject.birthdate=2000-01-01,2000-02-01&_contained=true";
oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(2L, oids.size());
//assertEquals(oids.toString(), "[Observation/1, Observation/2]");
-
+
//-- Simple name match with op = gt
uri = ourServerBase + "/Observation?subject.birthdate=gt2000-02-10&_contained=true";
oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(1L, oids.size());
assertThat(oids, contains(oid3.getValue()));
-
+
//-- Simple name match with AND
uri = ourServerBase + "/Observation?subject.family=Smith&subject.birthdate=eq2000-01-01&_contained=true";
oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(1L, oids.size());
assertThat(oids, contains(oid1.getValue()));
-
+
//-- Simple name match with AND - not found
uri = ourServerBase + "/Observation?subject.family=Smith&subject.birthdate=eq2000-02-01&_contained=true";
oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(0L, oids.size());
}
-
+
@Test
public void testContainedSearchByNumber() throws Exception {
-
+
IIdType cid1;
-
+
{
Patient p = new Patient();
p.setId("patient1");
p.addName().setFamily("Smith").addGiven("John");
p.getBirthDateElement().setValueAsString("2000-01-01");
-
-
+
+
RiskAssessment risk = new RiskAssessment();
risk.setId("risk1");
risk.setStatus(RiskAssessmentStatus.CORRECTED);
risk.getSubject().setReference("#patient1");
risk.getPredictionFirstRep().setProbability(new DecimalType(2));
-
+
ClinicalImpression imp = new ClinicalImpression();
imp.setStatus(ClinicalImpressionStatus.COMPLETED);
-
+
imp.getContained().add(p);
imp.getSubject().setReference("#patient1");
-
+
imp.getContained().add(risk);
imp.getInvestigationFirstRep().getItemFirstRep().setReference("#risk1");
-
+
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(imp));
-
+
cid1 = myClinicalImpressionDao.create(imp, mySrd).getId().toUnqualifiedVersionless();
-
+
ClinicalImpression createdImp = myClinicalImpressionDao.read(cid1);
-
+
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdImp));
}
-
+
{
Patient p = new Patient();
p.setId("patient1");
p.addName().setFamily("Smith").addGiven("John");
p.getBirthDateElement().setValueAsString("2000-01-01");
-
-
+
+
RiskAssessment risk = new RiskAssessment();
risk.setId("risk1");
risk.setStatus(RiskAssessmentStatus.CORRECTED);
risk.getSubject().setReference("#patient1");
risk.getPredictionFirstRep().setProbability(new DecimalType(5));
-
+
ClinicalImpression imp = new ClinicalImpression();
imp.setStatus(ClinicalImpressionStatus.COMPLETED);
-
+
imp.getContained().add(p);
imp.getSubject().setReference("#patient1");
-
+
imp.getContained().add(risk);
imp.getInvestigationFirstRep().getItemFirstRep().setReference("#risk1");
-
+
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(imp));
-
+
IIdType cid2 = myClinicalImpressionDao.create(imp, mySrd).getId().toUnqualifiedVersionless();
-
+
ClinicalImpression createdImp = myClinicalImpressionDao.read(cid2);
-
+
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdImp));
}
-
+
{
Patient p = new Patient();
p.setId("patient1");
p.addName().setFamily("Smith").addGiven("John");
p.getBirthDateElement().setValueAsString("2000-01-01");
-
-
+
+
RiskAssessment risk = new RiskAssessment();
risk.setId("risk1");
risk.setStatus(RiskAssessmentStatus.CORRECTED);
risk.getSubject().setReference("#patient1");
risk.getPredictionFirstRep().setProbability(new DecimalType(10));
-
+
ClinicalImpression imp = new ClinicalImpression();
imp.setStatus(ClinicalImpressionStatus.COMPLETED);
-
+
imp.getContained().add(p);
imp.getSubject().setReference("#patient1");
-
+
imp.getContained().add(risk);
imp.getInvestigationFirstRep().getItemFirstRep().setReference("#risk1");
-
+
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(imp));
-
+
IIdType cid3 = myClinicalImpressionDao.create(imp, mySrd).getId().toUnqualifiedVersionless();
-
+
ClinicalImpression createdImp = myClinicalImpressionDao.read(cid3);
-
+
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdImp));
}
-
+
//-- Search by number
String uri = ourServerBase + "/ClinicalImpression?investigation.probability=2&_contained=true";
List cids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(1L, cids.size());
assertThat(cids, contains(cid1.getValue()));
-
-
+
+
//-- Search by number with op = eq
uri = ourServerBase + "/ClinicalImpression?investigation.probability=eq2&_contained=true";
cids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(1L, cids.size());
assertThat(cids, contains(cid1.getValue()));
-
-
+
+
//-- Search by number with op = eq and or
uri = ourServerBase + "/ClinicalImpression?investigation.probability=eq2,10&_contained=true";
cids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
@@ -392,25 +407,25 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
//-- Search by number with op = lt
uri = ourServerBase + "/ClinicalImpression?investigation.probability=lt4&_contained=true";
cids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(1L, cids.size());
- assertThat(cids, contains(cid1.getValue()));
+ assertThat(cids, contains(cid1.getValue()));
}
-
+
@Test
public void testContainedSearchByQuantity() throws Exception {
-
+
IIdType eid1;
{
Encounter encounter = new Encounter();
encounter.setStatus(EncounterStatus.ARRIVED);
-
+
Patient patient = new Patient();
patient.setId("patient1");
patient.addName().setFamily("Doe").addGiven("Jane");
encounter.getSubject().setReference("#patient1");
encounter.getContained().add(patient);
-
+
Observation obs = new Observation();
obs.setId("obs1");
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
@@ -421,27 +436,27 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
quantity.setValue(200);
encounter.addReasonReference().setReference("#obs1");
encounter.getContained().add(obs);
-
+
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
-
+
eid1 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
-
+
Encounter createdEncounter = myEncounterDao.read(eid1);
-
+
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
}
-
-
+
+
{
Encounter encounter = new Encounter();
encounter.setStatus(EncounterStatus.ARRIVED);
-
+
Patient patient = new Patient();
patient.setId("patient1");
patient.addName().setFamily("Doe").addGiven("Jane");
encounter.getSubject().setReference("#patient1");
encounter.getContained().add(patient);
-
+
Observation obs = new Observation();
obs.setId("obs1");
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
@@ -452,26 +467,26 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
quantity.setValue(300);
encounter.addReasonReference().setReference("#obs1");
encounter.getContained().add(obs);
-
+
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
-
+
IIdType eid2 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
-
+
Encounter createdEncounter = myEncounterDao.read(eid2);
-
+
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
}
-
+
{
Encounter encounter = new Encounter();
encounter.setStatus(EncounterStatus.ARRIVED);
-
+
Patient patient = new Patient();
patient.setId("patient1");
patient.addName().setFamily("Doe").addGiven("Jane");
encounter.getSubject().setReference("#patient1");
encounter.getContained().add(patient);
-
+
Observation obs = new Observation();
obs.setId("obs1");
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
@@ -482,46 +497,46 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
quantity.setValue(400);
encounter.addReasonReference().setReference("#obs1");
encounter.getContained().add(obs);
-
+
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
-
+
IIdType eid3 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
-
+
Encounter createdEncounter = myEncounterDao.read(eid3);
-
+
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
}
-
+
//-- Search by quantity
String uri = ourServerBase + "/Encounter?reason-reference.combo-value-quantity=200&_contained=true";
List eids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(1L, eids.size());
assertThat(eids, contains(eid1.getValue()));
-
-
+
+
//-- Search by quantity
uri = ourServerBase + "/Encounter?reason-reference.combo-value-quantity=le400&_contained=true";
eids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(3L, eids.size());
}
-
+
@Test
public void testContainedSearchByToken() throws Exception {
-
+
IIdType eid1;
{
Encounter encounter = new Encounter();
encounter.setStatus(EncounterStatus.ARRIVED);
-
+
Patient patient = new Patient();
patient.setId("patient1");
patient.addName().setFamily("Doe").addGiven("Jane");
encounter.getSubject().setReference("#patient1");
encounter.getContained().add(patient);
-
+
Observation obs = new Observation();
obs.setId("obs1");
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
@@ -532,27 +547,27 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
quantity.setValue(200);
encounter.addReasonReference().setReference("#obs1");
encounter.getContained().add(obs);
-
+
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
-
+
eid1 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
-
+
Encounter createdEncounter = myEncounterDao.read(eid1);
-
+
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
}
-
-
+
+
{
Encounter encounter = new Encounter();
encounter.setStatus(EncounterStatus.ARRIVED);
-
+
Patient patient = new Patient();
patient.setId("patient1");
patient.addName().setFamily("Doe").addGiven("Jane");
encounter.getSubject().setReference("#patient1");
encounter.getContained().add(patient);
-
+
Observation obs = new Observation();
obs.setId("obs1");
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
@@ -563,26 +578,26 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
quantity.setValue(300);
encounter.addReasonReference().setReference("#obs1");
encounter.getContained().add(obs);
-
+
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
-
+
IIdType eid2 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
-
+
Encounter createdEncounter = myEncounterDao.read(eid2);
-
+
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
}
-
+
{
Encounter encounter = new Encounter();
encounter.setStatus(EncounterStatus.ARRIVED);
-
+
Patient patient = new Patient();
patient.setId("patient1");
patient.addName().setFamily("Doe").addGiven("Jane");
encounter.getSubject().setReference("#patient1");
encounter.getContained().add(patient);
-
+
Observation obs = new Observation();
obs.setId("obs1");
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
@@ -593,39 +608,39 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
quantity.setValue(400);
encounter.addReasonReference().setReference("#obs1");
encounter.getContained().add(obs);
-
+
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
-
+
IIdType eid3 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
-
+
Encounter createdEncounter = myEncounterDao.read(eid3);
-
+
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
}
-
+
//-- Search by code
String uri = ourServerBase + "/Encounter?reason-reference.code=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7") + "&_contained=true";
List eids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(1L, eids.size());
assertThat(eids, contains(eid1.getValue()));
-
+
}
-
+
@Test
public void testContainedSearchByComposite() throws Exception {
-
+
IIdType eid2;
{
Encounter encounter = new Encounter();
encounter.setStatus(EncounterStatus.ARRIVED);
-
+
Patient patient = new Patient();
patient.setId("patient1");
patient.addName().setFamily("Doe").addGiven("Jane");
encounter.getSubject().setReference("#patient1");
encounter.getContained().add(patient);
-
+
Observation obs = new Observation();
obs.setId("obs1");
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
@@ -636,27 +651,27 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
quantity.setValue(200);
encounter.addReasonReference().setReference("#obs1");
encounter.getContained().add(obs);
-
+
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
-
+
IIdType eid1 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
-
+
Encounter createdEncounter = myEncounterDao.read(eid1);
-
+
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
}
-
-
+
+
{
Encounter encounter = new Encounter();
encounter.setStatus(EncounterStatus.ARRIVED);
-
+
Patient patient = new Patient();
patient.setId("patient1");
patient.addName().setFamily("Doe").addGiven("Jane");
encounter.getSubject().setReference("#patient1");
encounter.getContained().add(patient);
-
+
Observation obs = new Observation();
obs.setId("obs1");
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
@@ -667,26 +682,26 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
quantity.setValue(300);
encounter.addReasonReference().setReference("#obs1");
encounter.getContained().add(obs);
-
+
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
-
+
eid2 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
-
+
Encounter createdEncounter = myEncounterDao.read(eid2);
-
+
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
}
-
+
{
Encounter encounter = new Encounter();
encounter.setStatus(EncounterStatus.ARRIVED);
-
+
Patient patient = new Patient();
patient.setId("patient1");
patient.addName().setFamily("Doe").addGiven("Jane");
encounter.getSubject().setReference("#patient1");
encounter.getContained().add(patient);
-
+
Observation obs = new Observation();
obs.setId("obs1");
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
@@ -697,37 +712,37 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
quantity.setValue(400);
encounter.addReasonReference().setReference("#obs1");
encounter.getContained().add(obs);
-
+
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(encounter));
-
- IIdType eid3 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
-
+
+ IIdType eid3 = myEncounterDao.create(encounter, mySrd).getId().toUnqualifiedVersionless();
+
Encounter createdEncounter = myEncounterDao.read(eid3);
-
+
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdEncounter));
}
-
+
//-- Search by composite
String uri = ourServerBase + "/Encounter?reason-reference.combo-code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-8$300") + "&_contained=true";
List eids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(1L, eids.size());
assertThat(eids, contains(eid2.getValue()));
-
+
//-- Search by composite - not found
uri = ourServerBase + "/Encounter?reason-reference.combo-code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$300") + "&_contained=true";
eids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
- assertEquals(0L, eids.size());
-
+
+ assertEquals(0L, eids.size());
+
}
-
-
+
+
@Test
public void testContainedSearchByUri() throws Exception {
-
+
IIdType oid1;
-
+
{
Patient p = new Patient();
p.setId("patient1");
@@ -740,24 +755,24 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
carePlan.setIntent(CarePlanIntent.ORDER);
carePlan.getSubject().setReference("#patient1");
carePlan.addInstantiatesUri("http://www.hl7.com");
-
+
Observation obs = new Observation();
obs.getCode().setText("Observation 1");
obs.getContained().add(p);
obs.getSubject().setReference("#patient1");
obs.getContained().add(carePlan);
obs.getBasedOnFirstRep().setReference("#carePlan1");
-
-
+
+
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
-
+
Observation createdObs = myObservationDao.read(oid1);
-
+
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createdObs));
}
-
+
{
Patient p = new Patient();
p.setId("patient2");
@@ -770,19 +785,19 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
carePlan.setIntent(CarePlanIntent.ORDER);
carePlan.getSubject().setReference("#patient2");
carePlan.addInstantiatesUri("http://www2.hl7.com");
-
+
Observation obs = new Observation();
obs.getCode().setText("Observation 2");
obs.getContained().add(p);
obs.getSubject().setReference("#patient2");
obs.getContained().add(carePlan);
obs.getBasedOnFirstRep().setReference("#carePlan2");
-
+
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
-
+
{
Patient p = new Patient();
p.setId("patient3");
@@ -795,32 +810,32 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
carePlan.setIntent(CarePlanIntent.ORDER);
carePlan.getSubject().setReference("#patient3");
carePlan.addInstantiatesUri("http://www2.hl7.com");
-
+
Observation obs = new Observation();
obs.getCode().setText("Observation 3");
obs.getContained().add(p);
obs.getSubject().setReference("#patient3");
obs.getContained().add(carePlan);
obs.getBasedOnFirstRep().setReference("#carePlan3");
-
+
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
-
+
//-- Search by uri
String uri = ourServerBase + "/Observation?based-on.instantiates-uri=http://www.hl7.com&_contained=true";
List oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
assertEquals(1L, oids.size());
assertThat(oids, contains(oid1.getValue()));
-
+
//-- Search by uri more than 1 results
uri = ourServerBase + "/Observation?based-on.instantiates-uri=http://www2.hl7.com&_contained=true";
oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
assertEquals(2L, oids.size());
-
+
//-- Search by uri with 'or'
uri = ourServerBase + "/Observation?based-on.instantiates-uri=http://www.hl7.com,http://www2.hl7.com&_contained=true";
oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
@@ -828,94 +843,94 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
assertEquals(3L, oids.size());
}
-
+
@Test
public void testUpdateContainedResource() throws Exception {
-
+
IIdType oid1;
-
+
{
Patient p = new Patient();
p.setId("patient1");
p.addName().setFamily("Smith").addGiven("John");
-
+
Observation obs = new Observation();
obs.getCode().setText("Observation 1");
obs.getContained().add(p);
obs.getSubject().setReference("#patient1");
-
+
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
-
+
Observation createdObs = myObservationDao.read(oid1);
-
+
//-- changed the last name to Doe
List containedResources = createdObs.getContained();
-
+
for (Resource res : containedResources) {
if (res instanceof Patient) {
- Patient p1 = (Patient)res;
+ Patient p1 = (Patient) res;
HumanName name = p1.getNameFirstRep();
name.setFamily("Doe");
break;
}
}
-
+
// -- update
oid1 = myObservationDao.update(createdObs, mySrd).getId().toUnqualifiedVersionless();
}
-
+
{
Patient p = new Patient();
p.setId("patient1");
p.addName().setFamily("Doe").addGiven("Jane");
-
+
Observation obs = new Observation();
obs.getCode().setText("Observation 2");
obs.getContained().add(p);
obs.getSubject().setReference("#patient1");
-
+
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
-
+
{
Patient p = new Patient();
p.setId("patient1");
p.addName().setFamily("Jones").addGiven("Peter");
-
+
Observation obs = new Observation();
obs.getCode().setText("Observation 2");
obs.getContained().add(p);
obs.getSubject().setReference("#patient1");
-
+
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
-
-
+
+
//-- No Obs with Patient Smith
String uri = ourServerBase + "/Observation?subject.family=Smith&_contained=true";
List oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(0L, oids.size());
-
+
//-- Two Obs with Patient Doe
uri = ourServerBase + "/Observation?subject.family=Doe&_contained=true";
oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(2L, oids.size());
}
-
+
@Test
public void testDeleteContainedResource() throws Exception {
-
+
IIdType oid1;
-
+
{
Patient p1 = new Patient();
p1.setId("patient1");
@@ -929,39 +944,40 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
obs.getCode().setText("Observation 1");
obs.getContained().add(p1);
obs.getSubject().setReference("#patient1");
-
+
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
-
+
// -- remove contained resource
obs.getContained().remove(p1);
// -- add new contained resource
obs.getContained().add(p2);
obs.getSubject().setReference("#patient2");
-
+
ourLog.info("Input: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
// -- update
oid1 = myObservationDao.update(obs, mySrd).getId().toUnqualifiedVersionless();
-
+
Observation updatedObs = myObservationDao.read(oid1);
-
+
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(updatedObs));
}
-
+
//-- No Obs with Patient Smith
String uri = ourServerBase + "/Observation?subject.family=Smith&_contained=true";
List oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(0L, oids.size());
-
+
//-- 1 Obs with Patient Doe
uri = ourServerBase + "/Observation?subject.family=Doe&_contained=true";
oids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
-
+
assertEquals(1L, oids.size());
assertThat(oids, contains(oid1.getValue()));
}
+
private List searchAndReturnUnqualifiedVersionlessIdValues(String uri) throws IOException {
List ids;
HttpGet get = new HttpGet(uri);
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookWithEventDefinitionR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookWithEventDefinitionR4Test.java
index 4b9822f18db..54804331e05 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookWithEventDefinitionR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookWithEventDefinitionR4Test.java
@@ -7,7 +7,12 @@ import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
import ca.uhn.fhir.rest.api.MethodOutcome;
import com.google.common.collect.Lists;
import org.hl7.fhir.instance.model.api.IIdType;
-import org.hl7.fhir.r4.model.*;
+import org.hl7.fhir.r4.model.EventDefinition;
+import org.hl7.fhir.r4.model.Expression;
+import org.hl7.fhir.r4.model.Observation;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Subscription;
+import org.hl7.fhir.r4.model.TriggerDefinition;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
@@ -36,6 +41,7 @@ import java.util.List;
* 6. Execute the 'sendObservation' test
* 7. Look in the 'attachWebSocket' terminal execution and wait for your ping with the subscription id
*/
+
/**
* Ignored because this feature isn't implemented yet
*/
@@ -77,15 +83,6 @@ public class RestHookWithEventDefinitionR4Test extends BaseResourceProviderR4Tes
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
}
- @Override
- @BeforeEach
- public void before() throws Exception {
- super.before();
-
- myDaoConfig.setSubscriptionEnabled(true);
-
- }
-
@Test
public void testSubscriptionAddedTrigger() {
/*
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu3Test.java
index 85d46486957..6f35dfdfc9b 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu3Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu3Test.java
@@ -9,7 +9,12 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
-import org.hl7.fhir.dstu3.model.*;
+import org.hl7.fhir.dstu3.model.CodeableConcept;
+import org.hl7.fhir.dstu3.model.Coding;
+import org.hl7.fhir.dstu3.model.Observation;
+import org.hl7.fhir.dstu3.model.Patient;
+import org.hl7.fhir.dstu3.model.Reference;
+import org.hl7.fhir.dstu3.model.Subscription;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -20,8 +25,8 @@ import java.net.URI;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
-import static org.hamcrest.Matchers.contains;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
/**
* Adds a FHIR subscription with criteria through the rest interface. Then creates a websocket with the id of the
@@ -71,9 +76,6 @@ public class WebsocketWithSubscriptionIdDstu3Test extends BaseResourceProviderDs
public void before() throws Exception {
super.before();
- myDaoConfig.setSubscriptionEnabled(true);
- myDaoConfig.setSubscriptionPollDelay(0L);
-
mySubscriptionTestUtil.registerWebSocketInterceptor();
/*
diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ModelConfig.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ModelConfig.java
index 4e06fd1d4f5..b875d57cf80 100644
--- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ModelConfig.java
+++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ModelConfig.java
@@ -734,8 +734,8 @@ public class ModelConfig {
/**
- * Should indexed on the contained resources, it could be searched by _contained=true
- * This may have performance impacts
+ * Should indexing and searching on contained resources be enabled on this server.
+ * This may have performance impacts, and should be enabled only if it is needed. Default is false
.
*
* @since 5.4.0
*/
@@ -744,9 +744,9 @@ public class ModelConfig {
}
/**
- * Should indexed on the contained resources, it could be searched by _contained=true
- * This may have performance impacts
- *
+ * Should indexing and searching on contained resources be enabled on this server.
+ * This may have performance impacts, and should be enabled only if it is needed. Default is false
.
+ *
* @since 5.4.0
*/
public void setIndexOnContainedResources(boolean theIndexOnContainedResources) {
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchContainedEnum.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchContainedEnum.java
deleted file mode 100644
index 1755b09b85d..00000000000
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchContainedEnum.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package ca.uhn.fhir.jpa.searchparam;
-
-/*
- * #%L
- * HAPI FHIR Search Parameters
- * %%
- * Copyright (C) 2014 - 2021 Smile CDR, Inc.
- * %%
- * 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%
- */
-
-public enum SearchContainedEnum {
-
- /**
- * default, search on the non-contained (normal) resources
- */
- FALSE,
-
- /**
- * search on the contained resources only
- */
- TRUE,
-
- /**
- * Search on the normal resources and contained resources.
- * This option is not supported yet.
- */
- BOTH,
-}
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 e6434a8cf5e..85a6ef8cf4a 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
@@ -6,6 +6,7 @@ import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.Constants;
+import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
@@ -79,7 +80,7 @@ public class SearchParameterMap implements Serializable {
private boolean myLastN;
private Integer myLastNMax;
private boolean myDeleteExpunge;
- private SearchContainedEnum mySearchContainedMode = SearchContainedEnum.FALSE;
+ private SearchContainedModeEnum mySearchContainedMode = SearchContainedModeEnum.FALSE;
/**
* Constructor
@@ -735,12 +736,16 @@ public class SearchParameterMap implements Serializable {
return retVal;
}
- public SearchContainedEnum getSearchContainedMode() {
+ public SearchContainedModeEnum getSearchContainedMode() {
return mySearchContainedMode;
}
- public void setSearchContainedMode(SearchContainedEnum theSearchContainedMode) {
- this.mySearchContainedMode = theSearchContainedMode;
+ public void setSearchContainedMode(SearchContainedModeEnum theSearchContainedMode) {
+ if (theSearchContainedMode == null) {
+ mySearchContainedMode = SearchContainedModeEnum.FALSE;
+ } else {
+ this.mySearchContainedMode = theSearchContainedMode;
+ }
}
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java
index 365957556f4..cbaf424d495 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java
@@ -79,6 +79,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
retVal.setWebsocketContextPath("/");
retVal.setFilterParameterEnabled(true);
retVal.setDefaultSearchParamsCanBeOverridden(false);
+ retVal.getModelConfig().setIndexOnContainedResources(true);
return retVal;
}
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java
index b9d81163c10..bcbdb4a825f 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java
@@ -77,6 +77,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
retVal.setExpungeEnabled(true);
retVal.setFilterParameterEnabled(true);
retVal.setDefaultSearchParamsCanBeOverridden(false);
+ retVal.getModelConfig().setIndexOnContainedResources(true);
return retVal;
}
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java
index db7f0b7192d..f1194ba835d 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java
@@ -78,6 +78,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
retVal.setExpungeEnabled(true);
retVal.setFilterParameterEnabled(true);
retVal.setDefaultSearchParamsCanBeOverridden(false);
+ retVal.getModelConfig().setIndexOnContainedResources(true);
return retVal;
}
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java
index e5f3a5b2aee..f7f4d08b241 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java
+++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java
@@ -77,6 +77,7 @@ public class TestR5Config extends BaseJavaConfigR5 {
retVal.setExpungeEnabled(true);
retVal.setFilterParameterEnabled(true);
retVal.setDefaultSearchParamsCanBeOverridden(false);
+ retVal.getModelConfig().setIndexOnContainedResources(true);
return retVal;
}
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java
index 58281c642f5..a9d131791f0 100644
--- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java
@@ -44,7 +44,6 @@ import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
public class RuleBuilder implements IAuthRuleBuilder {
- private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final ConcurrentHashMap, String> ourTypeToName = new ConcurrentHashMap<>();
private ArrayList myRules;
private IAuthRuleBuilderRule myAllow;
@@ -144,7 +143,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
@Override
public IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId forTenantIds(String... theTenantIds) {
- return forTenantIds(Arrays.asList(defaultIfNull(theTenantIds, EMPTY_STRING_ARRAY)));
+ return forTenantIds(Arrays.asList(defaultIfNull(theTenantIds, Constants.EMPTY_STRING_ARRAY)));
}
@Override
@@ -162,7 +161,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
@Override
public IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId notForTenantIds(String... theTenantIds) {
- return notForTenantIds(Arrays.asList(defaultIfNull(theTenantIds, EMPTY_STRING_ARRAY)));
+ return notForTenantIds(Arrays.asList(defaultIfNull(theTenantIds, Constants.EMPTY_STRING_ARRAY)));
}
@Override
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 ee39ab733c1..e01d1daa859 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
@@ -131,6 +131,8 @@ public class MethodUtil {
param = new SummaryEnumParameter();
} else if (parameterType.equals(PatchTypeEnum.class)) {
param = new PatchTypeParameter();
+ } else if (parameterType.equals(SearchContainedModeEnum.class)) {
+ param = new SearchContainedModeParameter();
} else if (parameterType.equals(SearchTotalModeEnum.class)) {
param = new SearchTotalModeParameter();
} else {
diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchContainedModeParameter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchContainedModeParameter.java
new file mode 100644
index 00000000000..bb6480c1320
--- /dev/null
+++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchContainedModeParameter.java
@@ -0,0 +1,34 @@
+package ca.uhn.fhir.rest.server.method;
+
+import ca.uhn.fhir.rest.api.Constants;
+import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
+import ca.uhn.fhir.rest.api.server.RequestDetails;
+import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
+import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+
+import static org.apache.commons.lang3.StringUtils.isNotBlank;
+
+class SearchContainedModeParameter implements IParameter {
+
+ @Override
+ public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding> theMethodBinding) throws InternalErrorException, InvalidRequestException {
+ return getTypeForRequestOrThrowInvalidRequestException(theRequest);
+ }
+
+ @Override
+ public void initializeTypes(Method theMethod, Class extends Collection>> theOuterCollectionType, Class extends Collection>> theInnerCollectionType, Class> theParameterType) {
+ // ignore
+ }
+
+ public static SearchContainedModeEnum getTypeForRequestOrThrowInvalidRequestException(RequestDetails theRequest) {
+ String[] paramValues = theRequest.getParameters().getOrDefault(Constants.PARAM_CONTAINED, Constants.EMPTY_STRING_ARRAY);
+ if (paramValues.length > 0 && isNotBlank(paramValues[0])) {
+ return SearchContainedModeEnum.fromCode(paramValues[0]);
+ }
+ return null;
+ }
+
+}
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 44323929224..9c5a9f533e8 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
@@ -20,6 +20,7 @@ import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
+import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
public class ${className}ResourceProvider extends
## We have specialized base classes for RPs that handle certain resource types. These
@@ -138,7 +139,9 @@ public class ${className}ResourceProvider extends
SummaryEnum theSummaryMode,
- SearchTotalModeEnum theSearchTotalMode
+ SearchTotalModeEnum theSearchTotalMode,
+
+ SearchContainedModeEnum theSearchContainedMode
) {
startRequest(theServletRequest);
@@ -165,6 +168,7 @@ public class ${className}ResourceProvider extends
paramMap.setOffset(theOffset);
paramMap.setSummaryMode(theSummaryMode);
paramMap.setSearchTotalMode(theSearchTotalMode);
+ paramMap.setSearchContainedMode(theSearchContainedMode);
getDao().translateRawParameters(theAdditionalRawParams, paramMap);