diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml
index bfe99e051b0..b9e16473e1a 100644
--- a/hapi-deployable-pom/pom.xml
+++ b/hapi-deployable-pom/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml
index 824f780f5fc..e99361c3363 100644
--- a/hapi-fhir-android/pom.xml
+++ b/hapi-fhir-android/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml
index 86a97c9e48c..37a04d3b776 100644
--- a/hapi-fhir-base/pom.xml
+++ b/hapi-fhir-base/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ComboSearchParamType.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ComboSearchParamType.java
new file mode 100644
index 00000000000..17931b09ea1
--- /dev/null
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ComboSearchParamType.java
@@ -0,0 +1,28 @@
+package ca.uhn.fhir.context;
+
+/*-
+ * #%L
+ * HAPI FHIR - Core Library
+ * %%
+ * 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 ComboSearchParamType {
+
+ UNIQUE,
+ NON_UNIQUE
+
+}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java
index 81056846b12..a03d8d39414 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java
@@ -416,7 +416,7 @@ class ModelScanner {
if (theResourceDef.isStandardType()) {
url = "http://hl7.org/fhir/SearchParameter/" + theResourceDef.getName().toLowerCase() + "-" + searchParam.name();
}
- RuntimeSearchParam param = new RuntimeSearchParam(null, url, searchParam.name(), searchParam.description(), searchParam.path(), paramType, providesMembershipInCompartments, toTargetList(searchParam.target()), RuntimeSearchParamStatusEnum.ACTIVE, false, components, base);
+ RuntimeSearchParam param = new RuntimeSearchParam(null, url, searchParam.name(), searchParam.description(), searchParam.path(), paramType, providesMembershipInCompartments, toTargetList(searchParam.target()), RuntimeSearchParamStatusEnum.ACTIVE, null, components, base);
theResourceDef.addSearchParam(param);
nameToParam.put(param.getName(), param);
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java
index 8e795c32e1a..746deccd120 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java
@@ -10,6 +10,7 @@ import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IIdType;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -55,7 +56,7 @@ public class RuntimeSearchParam {
private final RuntimeSearchParamStatusEnum myStatus;
private final String myUri;
private final Map>> myExtensions = new HashMap<>();
- private final boolean myUnique;
+ private final ComboSearchParamType myComboSearchParamType;
private final List myComponents;
private IPhoneticEncoder myPhoneticEncoder;
@@ -64,20 +65,20 @@ public class RuntimeSearchParam {
*/
public RuntimeSearchParam(IIdType theId, String theUri, String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType,
Set theProvidesMembershipInCompartments, Set theTargets, RuntimeSearchParamStatusEnum theStatus, Collection theBase) {
- this(theId, theUri, theName, theDescription, thePath, theParamType, theProvidesMembershipInCompartments, theTargets, theStatus, false, Collections.emptyList(), theBase);
+ this(theId, theUri, theName, theDescription, thePath, theParamType, theProvidesMembershipInCompartments, theTargets, theStatus, null, Collections.emptyList(), theBase);
}
/**
* Copy constructor
*/
public RuntimeSearchParam(RuntimeSearchParam theSp) {
- this(theSp.getId(), theSp.getUri(), theSp.getName(), theSp.getDescription(), theSp.getPath(), theSp.getParamType(), theSp.getProvidesMembershipInCompartments(), theSp.getTargets(), theSp.getStatus(), theSp.isUnique(), theSp.getComponents(), theSp.getBase());
+ this(theSp.getId(), theSp.getUri(), theSp.getName(), theSp.getDescription(), theSp.getPath(), theSp.getParamType(), theSp.getProvidesMembershipInCompartments(), theSp.getTargets(), theSp.getStatus(), theSp.getComboSearchParamType(), theSp.getComponents(), theSp.getBase());
}
/**
* Constructor
*/
- public RuntimeSearchParam(IIdType theId, String theUri, String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, Set theProvidesMembershipInCompartments, Set theTargets, RuntimeSearchParamStatusEnum theStatus, boolean theUnique, List theComponents, Collection theBase) {
+ public RuntimeSearchParam(IIdType theId, String theUri, String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, Set theProvidesMembershipInCompartments, Set theTargets, RuntimeSearchParamStatusEnum theStatus, ComboSearchParamType theComboSearchParamType, List theComponents, Collection theBase) {
super();
myId = theId;
@@ -110,7 +111,7 @@ public class RuntimeSearchParam {
} else {
myBase = Collections.unmodifiableSet(new HashSet<>(theBase));
}
- myUnique = theUnique;
+ myComboSearchParamType = theComboSearchParamType;
if (theComponents != null) {
myComponents = Collections.unmodifiableList(theComponents);
} else {
@@ -122,8 +123,12 @@ public class RuntimeSearchParam {
return myComponents;
}
- public boolean isUnique() {
- return myUnique;
+ /**
+ * Returns null
if this is not a combo search param type
+ */
+ @Nullable
+ public ComboSearchParamType getComboSearchParamType() {
+ return myComboSearchParamType;
}
/**
diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml
index 81c1588e76f..5e2c982af71 100644
--- a/hapi-fhir-bom/pom.xml
+++ b/hapi-fhir-bom/pom.xml
@@ -3,14 +3,14 @@
4.0.0
ca.uhn.hapi.fhir
hapi-fhir-bom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
pom
HAPI FHIR BOM
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml
index 1097f5600e7..ba080272c45 100644
--- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml
+++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml
index 3085ccc4114..1a2e3701f42 100644
--- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml
+++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-fhir-cli
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml
index 0720e557706..9fe3bec0a31 100644
--- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml
+++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../../hapi-deployable-pom
diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml
index eec2c53e1e0..454a3a95bef 100644
--- a/hapi-fhir-cli/pom.xml
+++ b/hapi-fhir-cli/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml
index 16aa0ef24d0..d3e4e0bccac 100644
--- a/hapi-fhir-client-okhttp/pom.xml
+++ b/hapi-fhir-client-okhttp/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml
index 64c0cc41683..075afdc3046 100644
--- a/hapi-fhir-client/pom.xml
+++ b/hapi-fhir-client/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml
index 6ecc1015468..831a0c69b78 100644
--- a/hapi-fhir-converter/pom.xml
+++ b/hapi-fhir-converter/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml
index 7f6bcfd4800..b55f14174d9 100644
--- a/hapi-fhir-dist/pom.xml
+++ b/hapi-fhir-dist/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml
index 04637b93b09..fe6c2fc7d7d 100644
--- a/hapi-fhir-docs/pom.xml
+++ b/hapi-fhir-docs/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/atlas/points.json b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/atlas/points.json
index f952add90cd..15ff1b26762 100644
--- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/atlas/points.json
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/atlas/points.json
@@ -381,6 +381,17 @@
"lat": 49.234000,
"lon": -123.065890,
"added": "2021-06-29"
+ },
+ {
+ "title": "Innovattic",
+ "description": "Creating remote monitoring solutions for several hospitals in the Netherlands.",
+ "contactName": "Lauwerens Metz",
+ "contactEmail": "info@innovattic.com",
+ "link": "https://innovattic.com/",
+ "city": "Delft, The Netherlands",
+ "lat": 52.01804,
+ "lon": 4.354307,
+ "added": "2021-07-15"
}
]
}
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2791-increase-identifier-length.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2791-increase-identifier-length.yaml
new file mode 100644
index 00000000000..78dbbf1262e
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2791-increase-identifier-length.yaml
@@ -0,0 +1,4 @@
+---
+type: change
+issue: 2791
+title: "Identifier maximum length increased from 200 to 500. This specifically applies to table HFJ_IDX_CMP_STRING_UNIQ."
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2794-res-links-not-resolved.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2794-res-links-not-resolved.yaml
new file mode 100644
index 00000000000..f3a98360e6f
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2794-res-links-not-resolved.yaml
@@ -0,0 +1,4 @@
+---
+type: fix
+issue: 2794
+title: "When providing links for placeholder creation, DaoResourceLinkResolver expects just a single 'identifier=value' param, but it can be additional data, s.a. tags, extra identifiers, etc."
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2797-allow-multiple-typefilters-in-bulk-export.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2797-allow-multiple-typefilters-in-bulk-export.yaml
new file mode 100644
index 00000000000..327b7a5be20
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2797-allow-multiple-typefilters-in-bulk-export.yaml
@@ -0,0 +1,5 @@
+---
+type: fix
+issue: 2797
+title: "When initiating a FHIR bulk export, if more than one `_typeFilter` parameter was supplied
+ only the first one was respected. This has been corrected."
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2800-loinc-consumer-name-support.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2800-loinc-consumer-name-support.yaml
new file mode 100644
index 00000000000..3eff90b7316
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2800-loinc-consumer-name-support.yaml
@@ -0,0 +1,6 @@
+---
+type: add
+issue: 2800
+title: "Allowed the optional inclusion of the LOINC Consumer Names archive in addition to
+ the main LOINC distribution. If it is supplied, the consumer names CSV file will be scanned,
+ and all consumer names will be added to uploaded Concepts as additional designations"
\ No newline at end of file
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2803-loinc-linguistic-variants-support.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2803-loinc-linguistic-variants-support.yaml
new file mode 100644
index 00000000000..ec19770b035
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2803-loinc-linguistic-variants-support.yaml
@@ -0,0 +1,6 @@
+---
+type: add
+issue: 2803
+title: "Allowed the optional inclusion of the LOINC Linguistic Variants archive in addition to
+ the main LOINC distribution. If it is supplied, all linguistic variants files will be scanned,
+ and all translations will be added to uploaded Concepts as additional designations"
\ No newline at end of file
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2805-improve-package-load-errors.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2805-improve-package-load-errors.yaml
new file mode 100644
index 00000000000..35edc59668d
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2805-improve-package-load-errors.yaml
@@ -0,0 +1,4 @@
+---
+type: change
+issue: 2805
+title: "When the contents of a package are corrupt, the error messages now identify the corrupt element"
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2808-allow-package-loading-with-unnamed-partitions.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2808-allow-package-loading-with-unnamed-partitions.yaml
new file mode 100644
index 00000000000..24789d6335a
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2808-allow-package-loading-with-unnamed-partitions.yaml
@@ -0,0 +1,4 @@
+---
+type: fix
+issue: 2808
+title: "Loading packages would fail when partitioning was enabled with unnamed partitions. This has been fixed."
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2810-fix-patient-partition-mode-encounter.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2810-fix-patient-partition-mode-encounter.yaml
new file mode 100644
index 00000000000..a903004eeaa
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2810-fix-patient-partition-mode-encounter.yaml
@@ -0,0 +1,5 @@
+---
+type: fix
+issue: 2810
+title: "An issue in the FHIRPath evaluator prevented Encounters from being stored when using the new
+ PatientIdPartitionInterceptor. This has been corrected."
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_rules.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_rules.md
index 3075b67543d..50d5ac73fd7 100644
--- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_rules.md
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_rules.md
@@ -165,7 +165,7 @@ then the above `candidateSearchParams` and `candidateFilterSearchParams` would r
### matchFields
-Once the match candidates have been found, they are then each compared to the incoming Patient resource. This comparison is made across a list of `matchField`s. Each matchField returns `true` or `false` indicating whether the candidate and the incoming Patient match on that field. There are two types of matchFields: `matcher` and `similarity`. `matcher` matchFields return a `true` or `false` directly, whereas `similarity` matchFields return a score between 0.0 (no match) and 1.0 (exact match) and this score is translated to a `true/false` via a `matchThreshold`. E.g. if a `JARO_WINKLER` matchField is configured with a `matchThreshold` of 0.8 then that matchField will only return `true` if the `JARO_WINKLER` similarity evaluates to a score >= 8.0.
+Once the match candidates have been found, they are then each compared to the incoming Patient resource. This comparison is made across a list of `matchField`s. Each matchField returns `true` or `false` indicating whether the candidate and the incoming Patient match on that field. There are two types of matchFields: `matcher` and `similarity`. `matcher` matchFields return a `true` or `false` directly, whereas `similarity` matchFields return a score between 0.0 (no match) and 1.0 (exact match) and this score is translated to a `true/false` via a `matchThreshold`. E.g. if a `JARO_WINKLER` matchField is configured with a `matchThreshold` of 0.8 then that matchField will only return `true` if the `JARO_WINKLER` similarity evaluates to a score >= 0.8.
By default, all matchFields have `exact=false` which means that they will have all diacritical marks removed and all letters will be converted to upper case before matching. `exact=true` can be added to any matchField to compare the strings as they are originally capitalized and accented.
diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml
index cfd2e0c8cd7..2f66266f48e 100644
--- a/hapi-fhir-jacoco/pom.xml
+++ b/hapi-fhir-jacoco/pom.xml
@@ -11,7 +11,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml
index c2d87484e25..f5823a8c865 100644
--- a/hapi-fhir-jaxrsserver-base/pom.xml
+++ b/hapi-fhir-jaxrsserver-base/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jaxrsserver-example/pom.xml b/hapi-fhir-jaxrsserver-example/pom.xml
index a8098598dc3..627bf1c344b 100644
--- a/hapi-fhir-jaxrsserver-example/pom.xml
+++ b/hapi-fhir-jaxrsserver-example/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../pom.xml
diff --git a/hapi-fhir-jpaserver-api/pom.xml b/hapi-fhir-jpaserver-api/pom.xml
index d326afc4010..c30ed47c449 100644
--- a/hapi-fhir-jpaserver-api/pom.xml
+++ b/hapi-fhir-jpaserver-api/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml
index ba176a91bfd..833b54077b4 100644
--- a/hapi-fhir-jpaserver-base/pom.xml
+++ b/hapi-fhir-jpaserver-base/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java
index 7515fad98d7..466bbdfd7d8 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java
@@ -85,7 +85,7 @@ public class BulkDataExportProvider {
@OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") IPrimitiveType theOutputFormat,
@OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE, min = 0, max = 1, typeName = "string") IPrimitiveType theType,
@OperationParam(name = JpaConstants.PARAM_EXPORT_SINCE, min = 0, max = 1, typeName = "instant") IPrimitiveType theSince,
- @OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE_FILTER, min = 0, max = 1, typeName = "string") IPrimitiveType theTypeFilter,
+ @OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") List> theTypeFilter,
ServletRequestDetails theRequestDetails
) {
validatePreferAsyncHeader(theRequestDetails);
@@ -113,7 +113,7 @@ public class BulkDataExportProvider {
@OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") IPrimitiveType theOutputFormat,
@OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE, min = 0, max = 1, typeName = "string") IPrimitiveType theType,
@OperationParam(name = JpaConstants.PARAM_EXPORT_SINCE, min = 0, max = 1, typeName = "instant") IPrimitiveType theSince,
- @OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE_FILTER, min = 0, max = 1, typeName = "string") IPrimitiveType theTypeFilter,
+ @OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") List> theTypeFilter,
@OperationParam(name = JpaConstants.PARAM_EXPORT_MDM, min = 0, max = 1, typeName = "boolean") IPrimitiveType theMdm,
ServletRequestDetails theRequestDetails
) {
@@ -151,7 +151,7 @@ public class BulkDataExportProvider {
@OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") IPrimitiveType theOutputFormat,
@OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE, min = 0, max = 1, typeName = "string") IPrimitiveType theType,
@OperationParam(name = JpaConstants.PARAM_EXPORT_SINCE, min = 0, max = 1, typeName = "instant") IPrimitiveType theSince,
- @OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE_FILTER, min = 0, max = 1, typeName = "string") IPrimitiveType theTypeFilter,
+ @OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") List> theTypeFilter,
ServletRequestDetails theRequestDetails
) {
validatePreferAsyncHeader(theRequestDetails);
@@ -218,11 +218,11 @@ public class BulkDataExportProvider {
}
}
- private BulkDataExportOptions buildSystemBulkExportOptions(IPrimitiveType theOutputFormat, IPrimitiveType theType, IPrimitiveType theSince, IPrimitiveType theTypeFilter) {
+ private BulkDataExportOptions buildSystemBulkExportOptions(IPrimitiveType theOutputFormat, IPrimitiveType theType, IPrimitiveType theSince, List> theTypeFilter) {
return buildBulkDataExportOptions(theOutputFormat, theType, theSince, theTypeFilter, BulkDataExportOptions.ExportStyle.SYSTEM);
}
- private BulkDataExportOptions buildGroupBulkExportOptions(IPrimitiveType theOutputFormat, IPrimitiveType theType, IPrimitiveType theSince, IPrimitiveType theTypeFilter, IIdType theGroupId, IPrimitiveType theExpandMdm) {
+ private BulkDataExportOptions buildGroupBulkExportOptions(IPrimitiveType theOutputFormat, IPrimitiveType theType, IPrimitiveType theSince, List> theTypeFilter, IIdType theGroupId, IPrimitiveType theExpandMdm) {
BulkDataExportOptions bulkDataExportOptions = buildBulkDataExportOptions(theOutputFormat, theType, theSince, theTypeFilter, BulkDataExportOptions.ExportStyle.GROUP);
bulkDataExportOptions.setGroupId(theGroupId);
@@ -235,11 +235,11 @@ public class BulkDataExportProvider {
return bulkDataExportOptions;
}
- private BulkDataExportOptions buildPatientBulkExportOptions(IPrimitiveType theOutputFormat, IPrimitiveType theType, IPrimitiveType theSince, IPrimitiveType theTypeFilter) {
+ private BulkDataExportOptions buildPatientBulkExportOptions(IPrimitiveType theOutputFormat, IPrimitiveType theType, IPrimitiveType theSince, List> theTypeFilter) {
return buildBulkDataExportOptions(theOutputFormat, theType, theSince, theTypeFilter, BulkDataExportOptions.ExportStyle.PATIENT);
}
- private BulkDataExportOptions buildBulkDataExportOptions(IPrimitiveType theOutputFormat, IPrimitiveType theType, IPrimitiveType theSince, IPrimitiveType theTypeFilter, BulkDataExportOptions.ExportStyle theExportStyle) {
+ private BulkDataExportOptions buildBulkDataExportOptions(IPrimitiveType theOutputFormat, IPrimitiveType theType, IPrimitiveType theSince, List> theTypeFilter, BulkDataExportOptions.ExportStyle theExportStyle) {
String outputFormat = theOutputFormat != null ? theOutputFormat.getValueAsString() : null;
Set resourceTypes = null;
@@ -285,17 +285,22 @@ public class BulkDataExportProvider {
}
}
- private Set splitTypeFilters(IPrimitiveType theTypeFilter) {
+ private Set splitTypeFilters(List> theTypeFilter) {
if (theTypeFilter== null) {
return null;
}
- String typeFilterSring = theTypeFilter.getValueAsString();
- String[] typeFilters = typeFilterSring.split(FARM_TO_TABLE_TYPE_FILTER_REGEX);
- if (typeFilters == null || typeFilters.length == 0) {
- return null;
+
+ Set retVal = new HashSet<>();
+
+ for (IPrimitiveType next : theTypeFilter) {
+ String typeFilterString = next.getValueAsString();
+ Arrays
+ .stream(typeFilterString.split(FARM_TO_TABLE_TYPE_FILTER_REGEX))
+ .filter(StringUtils::isNotBlank)
+ .forEach(t->retVal.add(t));
}
- return new HashSet<>(Arrays.asList(typeFilters));
+ return retVal;
}
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java
index e79a9acfdb2..96e09ddeee5 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java
@@ -94,7 +94,8 @@ import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
import ca.uhn.fhir.jpa.search.builder.QueryStack;
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
-import ca.uhn.fhir.jpa.search.builder.predicate.CompositeUniqueSearchParameterPredicateBuilder;
+import ca.uhn.fhir.jpa.search.builder.predicate.ComboNonUniqueSearchParameterPredicateBuilder;
+import ca.uhn.fhir.jpa.search.builder.predicate.ComboUniqueSearchParameterPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.CoordsPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.ForcedIdPredicateBuilder;
@@ -609,8 +610,14 @@ public abstract class BaseConfig {
@Bean
@Scope("prototype")
- public CompositeUniqueSearchParameterPredicateBuilder newCompositeUniqueSearchParameterPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
- return new CompositeUniqueSearchParameterPredicateBuilder(theSearchSqlBuilder);
+ public ComboUniqueSearchParameterPredicateBuilder newComboUniqueSearchParameterPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
+ return new ComboUniqueSearchParameterPredicateBuilder(theSearchSqlBuilder);
+ }
+
+ @Bean
+ @Scope("prototype")
+ public ComboNonUniqueSearchParameterPredicateBuilder newComboNonUniqueSearchParameterPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
+ return new ComboNonUniqueSearchParameterPredicateBuilder(theSearchSqlBuilder);
}
@Bean
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java
index ada43d0f291..3ddf9760ecc 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java
@@ -23,7 +23,7 @@ package ca.uhn.fhir.jpa.config;
import ca.uhn.fhir.i18n.HapiLocalizer;
import ca.uhn.fhir.jpa.model.entity.ForcedId;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
-import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
+import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import org.hibernate.HibernateException;
import org.hibernate.PessimisticLockException;
@@ -81,7 +81,7 @@ public class HapiFhirHibernateJpaDialect extends HibernateJpaDialect {
if (constraintName.contains(ResourceHistoryTable.IDX_RESVER_ID_VER)) {
throw new ResourceVersionConflictException(messageToPrepend + myLocalizer.getMessage(HapiFhirHibernateJpaDialect.class, "resourceVersionConstraintFailure"));
}
- if (constraintName.contains(ResourceIndexedCompositeStringUnique.IDX_IDXCMPSTRUNIQ_STRING)) {
+ if (constraintName.contains(ResourceIndexedComboStringUnique.IDX_IDXCMPSTRUNIQ_STRING)) {
throw new ResourceVersionConflictException(messageToPrepend + myLocalizer.getMessage(HapiFhirHibernateJpaDialect.class, "resourceIndexedCompositeStringUniqueConstraintFailure"));
}
if (constraintName.contains(ForcedId.IDX_FORCEDID_TYPE_FID)) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
index 8f567f1ea3c..bd2e964e523 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
@@ -129,7 +129,6 @@ import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
-import javax.validation.constraints.Null;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.XMLEvent;
import java.util.ArrayList;
@@ -1404,7 +1403,7 @@ public abstract class BaseHapiFhirDao extends BaseStora
}
// Synchronize composite params
- mySearchParamWithInlineReferencesExtractor.storeCompositeStringUniques(newParams, entity, existingParams);
+ mySearchParamWithInlineReferencesExtractor.storeUniqueComboParameters(newParams, entity, existingParams);
}
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/LegacySearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/LegacySearchBuilder.java
index 1f259ecaa34..5ffbe5fe19e 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/LegacySearchBuilder.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/LegacySearchBuilder.java
@@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao;
* #L%
*/
+import ca.uhn.fhir.context.ComboSearchParamType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
@@ -42,7 +43,7 @@ import ca.uhn.fhir.jpa.entity.ResourceSearchView;
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
-import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
+import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
@@ -868,9 +869,11 @@ public class LegacySearchBuilder implements ISearchBuilder {
// Since we're going to remove elements below
theParams.values().forEach(nextAndList -> ensureSubListsAreWritable(nextAndList));
- List activeUniqueSearchParams = mySearchParamRegistry.getActiveUniqueSearchParams(myResourceName, theParams.keySet());
+ List activeUniqueSearchParams = mySearchParamRegistry.getActiveComboSearchParams(myResourceName, theParams.keySet());
if (activeUniqueSearchParams.size() > 0) {
+ Validate.isTrue(activeUniqueSearchParams.get(0).getComboSearchParamType()== ComboSearchParamType.UNIQUE, "Non unique combo parameters are not supported with the legacy search builder");
+
StringBuilder sb = new StringBuilder();
sb.append(myResourceName);
sb.append("?");
@@ -943,7 +946,7 @@ public class LegacySearchBuilder implements ISearchBuilder {
}
private void addPredicateCompositeStringUnique(@Nonnull SearchParameterMap theParams, String theIndexedString, RequestPartitionId theRequestPartitionId) {
- From, ResourceIndexedCompositeStringUnique> join = myQueryStack.createJoin(SearchBuilderJoinEnum.COMPOSITE_UNIQUE, null);
+ From, ResourceIndexedComboStringUnique> join = myQueryStack.createJoin(SearchBuilderJoinEnum.COMPOSITE_UNIQUE, null);
if (!theRequestPartitionId.isAllPartitions()) {
Integer partitionId = theRequestPartitionId.getFirstPartitionIdOrNull();
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedCompositeStringUniqueDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedComboStringUniqueDao.java
similarity index 60%
rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedCompositeStringUniqueDao.java
rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedComboStringUniqueDao.java
index 3274e5adeda..f68d552d6ce 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedCompositeStringUniqueDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedComboStringUniqueDao.java
@@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.dao.data;
* #L%
*/
-import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
+import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
@@ -28,15 +28,15 @@ import org.springframework.data.repository.query.Param;
import java.util.List;
-public interface IResourceIndexedCompositeStringUniqueDao extends JpaRepository {
+public interface IResourceIndexedComboStringUniqueDao extends JpaRepository {
- @Query("SELECT r FROM ResourceIndexedCompositeStringUnique r WHERE r.myIndexString = :str")
- ResourceIndexedCompositeStringUnique findByQueryString(@Param("str") String theQueryString);
+ @Query("SELECT r FROM ResourceIndexedComboStringUnique r WHERE r.myIndexString = :str")
+ ResourceIndexedComboStringUnique findByQueryString(@Param("str") String theQueryString);
- @Query("SELECT r FROM ResourceIndexedCompositeStringUnique r WHERE r.myResourceId = :resId")
- List findAllForResourceIdForUnitTest(@Param("resId") Long theResourceId);
+ @Query("SELECT r FROM ResourceIndexedComboStringUnique r WHERE r.myResourceId = :resId")
+ List findAllForResourceIdForUnitTest(@Param("resId") Long theResourceId);
@Modifying
- @Query("delete from ResourceIndexedCompositeStringUnique t WHERE t.myResourceId = :resid")
+ @Query("delete from ResourceIndexedComboStringUnique t WHERE t.myResourceId = :resid")
void deleteByResourceId(@Param("resid") Long theResourcePid);
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedComboTokensNonUniqueDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedComboTokensNonUniqueDao.java
new file mode 100644
index 00000000000..a4f8bf76593
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceIndexedComboTokensNonUniqueDao.java
@@ -0,0 +1,34 @@
+package ca.uhn.fhir.jpa.dao.data;
+
+/*-
+ * #%L
+ * HAPI FHIR JPA Server
+ * %%
+ * 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.jpa.model.entity.ResourceIndexedComboTokenNonUnique;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+public interface IResourceIndexedComboTokensNonUniqueDao extends JpaRepository {
+
+ @Modifying
+ @Query("DELETE FROM ResourceIndexedComboTokenNonUnique t WHERE t.myResourceId = :res_id")
+ void deleteByResourceId(@Param("res_id") Long theResourcePid);
+}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java
index 3ff4538cf7c..1fa83140368 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java
@@ -50,7 +50,8 @@ import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionResourceEntity;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTag;
-import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
+import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique;
+import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboTokenNonUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
@@ -136,7 +137,8 @@ public class ExpungeEverythingService {
counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamToken.class));
counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamUri.class));
counter.addAndGet(expungeEverythingByType(ResourceIndexedSearchParamCoords.class));
- counter.addAndGet(expungeEverythingByType(ResourceIndexedCompositeStringUnique.class));
+ counter.addAndGet(expungeEverythingByType(ResourceIndexedComboStringUnique.class));
+ counter.addAndGet(expungeEverythingByType(ResourceIndexedComboTokenNonUnique.class));
counter.addAndGet(expungeEverythingByType(ResourceLink.class));
counter.addAndGet(expungeEverythingByType(SearchResult.class));
counter.addAndGet(expungeEverythingByType(SearchInclude.class));
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceExpungeService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceExpungeService.java
index 9f8a489bb86..fa3041ad1c3 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceExpungeService.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceExpungeService.java
@@ -28,7 +28,8 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTagDao;
-import ca.uhn.fhir.jpa.dao.data.IResourceIndexedCompositeStringUniqueDao;
+import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboStringUniqueDao;
+import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboTokensNonUniqueDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamCoordsDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamDateDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamNumberDao;
@@ -95,7 +96,9 @@ public class ResourceExpungeService implements IResourceExpungeService {
@Autowired
private IResourceIndexedSearchParamNumberDao myResourceIndexedSearchParamNumberDao;
@Autowired
- private IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
+ private IResourceIndexedComboStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
+ @Autowired
+ private IResourceIndexedComboTokensNonUniqueDao myResourceIndexedComboTokensNonUniqueDao;
@Autowired
private IResourceLinkDao myResourceLinkDao;
@Autowired
@@ -289,9 +292,12 @@ public class ResourceExpungeService implements IResourceExpungeService {
if (resource == null || resource.isParamsTokenPopulated()) {
myResourceIndexedSearchParamTokenDao.deleteByResourceId(theResourceId);
}
- if (resource == null || resource.isParamsCompositeStringUniquePresent()) {
+ if (resource == null || resource.isParamsComboStringUniquePresent()) {
myResourceIndexedCompositeStringUniqueDao.deleteByResourceId(theResourceId);
}
+ if (resource == null || resource.isParamsComboTokensNonUniquePresent()) {
+ myResourceIndexedComboTokensNonUniqueDao.deleteByResourceId(theResourceId);
+ }
if (myDaoConfig.getIndexMissingFields() == DaoConfig.IndexEnabledEnum.ENABLED) {
mySearchParamPresentDao.deleteByResourceId(theResourceId);
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProvider.java
index 0175bf37b3f..6ccb4888bb0 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProvider.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProvider.java
@@ -40,6 +40,7 @@ public class ResourceTableFKProvider {
// SELECT FKTABLE_NAME, FKCOLUMN_NAME FROM CROSS_REFERENCES WHERE PKTABLE_NAME = 'HFJ_RESOURCE'
retval.add(new ResourceForeignKey("HFJ_FORCED_ID", "RESOURCE_PID"));
retval.add(new ResourceForeignKey("HFJ_IDX_CMP_STRING_UNIQ", "RES_ID"));
+ retval.add(new ResourceForeignKey("HFJ_IDX_CMB_TOK_NU", "RES_ID"));
retval.add(new ResourceForeignKey("HFJ_RES_LINK", "SRC_RESOURCE_ID"));
retval.add(new ResourceForeignKey("HFJ_RES_LINK", "TARGET_RESOURCE_ID"));
retval.add(new ResourceForeignKey("HFJ_RES_PARAM_PRESENT", "RES_ID"));
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolver.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolver.java
index 1ec1ff611b0..a219f39f292 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolver.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolver.java
@@ -40,6 +40,8 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.HapiExtensions;
import ca.uhn.fhir.util.TerserUtil;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URLEncodedUtils;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
@@ -54,6 +56,8 @@ import javax.annotation.Nullable;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
import java.util.Optional;
public class DaoResourceLinkResolver implements IResourceLinkResolver {
@@ -160,7 +164,7 @@ public class DaoResourceLinkResolver implements IResourceLinkResolver {
if (referenceIdentifier == null && referenceMatchUrlIdentifier != null) {
addMatchUrlIdentifierToTargetResource(theTargetResourceDef, theTargetResource, referenceMatchUrlIdentifier);
- } else if (referenceIdentifier!= null && referenceMatchUrlIdentifier == null) {
+ } else if (referenceIdentifier != null && referenceMatchUrlIdentifier == null) {
addSubjectIdentifierToTargetResource(theSourceReference, theTargetResourceDef, theTargetResource);
} else if (referenceIdentifier != null && referenceMatchUrlIdentifier != null) {
if (referenceIdentifier.equals(referenceMatchUrlIdentifier)) {
@@ -222,16 +226,33 @@ public class DaoResourceLinkResolver implements IResourceLinkResolver {
}
}
- private CanonicalIdentifier extractIdentifierFromUrl(String theValue) {
- if (!theValue.contains("identifier=")) {
+ /**
+ * Extracts the first available identifier from the URL part
+ *
+ * @param theValue Part of the URL to extract identifiers from
+ * @return Returns the first available identifier in the canonical form or null if URL contains no identifier param
+ * @throws IllegalArgumentException IllegalArgumentException is thrown in case identifier parameter can not be split using system|value
pattern.
+ */
+ protected CanonicalIdentifier extractIdentifierFromUrl(String theValue) {
+ int identifierIndex = theValue.indexOf("identifier=");
+ if (identifierIndex == -1) {
return null;
}
- CanonicalIdentifier identifier = new CanonicalIdentifier();
- String identifierString = theValue.substring(theValue.indexOf("=") + 1);
+
+ List params = URLEncodedUtils.parse(theValue.substring(identifierIndex), StandardCharsets.UTF_8, '&', ';');
+ Optional idOptional = params.stream().filter(p -> p.getName().equals("identifier")).findFirst();
+ if (!idOptional.isPresent()) {
+ return null;
+ }
+
+ NameValuePair id = idOptional.get();
+ String identifierString = id.getValue();
String[] split = identifierString.split("\\|");
if (split.length != 2) {
throw new IllegalArgumentException("Can't create a placeholder reference with identifier " + theValue + ". It is not a valid identifier");
}
+
+ CanonicalIdentifier identifier = new CanonicalIdentifier();
identifier.setSystem(split[0]);
identifier.setValue(split[1]);
return identifier;
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java
index 158d576ffbf..2cb3f374be1 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java
@@ -20,17 +20,11 @@ package ca.uhn.fhir.jpa.dao.index;
* #L%
*/
-import ca.uhn.fhir.jpa.api.config.DaoConfig;
-import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndex;
-import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
import ca.uhn.fhir.jpa.util.AddRemoveCount;
import com.google.common.annotations.VisibleForTesting;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.persistence.EntityManager;
@@ -43,15 +37,8 @@ import java.util.List;
@Service
public class DaoSearchParamSynchronizer {
- private static final Logger ourLog = LoggerFactory.getLogger(DaoSearchParamSynchronizer.class);
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
protected EntityManager myEntityManager;
- @Autowired
- private DaoConfig myDaoConfig;
- @Autowired
- private PartitionSettings myPartitionSettings;
- @Autowired
- private ModelConfig myModelConfig;
public AddRemoveCount synchronizeSearchParamsToDatabase(ResourceIndexedSearchParams theParams, ResourceTable theEntity, ResourceIndexedSearchParams existingParams) {
AddRemoveCount retVal = new AddRemoveCount();
@@ -65,6 +52,7 @@ public class DaoSearchParamSynchronizer {
synchronize(theEntity, retVal, theParams.myUriParams, existingParams.myUriParams);
synchronize(theEntity, retVal, theParams.myCoordsParams, existingParams.myCoordsParams);
synchronize(theEntity, retVal, theParams.myLinks, existingParams.myLinks);
+ synchronize(theEntity, retVal, theParams.myComboTokenNonUnique, existingParams.myComboTokenNonUnique);
// make sure links are indexed
theEntity.setResourceLinks(theParams.myLinks);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java
index 031d75bc608..69302a227ca 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java
@@ -36,6 +36,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import org.apache.commons.lang3.StringUtils;
@@ -389,9 +390,9 @@ public class IdHelperService {
return retVal;
}
- private RequestPartitionId replaceDefault(RequestPartitionId theRequestPartitionId) {
+ RequestPartitionId replaceDefault(RequestPartitionId theRequestPartitionId) {
if (myPartitionSettings.getDefaultPartitionId() != null) {
- if (theRequestPartitionId.hasDefaultPartitionId()) {
+ if (!theRequestPartitionId.isAllPartitions() && theRequestPartitionId.hasDefaultPartitionId()) {
List partitionIds = theRequestPartitionId
.getPartitionIds()
.stream()
@@ -577,6 +578,11 @@ public class IdHelperService {
}
}
+ @VisibleForTesting
+ void setPartitionSettingsForUnitTest(PartitionSettings thePartitionSettings) {
+ myPartitionSettings = thePartitionSettings;
+ }
+
public static boolean isValidPid(IIdType theId) {
if (theId == null) {
return false;
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java
index 379d19f5bd4..d057fb4c7cf 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java
@@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.index;
* #L%
*/
+import ca.uhn.fhir.context.ComboSearchParamType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
@@ -27,17 +28,19 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.MatchResourceUrlService;
-import ca.uhn.fhir.jpa.dao.data.IResourceIndexedCompositeStringUniqueDao;
+import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboStringUniqueDao;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId;
-import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
+import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique;
+import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboTokenNonUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService;
import ca.uhn.fhir.jpa.searchparam.util.JpaParamUtil;
import ca.uhn.fhir.model.api.IQueryParameterType;
+import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
@@ -46,15 +49,18 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
import ca.uhn.fhir.util.FhirTerser;
+import ca.uhn.fhir.util.StringUtil;
import ca.uhn.fhir.util.UrlUtil;
import com.google.common.annotations.VisibleForTesting;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
+import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
+import javax.annotation.Nonnull;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
@@ -64,8 +70,10 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@@ -92,7 +100,7 @@ public class SearchParamWithInlineReferencesExtractor {
@Autowired
private DaoSearchParamSynchronizer myDaoSearchParamSynchronizer;
@Autowired
- private IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
+ private IResourceIndexedComboStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
@Autowired
private PartitionSettings myPartitionSettings;
@@ -140,7 +148,7 @@ public class SearchParamWithInlineReferencesExtractor {
}
/*
- * Handle composites
+ * Handle combo parameters
*/
extractCompositeStringUniques(theEntity, theParams);
}
@@ -148,86 +156,152 @@ public class SearchParamWithInlineReferencesExtractor {
private void extractCompositeStringUniques(ResourceTable theEntity, ResourceIndexedSearchParams theParams) {
final String resourceType = theEntity.getResourceType();
- List uniqueSearchParams = mySearchParamRegistry.getActiveUniqueSearchParams(resourceType);
+ List comboSearchParams = mySearchParamRegistry.getActiveComboSearchParams(resourceType);
- for (RuntimeSearchParam next : uniqueSearchParams) {
+ for (RuntimeSearchParam next : comboSearchParams) {
+ switch (Objects.requireNonNull(next.getComboSearchParamType())) {
+ case UNIQUE:
+ extractComboUniqueParam(theEntity, theParams, resourceType, next);
+ break;
+ case NON_UNIQUE:
+ extractComboNonUniqueParam(theEntity, theParams, resourceType, next);
+ }
+ }
+ }
- List> partsChoices = new ArrayList<>();
+ private void extractComboNonUniqueParam(ResourceTable theEntity, ResourceIndexedSearchParams theParams, String theResourceType, RuntimeSearchParam theParam) {
+ Set queryStringsToPopulate = extractParameterCombinationsForComboParam(theParams, theResourceType, theParam);
- List compositeComponents = JpaParamUtil.resolveComponentParameters(mySearchParamRegistry, next);
- for (RuntimeSearchParam nextCompositeOf : compositeComponents) {
- Collection extends BaseResourceIndexedSearchParam> paramsListForCompositePart = null;
- Collection linksForCompositePart = null;
- Collection linksForCompositePartWantPaths = null;
- switch (nextCompositeOf.getParamType()) {
- case NUMBER:
- paramsListForCompositePart = theParams.myNumberParams;
- break;
- case DATE:
- paramsListForCompositePart = theParams.myDateParams;
- break;
- case STRING:
- paramsListForCompositePart = theParams.myStringParams;
- break;
- case TOKEN:
- paramsListForCompositePart = theParams.myTokenParams;
- break;
- case REFERENCE:
- linksForCompositePart = theParams.myLinks;
- linksForCompositePartWantPaths = new HashSet<>(nextCompositeOf.getPathsSplit());
- break;
- case QUANTITY:
- paramsListForCompositePart = theParams.myQuantityParams;
- break;
- case URI:
- paramsListForCompositePart = theParams.myUriParams;
- break;
- case SPECIAL:
- case COMPOSITE:
- case HAS:
- break;
- }
+ for (String nextQueryString : queryStringsToPopulate) {
+ ourLog.trace("Adding composite unique SP: {}", nextQueryString);
+ theParams.myComboTokenNonUnique.add(new ResourceIndexedComboTokenNonUnique(myPartitionSettings, theEntity, nextQueryString));
+ }
+ }
- ArrayList nextChoicesList = new ArrayList<>();
- partsChoices.add(nextChoicesList);
+ private void extractComboUniqueParam(ResourceTable theEntity, ResourceIndexedSearchParams theParams, String theResourceType, RuntimeSearchParam theParam) {
+ Set queryStringsToPopulate = extractParameterCombinationsForComboParam(theParams, theResourceType, theParam);
- String key = UrlUtil.escapeUrlParam(nextCompositeOf.getName());
- if (paramsListForCompositePart != null) {
- for (BaseResourceIndexedSearchParam nextParam : paramsListForCompositePart) {
- if (nextParam.getParamName().equals(nextCompositeOf.getName())) {
- IQueryParameterType nextParamAsClientParam = nextParam.toQueryParameterType();
- String value = nextParamAsClientParam.getValueAsQueryToken(myContext);
- if (isNotBlank(value)) {
- value = UrlUtil.escapeUrlParam(value);
- nextChoicesList.add(key + "=" + value);
- }
- }
+ for (String nextQueryString : queryStringsToPopulate) {
+ ourLog.trace("Adding composite unique SP: {}", nextQueryString);
+ theParams.myComboStringUniques.add(new ResourceIndexedComboStringUnique(theEntity, nextQueryString, theParam.getId()));
+ }
+ }
+
+ @Nonnull
+ private Set extractParameterCombinationsForComboParam(ResourceIndexedSearchParams theParams, String theResourceType, RuntimeSearchParam theParam) {
+ List> partsChoices = new ArrayList<>();
+
+ List compositeComponents = JpaParamUtil.resolveComponentParameters(mySearchParamRegistry, theParam);
+ for (RuntimeSearchParam nextCompositeOf : compositeComponents) {
+ Collection extends BaseResourceIndexedSearchParam> paramsListForCompositePart = findParameterIndexes(theParams, nextCompositeOf);
+
+ Collection linksForCompositePart = null;
+ switch (nextCompositeOf.getParamType()) {
+ case REFERENCE:
+ linksForCompositePart = theParams.myLinks;
+ break;
+ case NUMBER:
+ case DATE:
+ case STRING:
+ case TOKEN:
+ case QUANTITY:
+ case URI:
+ case SPECIAL:
+ case COMPOSITE:
+ case HAS:
+ break;
+ }
+
+ Collection linksForCompositePartWantPaths = null;
+ switch (nextCompositeOf.getParamType()) {
+ case REFERENCE:
+ linksForCompositePartWantPaths = new HashSet<>(nextCompositeOf.getPathsSplit());
+ break;
+ case NUMBER:
+ case DATE:
+ case STRING:
+ case TOKEN:
+ case QUANTITY:
+ case URI:
+ case SPECIAL:
+ case COMPOSITE:
+ case HAS:
+ break;
+ }
+
+ ArrayList nextChoicesList = new ArrayList<>();
+ partsChoices.add(nextChoicesList);
+
+ String key = UrlUtil.escapeUrlParam(nextCompositeOf.getName());
+ if (paramsListForCompositePart != null) {
+ for (BaseResourceIndexedSearchParam nextParam : paramsListForCompositePart) {
+ IQueryParameterType nextParamAsClientParam = nextParam.toQueryParameterType();
+ String value = nextParamAsClientParam.getValueAsQueryToken(myContext);
+
+ RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam(theResourceType, key);
+ if (theParam.getComboSearchParamType() == ComboSearchParamType.NON_UNIQUE && param != null && param.getParamType() == RestSearchParameterTypeEnum.STRING) {
+ value = StringUtil.normalizeStringForSearchIndexing(value);
}
- }
- if (linksForCompositePart != null) {
- for (ResourceLink nextLink : linksForCompositePart) {
- if (linksForCompositePartWantPaths.contains(nextLink.getSourcePath())) {
- assert isNotBlank(nextLink.getTargetResourceType());
- assert isNotBlank(nextLink.getTargetResourceId());
- String value = nextLink.getTargetResourceType() + "/" + nextLink.getTargetResourceId();
- if (isNotBlank(value)) {
- value = UrlUtil.escapeUrlParam(value);
- nextChoicesList.add(key + "=" + value);
- }
- }
+
+ if (isNotBlank(value)) {
+ value = UrlUtil.escapeUrlParam(value);
+ nextChoicesList.add(key + "=" + value);
}
}
}
-
- Set queryStringsToPopulate = ResourceIndexedSearchParams.extractCompositeStringUniquesValueChains(resourceType, partsChoices);
-
- for (String nextQueryString : queryStringsToPopulate) {
- if (isNotBlank(nextQueryString)) {
- ourLog.trace("Adding composite unique SP: {}", nextQueryString);
- theParams.myCompositeStringUniques.add(new ResourceIndexedCompositeStringUnique(theEntity, nextQueryString, next.getId()));
+ if (linksForCompositePart != null) {
+ for (ResourceLink nextLink : linksForCompositePart) {
+ if (linksForCompositePartWantPaths.contains(nextLink.getSourcePath())) {
+ assert isNotBlank(nextLink.getTargetResourceType());
+ assert isNotBlank(nextLink.getTargetResourceId());
+ String value = nextLink.getTargetResourceType() + "/" + nextLink.getTargetResourceId();
+ if (isNotBlank(value)) {
+ value = UrlUtil.escapeUrlParam(value);
+ nextChoicesList.add(key + "=" + value);
+ }
+ }
}
}
}
+
+ return ResourceIndexedSearchParams.extractCompositeStringUniquesValueChains(theResourceType, partsChoices);
+ }
+
+ @Nullable
+ private Collection extends BaseResourceIndexedSearchParam> findParameterIndexes(ResourceIndexedSearchParams theParams, RuntimeSearchParam nextCompositeOf) {
+ Collection extends BaseResourceIndexedSearchParam> paramsListForCompositePart = null;
+ switch (nextCompositeOf.getParamType()) {
+ case NUMBER:
+ paramsListForCompositePart = theParams.myNumberParams;
+ break;
+ case DATE:
+ paramsListForCompositePart = theParams.myDateParams;
+ break;
+ case STRING:
+ paramsListForCompositePart = theParams.myStringParams;
+ break;
+ case TOKEN:
+ paramsListForCompositePart = theParams.myTokenParams;
+ break;
+ case QUANTITY:
+ paramsListForCompositePart = theParams.myQuantityParams;
+ break;
+ case URI:
+ paramsListForCompositePart = theParams.myUriParams;
+ break;
+ case REFERENCE:
+ case SPECIAL:
+ case COMPOSITE:
+ case HAS:
+ break;
+ }
+ if (paramsListForCompositePart != null) {
+ paramsListForCompositePart = paramsListForCompositePart
+ .stream()
+ .filter(t->t.getParamName().equals(nextCompositeOf.getName()))
+ .collect(Collectors.toList());
+ }
+ return paramsListForCompositePart;
}
@@ -309,19 +383,21 @@ public class SearchParamWithInlineReferencesExtractor {
myDaoSearchParamSynchronizer = theDaoSearchParamSynchronizer;
}
- public void storeCompositeStringUniques(ResourceIndexedSearchParams theParams, ResourceTable theEntity, ResourceIndexedSearchParams existingParams) {
+ public void storeUniqueComboParameters(ResourceIndexedSearchParams theParams, ResourceTable theEntity, ResourceIndexedSearchParams theExistingParams) {
- // Store composite string uniques
+ /*
+ * String Uniques
+ */
if (myDaoConfig.isUniqueIndexesEnabled()) {
- for (ResourceIndexedCompositeStringUnique next : myDaoSearchParamSynchronizer.subtract(existingParams.myCompositeStringUniques, theParams.myCompositeStringUniques)) {
+ for (ResourceIndexedComboStringUnique next : myDaoSearchParamSynchronizer.subtract(theExistingParams.myComboStringUniques, theParams.myComboStringUniques)) {
ourLog.debug("Removing unique index: {}", next);
myEntityManager.remove(next);
- theEntity.getParamsCompositeStringUnique().remove(next);
+ theEntity.getParamsComboStringUnique().remove(next);
}
- boolean haveNewParams = false;
- for (ResourceIndexedCompositeStringUnique next : myDaoSearchParamSynchronizer.subtract(theParams.myCompositeStringUniques, existingParams.myCompositeStringUniques)) {
+ boolean haveNewStringUniqueParams = false;
+ for (ResourceIndexedComboStringUnique next : myDaoSearchParamSynchronizer.subtract(theParams.myComboStringUniques, theExistingParams.myComboStringUniques)) {
if (myDaoConfig.isUniqueIndexesCheckedBeforeSave()) {
- ResourceIndexedCompositeStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString());
+ ResourceIndexedComboStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString());
if (existing != null) {
String searchParameterId = "(unknown)";
@@ -335,12 +411,12 @@ public class SearchParamWithInlineReferencesExtractor {
}
ourLog.debug("Persisting unique index: {}", next);
myEntityManager.persist(next);
- haveNewParams = true;
+ haveNewStringUniqueParams = true;
}
- if (theParams.myCompositeStringUniques.size() > 0 || haveNewParams) {
- theEntity.setParamsCompositeStringUniquePresent(true);
+ if (theParams.myComboStringUniques.size() > 0 || haveNewStringUniqueParams) {
+ theEntity.setParamsComboStringUniquePresent(true);
} else {
- theEntity.setParamsCompositeStringUniquePresent(false);
+ theEntity.setParamsComboStringUniquePresent(false);
}
}
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryRootEntryResourceTable.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryRootEntryResourceTable.java
index a2bbf16192f..0655e7d5c3f 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryRootEntryResourceTable.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/querystack/QueryRootEntryResourceTable.java
@@ -170,7 +170,7 @@ class QueryRootEntryResourceTable extends QueryRootEntry {
join = myResourceTableRoot.join("mySearchParamPresents", JoinType.LEFT);
break;
case COMPOSITE_UNIQUE:
- join = myResourceTableRoot.join("myParamsCompositeStringUnique", JoinType.LEFT);
+ join = myResourceTableRoot.join("myParamsComboStringUnique", JoinType.LEFT);
break;
case RESOURCE_TAGS:
join = myResourceTableRoot.join("myTags", JoinType.LEFT);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptor.java
index 089d30c5dce..3706993ebb3 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptor.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptor.java
@@ -23,7 +23,6 @@ package ca.uhn.fhir.jpa.interceptor;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
-import ca.uhn.fhir.fhirpath.IFhirPath;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
@@ -31,6 +30,7 @@ import ca.uhn.fhir.interceptor.model.ReadPartitionIdRequestDetails;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor;
+import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@@ -45,7 +45,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.List;
-import java.util.Optional;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isBlank;
@@ -62,6 +61,9 @@ public class PatientIdPartitionInterceptor {
@Autowired
private FhirContext myFhirContext;
+ @Autowired
+ private ISearchParamExtractor mySearchParamExtractor;
+
/**
* Constructor
*/
@@ -72,9 +74,10 @@ public class PatientIdPartitionInterceptor {
/**
* Constructor
*/
- public PatientIdPartitionInterceptor(FhirContext theFhirContext) {
+ public PatientIdPartitionInterceptor(FhirContext theFhirContext, ISearchParamExtractor theSearchParamExtractor) {
this();
myFhirContext = theFhirContext;
+ mySearchParamExtractor = theSearchParamExtractor;
}
@Hook(Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE)
@@ -92,14 +95,15 @@ public class PatientIdPartitionInterceptor {
throw new MethodNotAllowedException("Patient resource IDs must be client-assigned in patient compartment mode");
}
} else {
- IFhirPath fhirPath = myFhirContext.newFhirPath();
compartmentIdentity = compartmentSps
.stream()
.flatMap(param -> Arrays.stream(BaseSearchParamExtractor.splitPathsR4(param.getPath())))
.filter(StringUtils::isNotBlank)
- .map(path -> fhirPath.evaluateFirst(theResource, path, IBaseReference.class))
- .filter(Optional::isPresent)
- .map(Optional::get)
+ .map(path -> mySearchParamExtractor.getPathValueExtractor(theResource, path).get())
+ .filter(t -> !t.isEmpty())
+ .map(t -> t.get(0))
+ .filter(t -> t instanceof IBaseReference)
+ .map(t -> (IBaseReference) t)
.map(t -> t.getReferenceElement().getValue())
.map(t -> new IdType(t).getIdPart())
.filter(StringUtils::isNotBlank)
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/JpaPackageCache.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/JpaPackageCache.java
index 880b505887a..aa57973a90d 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/JpaPackageCache.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/JpaPackageCache.java
@@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.packages;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
+import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
@@ -331,7 +332,11 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
if (myPartitionSettings.isPartitioningEnabled()) {
SystemRequestDetails requestDetails = new SystemRequestDetails();
- requestDetails.setTenantId(JpaConstants.DEFAULT_PARTITION_NAME);
+ if (myPartitionSettings.isUnnamedPartitionMode() && myPartitionSettings.getDefaultPartitionId() != null) {
+ requestDetails.setRequestPartitionId(RequestPartitionId.fromPartitionId(myPartitionSettings.getDefaultPartitionId()));
+ } else {
+ requestDetails.setTenantId(JpaConstants.DEFAULT_PARTITION_NAME);
+ }
return (ResourceTable) getBinaryDao().create(theResourceBinary, requestDetails).getEntity();
} else {
return (ResourceTable) getBinaryDao().create(theResourceBinary).getEntity();
@@ -472,6 +477,8 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
}
private IBaseResource loadPackageEntity(NpmPackageVersionResourceEntity contents) {
+ try {
+
ResourcePersistentId binaryPid = new ResourcePersistentId(contents.getResourceBinary().getId());
IBaseBinary binary = getBinaryDao().readByPid(binaryPid);
byte[] resourceContentsBytes = BinaryUtil.getOrCreateData(myCtx, binary).getValue();
@@ -479,6 +486,9 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac
FhirContext packageContext = getFhirContext(contents.getFhirVersion());
return EncodingEnum.detectEncoding(resourceContents).newParser(packageContext).parseResource(resourceContents);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to load package resource " + contents, e);
+ }
}
@Override
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java
index d3d3bbda7f3..5a463fca932 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java
@@ -359,7 +359,6 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc {
private IBundleProvider searchResource(IFhirResourceDao theDao, SearchParameterMap theMap) {
if (myPartitionSettings.isPartitioningEnabled()) {
SystemRequestDetails requestDetails = new SystemRequestDetails();
-// requestDetails.setTenantId(JpaConstants.DEFAULT_PARTITION_NAME);
return theDao.search(theMap, requestDetails);
} else {
return theDao.search(theMap);
@@ -369,7 +368,6 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc {
private void createResource(IFhirResourceDao theDao, IBaseResource theResource) {
if (myPartitionSettings.isPartitioningEnabled()) {
SystemRequestDetails requestDetails = new SystemRequestDetails();
- requestDetails.setTenantId(JpaConstants.DEFAULT_PARTITION_NAME);
theDao.create(theResource, requestDetails);
} else {
theDao.create(theResource);
@@ -379,7 +377,6 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc {
private DaoMethodOutcome updateResource(IFhirResourceDao theDao, IBaseResource theResource) {
if (myPartitionSettings.isPartitioningEnabled()) {
SystemRequestDetails requestDetails = new SystemRequestDetails();
- requestDetails.setTenantId(JpaConstants.DEFAULT_PARTITION_NAME);
return theDao.update(theResource, requestDetails);
} else {
return theDao.update(theResource);
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 fddbab11fb0..253b07e33b6 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
@@ -34,7 +34,8 @@ import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import ca.uhn.fhir.jpa.search.builder.predicate.BaseJoiningPredicateBuilder;
-import ca.uhn.fhir.jpa.search.builder.predicate.CompositeUniqueSearchParameterPredicateBuilder;
+import ca.uhn.fhir.jpa.search.builder.predicate.ComboNonUniqueSearchParameterPredicateBuilder;
+import ca.uhn.fhir.jpa.search.builder.predicate.ComboUniqueSearchParameterPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.CoordsPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.ForcedIdPredicateBuilder;
@@ -1214,11 +1215,18 @@ public class QueryStack {
}
public void addPredicateCompositeUnique(String theIndexString, RequestPartitionId theRequestPartitionId) {
- CompositeUniqueSearchParameterPredicateBuilder predicateBuilder = mySqlBuilder.addCompositeUniquePredicateBuilder();
+ ComboUniqueSearchParameterPredicateBuilder predicateBuilder = mySqlBuilder.addComboUniquePredicateBuilder();
Condition predicate = predicateBuilder.createPredicateIndexString(theRequestPartitionId, theIndexString);
mySqlBuilder.addPredicate(predicate);
}
+ public void addPredicateCompositeNonUnique(String theIndexString, RequestPartitionId theRequestPartitionId) {
+ ComboNonUniqueSearchParameterPredicateBuilder predicateBuilder = mySqlBuilder.addComboNonUniquePredicateBuilder();
+ Condition predicate = predicateBuilder.createPredicateHashComplete(theRequestPartitionId, theIndexString);
+ mySqlBuilder.addPredicate(predicate);
+ }
+
+
public void addPredicateEverythingOperation(String theResourceName, Long theTargetPid) {
ResourceLinkPredicateBuilder table = mySqlBuilder.addReferencePredicateBuilder(this, null);
Condition predicate = table.createEverythingPredicate(theResourceName, theTargetPid);
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 85fc3fb7ddf..c116819da7e 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
@@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.search.builder;
* #L%
*/
+import ca.uhn.fhir.context.ComboSearchParamType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
@@ -85,6 +86,7 @@ import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.StopWatch;
+import ca.uhn.fhir.util.StringUtil;
import ca.uhn.fhir.util.UrlUtil;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists;
@@ -119,6 +121,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
@@ -213,7 +216,7 @@ public class SearchBuilder implements ISearchBuilder {
// Attempt to lookup via composite unique key.
if (isCompositeUniqueSpCandidate()) {
- attemptCompositeUniqueSpProcessing(theQueryStack, theParams, theRequest);
+ attemptComboUniqueSpProcessing(theQueryStack, theParams, theRequest);
}
SearchContainedModeEnum searchContainedMode = theParams.getSearchContainedMode();
@@ -366,7 +369,10 @@ public class SearchBuilder implements ISearchBuilder {
QueryStack queryStack3 = new QueryStack(theParams, myDaoConfig, myDaoConfig.getModelConfig(), myContext, sqlBuilder, mySearchParamRegistry, myPartitionSettings);
if (theParams.keySet().size() > 1 || theParams.getSort() != null || theParams.keySet().contains(Constants.PARAM_HAS)) {
- sqlBuilder.setNeedResourceTableRoot(true);
+ List activeComboParams = mySearchParamRegistry.getActiveComboSearchParams(myResourceName, theParams.keySet());
+ if (activeComboParams.isEmpty()) {
+ sqlBuilder.setNeedResourceTableRoot(true);
+ }
}
JdbcTemplate jdbcTemplate = new JdbcTemplate(myEntityManagerFactory.getDataSource());
@@ -1009,12 +1015,34 @@ public class SearchBuilder implements ISearchBuilder {
}
}
- private void attemptCompositeUniqueSpProcessing(QueryStack theQueryStack3, @Nonnull SearchParameterMap theParams, RequestDetails theRequest) {
- // Since we're going to remove elements below
- theParams.values().forEach(nextAndList -> ensureSubListsAreWritable(nextAndList));
+ private void attemptComboUniqueSpProcessing(QueryStack theQueryStack3, @Nonnull SearchParameterMap theParams, RequestDetails theRequest) {
+ RuntimeSearchParam comboParam = null;
+ List comboParamNames = null;
+ List exactMatchParams = mySearchParamRegistry.getActiveComboSearchParams(myResourceName, theParams.keySet());
+ if (exactMatchParams.size() > 0) {
+ comboParam = exactMatchParams.get(0);
+ comboParamNames = new ArrayList<>(theParams.keySet());
+ }
- List activeUniqueSearchParams = mySearchParamRegistry.getActiveUniqueSearchParams(myResourceName, theParams.keySet());
- if (activeUniqueSearchParams.size() > 0) {
+ if (comboParam == null) {
+ List candidateComboParams = mySearchParamRegistry.getActiveComboSearchParams(myResourceName);
+ for (RuntimeSearchParam nextCandidate : candidateComboParams) {
+ List nextCandidateParamNames = JpaParamUtil
+ .resolveComponentParameters(mySearchParamRegistry, nextCandidate)
+ .stream()
+ .map(t -> t.getName())
+ .collect(Collectors.toList());
+ if (theParams.keySet().containsAll(nextCandidateParamNames)) {
+ comboParam = nextCandidate;
+ comboParamNames = nextCandidateParamNames;
+ break;
+ }
+ }
+ }
+
+ if (comboParam != null) {
+ // Since we're going to remove elements below
+ theParams.values().forEach(nextAndList -> ensureSubListsAreWritable(nextAndList));
StringBuilder sb = new StringBuilder();
sb.append(myResourceName);
@@ -1022,9 +1050,8 @@ public class SearchBuilder implements ISearchBuilder {
boolean first = true;
- ArrayList keys = new ArrayList<>(theParams.keySet());
- Collections.sort(keys);
- for (String nextParamName : keys) {
+ Collections.sort(comboParamNames);
+ for (String nextParamName : comboParamNames) {
List> nextValues = theParams.get(nextParamName);
nextParamName = UrlUtil.escapeUrlParam(nextParamName);
@@ -1047,6 +1074,13 @@ public class SearchBuilder implements ISearchBuilder {
List extends IQueryParameterType> nextAnd = nextValues.remove(0);
IQueryParameterType nextOr = nextAnd.remove(0);
String nextOrValue = nextOr.getValueAsQueryToken(myContext);
+
+ if (comboParam.getComboSearchParamType() == ComboSearchParamType.NON_UNIQUE) {
+ if (nextParamDef.getParamType() == RestSearchParameterTypeEnum.STRING) {
+ nextOrValue = StringUtil.normalizeStringForSearchIndexing(nextOrValue);
+ }
+ }
+
nextOrValue = UrlUtil.escapeUrlParam(nextOrValue);
if (first) {
@@ -1061,18 +1095,25 @@ public class SearchBuilder implements ISearchBuilder {
if (sb != null) {
String indexString = sb.toString();
- ourLog.debug("Checking for unique index for query: {}", indexString);
+ ourLog.debug("Checking for {} combo index for query: {}", comboParam.getComboSearchParamType(), indexString);
// Interceptor broadcast: JPA_PERFTRACE_INFO
StorageProcessingMessage msg = new StorageProcessingMessage()
- .setMessage("Using unique index for query for search: " + indexString);
+ .setMessage("Using " + comboParam.getComboSearchParamType() + " index for query for search: " + indexString);
HookParams params = new HookParams()
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, msg);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
- theQueryStack3.addPredicateCompositeUnique(indexString, myRequestPartitionId);
+ switch (comboParam.getComboSearchParamType()) {
+ case UNIQUE:
+ theQueryStack3.addPredicateCompositeUnique(indexString, myRequestPartitionId);
+ break;
+ case NON_UNIQUE:
+ theQueryStack3.addPredicateCompositeNonUnique(indexString, myRequestPartitionId);
+ break;
+ }
// Remove any empty parameters remaining after this
theParams.clean();
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ComboNonUniqueSearchParameterPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ComboNonUniqueSearchParameterPredicateBuilder.java
new file mode 100644
index 00000000000..6e331f0ee68
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ComboNonUniqueSearchParameterPredicateBuilder.java
@@ -0,0 +1,47 @@
+package ca.uhn.fhir.jpa.search.builder.predicate;
+
+/*-
+ * #%L
+ * HAPI FHIR JPA Server
+ * %%
+ * 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.interceptor.model.RequestPartitionId;
+import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
+import com.healthmarketscience.sqlbuilder.BinaryCondition;
+import com.healthmarketscience.sqlbuilder.Condition;
+import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
+
+public class ComboNonUniqueSearchParameterPredicateBuilder extends BaseSearchParamPredicateBuilder {
+
+ private final DbColumn myColumnIndexString;
+
+ /**
+ * Constructor
+ */
+ public ComboNonUniqueSearchParameterPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
+ super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_IDX_CMB_TOK_NU"));
+
+ myColumnIndexString = getTable().addColumn("IDX_STRING");
+ }
+
+
+ public Condition createPredicateHashComplete(RequestPartitionId theRequestPartitionId, String theIndexString) {
+ BinaryCondition predicate = BinaryCondition.equalTo(myColumnIndexString, generatePlaceholder(theIndexString));
+ return combineWithRequestPartitionIdPredicate(theRequestPartitionId, predicate);
+ }
+}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/CompositeUniqueSearchParameterPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ComboUniqueSearchParameterPredicateBuilder.java
similarity index 88%
rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/CompositeUniqueSearchParameterPredicateBuilder.java
rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ComboUniqueSearchParameterPredicateBuilder.java
index b3e471857e0..a6d0f226f25 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/CompositeUniqueSearchParameterPredicateBuilder.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ComboUniqueSearchParameterPredicateBuilder.java
@@ -26,14 +26,14 @@ import com.healthmarketscience.sqlbuilder.BinaryCondition;
import com.healthmarketscience.sqlbuilder.Condition;
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
-public class CompositeUniqueSearchParameterPredicateBuilder extends BaseSearchParamPredicateBuilder {
+public class ComboUniqueSearchParameterPredicateBuilder extends BaseSearchParamPredicateBuilder {
private final DbColumn myColumnString;
/**
* Constructor
*/
- public CompositeUniqueSearchParameterPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
+ public ComboUniqueSearchParameterPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_IDX_CMP_STRING_UNIQ"));
myColumnString = getTable().addColumn("IDX_STRING");
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java
index c974f8a3bac..582e4eebbaf 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java
@@ -27,7 +27,8 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.search.builder.QueryStack;
import ca.uhn.fhir.jpa.search.builder.predicate.BaseJoiningPredicateBuilder;
-import ca.uhn.fhir.jpa.search.builder.predicate.CompositeUniqueSearchParameterPredicateBuilder;
+import ca.uhn.fhir.jpa.search.builder.predicate.ComboNonUniqueSearchParameterPredicateBuilder;
+import ca.uhn.fhir.jpa.search.builder.predicate.ComboUniqueSearchParameterPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.CoordsPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.ForcedIdPredicateBuilder;
@@ -145,12 +146,20 @@ public class SearchQueryBuilder {
/**
* Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a Composite Unique search parameter
*/
- public CompositeUniqueSearchParameterPredicateBuilder addCompositeUniquePredicateBuilder() {
- CompositeUniqueSearchParameterPredicateBuilder retVal = mySqlBuilderFactory.newCompositeUniqueSearchParameterPredicateBuilder(this);
+ public ComboUniqueSearchParameterPredicateBuilder addComboUniquePredicateBuilder() {
+ ComboUniqueSearchParameterPredicateBuilder retVal = mySqlBuilderFactory.newComboUniqueSearchParameterPredicateBuilder(this);
addTable(retVal, null);
return retVal;
}
+ /**
+ * Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a Composite Unique search parameter
+ */
+ public ComboNonUniqueSearchParameterPredicateBuilder addComboNonUniquePredicateBuilder() {
+ ComboNonUniqueSearchParameterPredicateBuilder retVal = mySqlBuilderFactory.newComboNonUniqueSearchParameterPredicateBuilder(this);
+ addTable(retVal, null);
+ return retVal;
+ }
/**
* Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a COORDS search parameter
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SqlObjectFactory.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SqlObjectFactory.java
index e0fcdbb0916..0c1ecb0ce19 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SqlObjectFactory.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SqlObjectFactory.java
@@ -21,7 +21,8 @@ package ca.uhn.fhir.jpa.search.builder.sql;
*/
import ca.uhn.fhir.jpa.search.builder.QueryStack;
-import ca.uhn.fhir.jpa.search.builder.predicate.CompositeUniqueSearchParameterPredicateBuilder;
+import ca.uhn.fhir.jpa.search.builder.predicate.ComboNonUniqueSearchParameterPredicateBuilder;
+import ca.uhn.fhir.jpa.search.builder.predicate.ComboUniqueSearchParameterPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.CoordsPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.ForcedIdPredicateBuilder;
@@ -45,10 +46,15 @@ public class SqlObjectFactory {
@Autowired
private ApplicationContext myApplicationContext;
- public CompositeUniqueSearchParameterPredicateBuilder newCompositeUniqueSearchParameterPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
- return myApplicationContext.getBean(CompositeUniqueSearchParameterPredicateBuilder.class, theSearchSqlBuilder);
+ public ComboUniqueSearchParameterPredicateBuilder newComboUniqueSearchParameterPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
+ return myApplicationContext.getBean(ComboUniqueSearchParameterPredicateBuilder.class, theSearchSqlBuilder);
}
+ public ComboNonUniqueSearchParameterPredicateBuilder newComboNonUniqueSearchParameterPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
+ return myApplicationContext.getBean(ComboNonUniqueSearchParameterPredicateBuilder.class, theSearchSqlBuilder);
+ }
+
+
public CoordsPredicateBuilder coordsPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
return myApplicationContext.getBean(CoordsPredicateBuilder.class, theSearchSqlBuilder);
}
@@ -112,4 +118,5 @@ public class SqlObjectFactory {
public SearchQueryExecutor newSearchQueryExecutor(GeneratedSql theGeneratedSql, Integer theMaxResultsToFetch) {
return myApplicationContext.getBean(SearchQueryExecutor.class, theGeneratedSql, theMaxResultsToFetch);
}
+
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java
index 58d7b6c2ce6..88952b0cc78 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java
@@ -12,6 +12,7 @@ import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet;
import ca.uhn.fhir.jpa.term.icd10cm.Icd10CmLoader;
import ca.uhn.fhir.jpa.term.loinc.LoincAnswerListHandler;
import ca.uhn.fhir.jpa.term.loinc.LoincAnswerListLinkHandler;
+import ca.uhn.fhir.jpa.term.loinc.LoincConsumerNameHandler;
import ca.uhn.fhir.jpa.term.loinc.LoincDocumentOntologyHandler;
import ca.uhn.fhir.jpa.term.loinc.LoincGroupFileHandler;
import ca.uhn.fhir.jpa.term.loinc.LoincGroupTermsFileHandler;
@@ -19,6 +20,8 @@ import ca.uhn.fhir.jpa.term.loinc.LoincHandler;
import ca.uhn.fhir.jpa.term.loinc.LoincHierarchyHandler;
import ca.uhn.fhir.jpa.term.loinc.LoincIeeeMedicalDeviceCodeHandler;
import ca.uhn.fhir.jpa.term.loinc.LoincImagingDocumentCodeHandler;
+import ca.uhn.fhir.jpa.term.loinc.LoincLinguisticVariantHandler;
+import ca.uhn.fhir.jpa.term.loinc.LoincLinguisticVariantsHandler;
import ca.uhn.fhir.jpa.term.loinc.LoincParentGroupFileHandler;
import ca.uhn.fhir.jpa.term.loinc.LoincPartHandler;
import ca.uhn.fhir.jpa.term.loinc.LoincPartLinkHandler;
@@ -90,6 +93,8 @@ import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERL
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERLIST_LINK_FILE;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERLIST_LINK_FILE_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_VERSION;
+import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONSUMER_NAME_FILE;
+import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONSUMER_NAME_FILE_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_DOCUMENT_ONTOLOGY_FILE;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_DOCUMENT_ONTOLOGY_FILE_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_FILE;
@@ -104,6 +109,10 @@ import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_IEEE_ME
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_IEEE_MEDICAL_DEVICE_CODE_MAPPING_TABLE_FILE_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_IMAGING_DOCUMENT_CODES_FILE;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_IMAGING_DOCUMENT_CODES_FILE_DEFAULT;
+import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_LINGUISTIC_VARIANTS_FILE;
+import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_LINGUISTIC_VARIANTS_FILE_DEFAULT;
+import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_LINGUISTIC_VARIANTS_PATH;
+import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_LINGUISTIC_VARIANTS_PATH_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_PARENT_GROUP_FILE;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_PARENT_GROUP_FILE_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_PART_FILE;
@@ -249,7 +258,12 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc {
uploadProperties.getProperty(LOINC_GROUP_TERMS_FILE.getCode(), LOINC_GROUP_TERMS_FILE_DEFAULT.getCode()),
uploadProperties.getProperty(LOINC_PARENT_GROUP_FILE.getCode(), LOINC_PARENT_GROUP_FILE_DEFAULT.getCode()),
uploadProperties.getProperty(LOINC_TOP2000_COMMON_LAB_RESULTS_SI_FILE.getCode(), LOINC_TOP2000_COMMON_LAB_RESULTS_SI_FILE_DEFAULT.getCode()),
- uploadProperties.getProperty(LOINC_TOP2000_COMMON_LAB_RESULTS_US_FILE.getCode(), LOINC_TOP2000_COMMON_LAB_RESULTS_US_FILE_DEFAULT.getCode())
+ uploadProperties.getProperty(LOINC_TOP2000_COMMON_LAB_RESULTS_US_FILE.getCode(), LOINC_TOP2000_COMMON_LAB_RESULTS_US_FILE_DEFAULT.getCode()),
+
+ //-- optional consumer name
+ uploadProperties.getProperty(LOINC_CONSUMER_NAME_FILE.getCode(), LOINC_CONSUMER_NAME_FILE_DEFAULT.getCode()),
+ uploadProperties.getProperty(LOINC_LINGUISTIC_VARIANTS_FILE.getCode(), LOINC_LINGUISTIC_VARIANTS_FILE_DEFAULT.getCode())
+
);
descriptors.verifyOptionalFilesExist(optionalFilenameFragments);
@@ -567,6 +581,8 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc {
final List valueSets = new ArrayList<>();
final List conceptMaps = new ArrayList<>();
+ final List linguisticVariants = new ArrayList<>();
+
LoincXmlFileZipContentsHandler loincXmlHandler = new LoincXmlFileZipContentsHandler();
iterateOverZipFile(theDescriptors, "loinc.xml", false, false, loincXmlHandler);
String loincCsString = loincXmlHandler.getContents();
@@ -674,6 +690,21 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc {
iterateOverZipFileCsvOptional(theDescriptors, theUploadProperties.getProperty(LOINC_PART_LINK_FILE_PRIMARY.getCode(), LOINC_PART_LINK_FILE_PRIMARY_DEFAULT.getCode()), handler, ',', QuoteMode.NON_NUMERIC, false);
iterateOverZipFileCsvOptional(theDescriptors, theUploadProperties.getProperty(LOINC_PART_LINK_FILE_SUPPLEMENTARY.getCode(), LOINC_PART_LINK_FILE_SUPPLEMENTARY_DEFAULT.getCode()), handler, ',', QuoteMode.NON_NUMERIC, false);
+ // Consumer Name
+ handler = new LoincConsumerNameHandler(code2concept);
+ iterateOverZipFileCsvOptional(theDescriptors, theUploadProperties.getProperty(LOINC_CONSUMER_NAME_FILE.getCode(), LOINC_CONSUMER_NAME_FILE_DEFAULT.getCode()), handler, ',', QuoteMode.NON_NUMERIC, false);
+
+ // Linguistic Variants
+ handler = new LoincLinguisticVariantsHandler(linguisticVariants);
+ iterateOverZipFileCsvOptional(theDescriptors, theUploadProperties.getProperty(LOINC_LINGUISTIC_VARIANTS_FILE.getCode(), LOINC_LINGUISTIC_VARIANTS_FILE_DEFAULT.getCode()), handler, ',', QuoteMode.NON_NUMERIC, false);
+
+ String langFileName = null;
+ for (LoincLinguisticVariantsHandler.LinguisticVariant linguisticVariant : linguisticVariants) {
+ handler = new LoincLinguisticVariantHandler(code2concept, linguisticVariant.getLanguageCode());
+ langFileName = linguisticVariant.getLinguisticVariantFileName();
+ iterateOverZipFileCsvOptional(theDescriptors, theUploadProperties.getProperty(LOINC_LINGUISTIC_VARIANTS_PATH.getCode() + langFileName, LOINC_LINGUISTIC_VARIANTS_PATH_DEFAULT.getCode() + langFileName), handler, ',', QuoteMode.NON_NUMERIC, false);
+ }
+
if (theCloseFiles) {
IOUtils.closeQuietly(theDescriptors);
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincConsumerNameHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincConsumerNameHandler.java
new file mode 100644
index 00000000000..8a1a07f1d79
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincConsumerNameHandler.java
@@ -0,0 +1,63 @@
+package ca.uhn.fhir.jpa.term.loinc;
+
+/*-
+ * #%L
+ * HAPI FHIR JPA Server
+ * %%
+ * 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.jpa.entity.TermConcept;
+import ca.uhn.fhir.jpa.term.IZipContentsHandlerCsv;
+import org.apache.commons.csv.CSVRecord;
+
+import java.util.Map;
+
+import static org.apache.commons.lang3.StringUtils.isBlank;
+import static org.apache.commons.lang3.StringUtils.trim;
+
+public class LoincConsumerNameHandler implements IZipContentsHandlerCsv {
+
+ private final Map myCode2Concept;
+
+ public LoincConsumerNameHandler(Map theCode2concept) {
+ myCode2Concept = theCode2concept;
+ }
+
+ @Override
+ public void accept(CSVRecord theRecord) {
+
+ String loincNumber = trim(theRecord.get("LoincNumber"));
+ if (isBlank(loincNumber)) {
+ return;
+ }
+
+ String consumerName = trim(theRecord.get("ConsumerName"));
+ if (isBlank(consumerName)) {
+ return;
+ }
+
+ TermConcept loincCode = myCode2Concept.get(loincNumber);
+ if (loincCode == null) {
+ return;
+ }
+
+ loincCode.addDesignation()
+ .setUseDisplay("ConsumerName")
+ .setValue(consumerName);
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincLinguisticVariantHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincLinguisticVariantHandler.java
new file mode 100644
index 00000000000..a794d120a5a
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincLinguisticVariantHandler.java
@@ -0,0 +1,87 @@
+package ca.uhn.fhir.jpa.term.loinc;
+
+import static org.apache.commons.lang3.StringUtils.isBlank;
+import static org.apache.commons.lang3.StringUtils.trim;
+
+import java.util.Map;
+
+import org.apache.commons.csv.CSVRecord;
+
+import ca.uhn.fhir.jpa.entity.TermConcept;
+import ca.uhn.fhir.jpa.term.IZipContentsHandlerCsv;
+import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
+
+/*-
+ * #%L
+ * HAPI FHIR JPA Server
+ * %%
+ * 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 class LoincLinguisticVariantHandler implements IZipContentsHandlerCsv {
+
+ private final Map myCode2Concept;
+ private final String myLanguageCode;
+
+ public LoincLinguisticVariantHandler(Map theCode2Concept, String theLanguageCode) {
+ myCode2Concept = theCode2Concept;
+ myLanguageCode = theLanguageCode;
+ }
+
+ @Override
+ public void accept(CSVRecord theRecord) {
+
+ String loincNumber = trim(theRecord.get("LOINC_NUM"));
+ if (isBlank(loincNumber)) {
+ return;
+ }
+
+ TermConcept concept = myCode2Concept.get(loincNumber);
+ if (concept == null) {
+ return;
+ }
+
+ addDesignation(theRecord, concept, "COMPONENT");
+ addDesignation(theRecord, concept, "PROPERTY");
+ addDesignation(theRecord, concept, "TIME_ASPCT");
+ addDesignation(theRecord, concept, "SYSTEM");
+ addDesignation(theRecord, concept, "SCALE_TYP");
+
+ addDesignation(theRecord, concept, "METHOD_TYP");
+ addDesignation(theRecord, concept, "CLASS");
+ addDesignation(theRecord, concept, "SHORTNAME");
+ addDesignation(theRecord, concept, "LONG_COMMON_NAME");
+ addDesignation(theRecord, concept, "RELATEDNAMES2");
+
+ addDesignation(theRecord, concept, "LinguisticVariantDisplayName");
+
+ }
+
+ private void addDesignation(CSVRecord theRecord, TermConcept concept, String fieldName) {
+
+ String field = trim(theRecord.get(fieldName));
+ if (isBlank(field)) {
+ return;
+ }
+
+ concept.addDesignation()
+ .setLanguage(myLanguageCode)
+ .setUseSystem(ITermLoaderSvc.LOINC_URI)
+ .setUseCode(fieldName)
+ .setUseDisplay(fieldName)
+ .setValue(field);
+ }
+}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincLinguisticVariantsHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincLinguisticVariantsHandler.java
new file mode 100644
index 00000000000..20972f1f472
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincLinguisticVariantsHandler.java
@@ -0,0 +1,96 @@
+package ca.uhn.fhir.jpa.term.loinc;
+
+import static org.apache.commons.lang3.StringUtils.isBlank;
+import static org.apache.commons.lang3.StringUtils.trim;
+
+import java.util.List;
+
+import javax.validation.constraints.NotNull;
+
+import org.apache.commons.csv.CSVRecord;
+
+import ca.uhn.fhir.jpa.term.IZipContentsHandlerCsv;
+
+/*-
+ * #%L
+ * HAPI FHIR JPA Server
+ * %%
+ * 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 class LoincLinguisticVariantsHandler implements IZipContentsHandlerCsv {
+
+ private final List myLinguisticVariants;
+
+ public LoincLinguisticVariantsHandler(List thelinguisticVariants) {
+ myLinguisticVariants = thelinguisticVariants;
+ }
+
+ @Override
+ public void accept(CSVRecord theRecord) {
+
+ String id = trim(theRecord.get("ID"));
+ if (isBlank(id)) {
+ return;
+ }
+
+ String isoLanguage = trim(theRecord.get("ISO_LANGUAGE"));
+ if (isBlank(isoLanguage)) {
+ return;
+ }
+
+ String isoCountry = trim(theRecord.get("ISO_COUNTRY"));
+ if (isBlank(isoCountry)) {
+ return;
+ }
+
+ String languageName = trim(theRecord.get("LANGUAGE_NAME"));
+ if (isBlank(languageName)) {
+ return;
+ }
+
+ LinguisticVariant linguisticVariant = new LinguisticVariant(id, isoLanguage, isoCountry, languageName);
+ myLinguisticVariants.add(linguisticVariant);
+ }
+
+ public static class LinguisticVariant {
+
+ private String myId;
+ private String myIsoLanguage;
+ private String myIsoCountry;
+ private String myLanguageName;
+
+ public LinguisticVariant(@NotNull String theId, @NotNull String theIsoLanguage, @NotNull String theIsoCountry, @NotNull String theLanguageName) {
+ this.myId = theId;
+ this.myIsoLanguage = theIsoLanguage;
+ this.myIsoCountry = theIsoCountry;
+ this.myLanguageName = theLanguageName;
+ }
+
+ public String getLinguisticVariantFileName() {
+ return myIsoLanguage + myIsoCountry + myId + "LinguisticVariant.csv";
+ }
+
+ public String getLanguageName() {
+ return myLanguageName;
+ }
+
+ public String getLanguageCode() {
+ return myIsoLanguage + "-" + myIsoCountry;
+ }
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUploadPropertiesEnum.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUploadPropertiesEnum.java
index aa5cf5c32ad..d66d29bbdfb 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUploadPropertiesEnum.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUploadPropertiesEnum.java
@@ -117,6 +117,18 @@ public enum LoincUploadPropertiesEnum {
LOINC_PARENT_GROUP_FILE("loinc.parent.group.file"),
LOINC_PARENT_GROUP_FILE_DEFAULT("AccessoryFiles/GroupFile/ParentGroup.csv"),
+ // Consumer Name
+ LOINC_CONSUMER_NAME_FILE("loinc.consumer.name.file"),
+ LOINC_CONSUMER_NAME_FILE_DEFAULT("AccessoryFiles/ConsumerName/ConsumerName.csv"),
+
+ // Linguistic Variants
+ LOINC_LINGUISTIC_VARIANTS_FILE("loinc.linguistic.variants.file"),
+ LOINC_LINGUISTIC_VARIANTS_FILE_DEFAULT("AccessoryFiles/LinguisticVariants/LinguisticVariants.csv"),
+
+ // Linguistic Variants Folder Path which contains variants for different languages
+ LOINC_LINGUISTIC_VARIANTS_PATH("loinc.linguistic.variants.path"),
+ LOINC_LINGUISTIC_VARIANTS_PATH_DEFAULT("AccessoryFiles/LinguisticVariants/"),
+
/*
* DUPLICATES
*/
diff --git a/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/term/loinc/loincupload.properties b/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/term/loinc/loincupload.properties
index a3f35f7f686..d2f5a5099c2 100644
--- a/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/term/loinc/loincupload.properties
+++ b/hapi-fhir-jpaserver-base/src/main/resources/ca/uhn/fhir/jpa/term/loinc/loincupload.properties
@@ -91,3 +91,13 @@ loinc.group.terms.file=AccessoryFiles/GroupFile/GroupLoincTerms.csv
## Default value if key not provided: AccessoryFiles/GroupFile/ParentGroup.csv
## File may be omitted
loinc.parent.group.file=AccessoryFiles/GroupFile/ParentGroup.csv
+
+# Consumer Names
+## Default value if key not provided: AccessoryFiles/ConsumerName/ConsumerName.csv
+## File may be omitted
+loinc.consumer.name.file=AccessoryFiles/ConsumerName/ConsumerName.csv
+
+# Linguistic Variants
+## Default value if key not provided: AccessoryFiles/LinguisticVariants/LinguisticVariants.csv
+## File may be omitted
+loinc.linguistic.variants.file=AccessoryFiles/LinguisticVariants/LinguisticVariants.csv
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java
index d3ddc2ca6f1..e2ca669a6dd 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java
@@ -188,6 +188,39 @@ public class BulkDataExportProviderTest {
assertThat(options.getFilters(), containsInAnyOrder("Patient?identifier=foo"));
}
+
+ @Test
+ public void testSuccessfulInitiateBulkRequest_Get_MultipleTypeFilters() throws IOException {
+
+ IBulkDataExportSvc.JobInfo jobInfo = new IBulkDataExportSvc.JobInfo()
+ .setJobId(A_JOB_ID);
+ when(myBulkDataExportSvc.submitJob(any(),any(), nullable(RequestDetails.class))).thenReturn(jobInfo);
+
+ String url = "http://localhost:" + myPort + "/" + JpaConstants.OPERATION_EXPORT
+ + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON)
+ + "&" + JpaConstants.PARAM_EXPORT_TYPE + "=" + UrlUtil.escapeUrlParam("Patient,EpisodeOfCare")
+ + "&" + JpaConstants.PARAM_EXPORT_TYPE_FILTER + "=" + UrlUtil.escapeUrlParam("Patient?_id=P999999990")
+ + "&" + JpaConstants.PARAM_EXPORT_TYPE_FILTER + "=" + UrlUtil.escapeUrlParam("EpisodeOfCare?patient=P999999990");
+
+ HttpGet get = new HttpGet(url);
+ get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC);
+ ourLog.info("Request: {}", url);
+ try (CloseableHttpResponse response = myClient.execute(get)) {
+ ourLog.info("Response: {}", response.toString());
+
+ assertEquals(202, response.getStatusLine().getStatusCode());
+ assertEquals("Accepted", response.getStatusLine().getReasonPhrase());
+ assertEquals("http://localhost:" + myPort + "/$export-poll-status?_jobId=" + A_JOB_ID, response.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue());
+ }
+
+ verify(myBulkDataExportSvc, times(1)).submitJob(myBulkDataExportOptionsCaptor.capture(), any(), nullable(RequestDetails.class));
+ BulkDataExportOptions options = myBulkDataExportOptionsCaptor.getValue();
+ assertEquals(Constants.CT_FHIR_NDJSON, options.getOutputFormat());
+ assertThat(options.getResourceTypes(), containsInAnyOrder("Patient", "EpisodeOfCare"));
+ assertThat(options.getSince(), nullValue());
+ assertThat(options.getFilters(), containsInAnyOrder("Patient?_id=P999999990", "EpisodeOfCare?patient=P999999990"));
+ }
+
@Test
public void testPollForStatus_BUILDING() throws IOException {
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java
index 9973c79eb1c..d265473a300 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java
@@ -7,7 +7,6 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.batch.BatchJobsConfig;
import ca.uhn.fhir.jpa.batch.api.IBatchJobSubmitter;
-import ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions;
import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportSvc;
import ca.uhn.fhir.jpa.bulk.export.job.BulkExportJobParametersBuilder;
import ca.uhn.fhir.jpa.bulk.export.job.GroupBulkExportJobParametersBuilder;
@@ -26,6 +25,7 @@ import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
+import ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.test.utilities.BatchJobHelper;
import ca.uhn.fhir.util.HapiExtensions;
@@ -40,6 +40,7 @@ import org.hl7.fhir.r4.model.Binary;
import org.hl7.fhir.r4.model.CareTeam;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Enumerations;
+import org.hl7.fhir.r4.model.EpisodeOfCare;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.Group;
import org.hl7.fhir.r4.model.Immunization;
@@ -518,6 +519,63 @@ public class BulkDataExportSvcImplR4Test extends BaseJpaR4Test {
}
}
+ @Test
+ public void testGenerateBulkExport_WithMultipleTypeFilters() {
+ // Create some resources to load
+ Patient p = new Patient();
+ p.setId("P999999990");
+ p.setActive(true);
+ myPatientDao.update(p);
+
+ EpisodeOfCare eoc = new EpisodeOfCare();
+ eoc.setId("E0");
+ eoc.getPatient().setReference("Patient/P999999990");
+ myEpisodeOfCareDao.update(eoc);
+
+ // Create a bulk job
+ HashSet types = Sets.newHashSet("Patient", "EpisodeOfCare");
+ Set typeFilters = Sets.newHashSet("Patient?_id=P999999990", "EpisodeOfCare?patient=P999999990");
+ BulkDataExportOptions options = new BulkDataExportOptions();
+ options.setExportStyle(BulkDataExportOptions.ExportStyle.SYSTEM);
+ options.setResourceTypes(types);
+ options.setFilters(typeFilters);
+ IBulkDataExportSvc.JobInfo jobDetails = myBulkDataExportSvc.submitJob(options);
+ assertNotNull(jobDetails.getJobId());
+
+ // Check the status
+ IBulkDataExportSvc.JobInfo status = myBulkDataExportSvc.getJobInfoOrThrowResourceNotFound(jobDetails.getJobId());
+ assertEquals(BulkExportJobStatusEnum.SUBMITTED, status.getStatus());
+ assertEquals("/$export?_outputFormat=application%2Ffhir%2Bndjson&_type=EpisodeOfCare,Patient&_typeFilter=Patient%3F_id%3DP999999990&_typeFilter=EpisodeOfCare%3Fpatient%3DP999999990", status.getRequest());
+
+ // Run a scheduled pass to build the export
+ myBulkDataExportSvc.buildExportFiles();
+
+ awaitAllBulkJobCompletions();
+
+ // Fetch the job again
+ status = myBulkDataExportSvc.getJobInfoOrThrowResourceNotFound(jobDetails.getJobId());
+ assertEquals(BulkExportJobStatusEnum.COMPLETE, status.getStatus());
+ assertEquals(2, status.getFiles().size());
+
+ // Iterate over the files
+ for (IBulkDataExportSvc.FileEntry next : status.getFiles()) {
+ Binary nextBinary = myBinaryDao.read(next.getResourceId());
+ assertEquals(Constants.CT_FHIR_NDJSON, nextBinary.getContentType());
+ String nextContents = new String(nextBinary.getContent(), Constants.CHARSET_UTF8);
+ ourLog.info("Next contents for type {}:\n{}", next.getResourceType(), nextContents);
+
+ if ("Patient".equals(next.getResourceType())) {
+ assertThat(nextContents, containsString("\"id\":\"P999999990\""));
+ assertEquals(1, nextContents.split("\n").length);
+ } else if ("EpisodeOfCare".equals(next.getResourceType())) {
+ assertThat(nextContents, containsString("\"id\":\"E0\""));
+ assertEquals(1, nextContents.split("\n").length);
+ } else {
+ fail(next.getResourceType());
+ }
+ }
+ }
+
@Test
public void testGenerateBulkExport_WithSince() {
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java
index 56fb5d7aa96..95f2a255270 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java
@@ -13,6 +13,7 @@ import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportSvc;
import ca.uhn.fhir.jpa.config.BaseConfig;
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
+import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboTokensNonUniqueDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamDateDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamTokenDao;
import ca.uhn.fhir.jpa.dao.data.IResourceLinkDao;
@@ -161,6 +162,8 @@ public abstract class BaseJpaTest extends BaseTest {
@Autowired
protected IResourceIndexedSearchParamDateDao myResourceIndexedSearchParamDateDao;
@Autowired
+ protected IResourceIndexedComboTokensNonUniqueDao myResourceIndexedComboTokensNonUniqueDao;
+ @Autowired
private IdHelperService myIdHelperService;
@Autowired
private MemoryCacheService myMemoryCacheService;
@@ -306,6 +309,12 @@ public abstract class BaseJpaTest extends BaseTest {
});
}
+ protected void logAllNonUniqueIndexes() {
+ runInTransaction(() -> {
+ ourLog.info("Non unique indexes:\n * {}", myResourceIndexedComboTokensNonUniqueDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
+ });
+ }
+
protected void logAllTokenIndexes() {
runInTransaction(() -> {
ourLog.info("Token indexes:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
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 0953336ff66..0f7ac511ac6 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
@@ -17,7 +17,7 @@ import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportSvc;
import ca.uhn.fhir.jpa.config.TestDstu3Config;
import ca.uhn.fhir.jpa.dao.BaseJpaTest;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
-import ca.uhn.fhir.jpa.dao.data.IResourceIndexedCompositeStringUniqueDao;
+import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboStringUniqueDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamStringDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamTokenDao;
import ca.uhn.fhir.jpa.dao.data.IResourceReindexJobDao;
@@ -150,7 +150,7 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
@Qualifier("myCoverageDaoDstu3")
protected IFhirResourceDao myCoverageDao;
@Autowired
- protected IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
+ protected IResourceIndexedComboStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
@Autowired
@Qualifier("myAllergyIntoleranceDaoDstu3")
protected IFhirResourceDao myAllergyIntoleranceDao;
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolverTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolverTest.java
new file mode 100644
index 00000000000..edc9bfe9c7a
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolverTest.java
@@ -0,0 +1,35 @@
+package ca.uhn.fhir.jpa.dao.index;
+
+import ca.uhn.fhir.mdm.util.CanonicalIdentifier;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class DaoResourceLinkResolverTest {
+
+ @Test
+ public void testLinkResolution() {
+ DaoResourceLinkResolver resolver = new DaoResourceLinkResolver();
+ CanonicalIdentifier canonicalIdentifier = resolver.extractIdentifierFromUrl("Patient?_patient?" +
+ "identifier=http://hapifhir.io/fhir/namingsystem/my_id|123456");
+ assertEquals("http://hapifhir.io/fhir/namingsystem/my_id", canonicalIdentifier.getSystemElement().getValueAsString());
+ assertEquals("123456", canonicalIdentifier.getValueElement().getValueAsString());
+
+ canonicalIdentifier = resolver.extractIdentifierFromUrl("Patient?_patient?" +
+ "identifier=http://hapifhir.io/fhir/namingsystem/my_id|123456&identifier=https://www.id.org/identifiers/member|1101331");
+ assertEquals("http://hapifhir.io/fhir/namingsystem/my_id", canonicalIdentifier.getSystemElement().getValueAsString());
+ assertEquals("123456", canonicalIdentifier.getValueElement().getValueAsString());
+
+ canonicalIdentifier = resolver.extractIdentifierFromUrl("Patient?_tag:not=http://hapifhir.io/fhir/namingsystem/mdm-record-status|GOLDEn_rEcorD" +
+ "&identifier=https://www.my.org/identifiers/memBER|123456");
+ assertEquals("https://www.my.org/identifiers/memBER", canonicalIdentifier.getSystemElement().getValueAsString());
+ assertEquals("123456", canonicalIdentifier.getValueElement().getValueAsString());
+
+ canonicalIdentifier = resolver.extractIdentifierFromUrl("Patient?_tag:not=http://hapifhir.io/fhir/namingsystem/mdm-record-status|GOLDEn_rEcorD");
+ assertNull(canonicalIdentifier);
+
+ }
+
+}
+
+
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java
new file mode 100644
index 00000000000..20e100065f1
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java
@@ -0,0 +1,35 @@
+package ca.uhn.fhir.jpa.dao.index;
+
+import ca.uhn.fhir.interceptor.model.RequestPartitionId;
+import ca.uhn.fhir.jpa.model.config.PartitionSettings;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class IdHelperServiceTest {
+
+ @Test
+ public void testReplaceDefault_AllPartitions() {
+
+ IdHelperService svc = new IdHelperService();
+ PartitionSettings partitionSettings = new PartitionSettings();
+ partitionSettings.setDefaultPartitionId(1);
+ svc.setPartitionSettingsForUnitTest(partitionSettings);
+
+ RequestPartitionId outcome = svc.replaceDefault(RequestPartitionId.allPartitions());
+ assertSame(RequestPartitionId.allPartitions(), outcome);
+ }
+
+ @Test
+ public void testReplaceDefault_DefaultPartition() {
+
+ IdHelperService svc = new IdHelperService();
+ PartitionSettings partitionSettings = new PartitionSettings();
+ partitionSettings.setDefaultPartitionId(1);
+ svc.setPartitionSettingsForUnitTest(partitionSettings);
+
+ RequestPartitionId outcome = svc.replaceDefault(RequestPartitionId.defaultPartition());
+ assertEquals(1, outcome.getPartitionIds().get(0));
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseComboParamsR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseComboParamsR4Test.java
new file mode 100644
index 00000000000..4c0b9ae9cd4
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseComboParamsR4Test.java
@@ -0,0 +1,80 @@
+package ca.uhn.fhir.jpa.dao.r4;
+
+import ca.uhn.fhir.interceptor.api.HookParams;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.interceptor.api.Pointcut;
+import ca.uhn.fhir.jpa.api.config.DaoConfig;
+import ca.uhn.fhir.jpa.model.entity.ModelConfig;
+import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
+import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
+import ca.uhn.fhir.jpa.search.reindex.ResourceReindexingSvcImpl;
+import ca.uhn.fhir.jpa.util.SpringObjectCaster;
+import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.mockito.ArgumentMatchers;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public abstract class BaseComboParamsR4Test extends BaseJpaR4Test {
+
+ private static final Logger ourLog = LoggerFactory.getLogger(BaseComboParamsR4Test.class);
+ @Autowired
+ protected ISearchParamRegistry mySearchParamRegistry;
+ protected List myMessages = new ArrayList<>();
+ private IInterceptorBroadcaster myInterceptorBroadcaster;
+
+ @BeforeEach
+ public void before() {
+ myModelConfig.setDefaultSearchParamsCanBeOverridden(true);
+ myDaoConfig.setSchedulingDisabled(true);
+ myDaoConfig.setUniqueIndexesEnabled(true);
+
+ myInterceptorBroadcaster = mock(IInterceptorBroadcaster.class);
+ when(mySrd.getInterceptorBroadcaster()).thenReturn(myInterceptorBroadcaster);
+ when(mySrd.getServer().getPagingProvider()).thenReturn(new DatabaseBackedPagingProvider());
+
+ when(myInterceptorBroadcaster.hasHooks(eq(Pointcut.JPA_PERFTRACE_WARNING))).thenReturn(true);
+ when(myInterceptorBroadcaster.hasHooks(eq(Pointcut.JPA_PERFTRACE_INFO))).thenReturn(true);
+ when(myInterceptorBroadcaster.callHooks(eq(Pointcut.JPA_PERFTRACE_INFO), ArgumentMatchers.any(HookParams.class))).thenAnswer(t -> {
+ HookParams params = t.getArgument(1, HookParams.class);
+ myMessages.add("INFO " + params.get(StorageProcessingMessage.class).getMessage());
+ return null;
+ });
+ when(myInterceptorBroadcaster.callHooks(eq(Pointcut.JPA_PERFTRACE_WARNING), ArgumentMatchers.any(HookParams.class))).thenAnswer(t -> {
+ HookParams params = t.getArgument(1, HookParams.class);
+ myMessages.add("WARN " + params.get(StorageProcessingMessage.class).getMessage());
+ return null;
+ });
+ when(myInterceptorBroadcaster.callHooks(eq(Pointcut.JPA_PERFTRACE_SEARCH_REUSING_CACHED), ArgumentMatchers.any(HookParams.class))).thenAnswer(t -> {
+ HookParams params = t.getArgument(1, HookParams.class);
+ myMessages.add("REUSING CACHED SEARCH");
+ return null;
+ });
+ }
+
+ @AfterEach
+ public void after() throws Exception {
+ myModelConfig.setDefaultSearchParamsCanBeOverridden(new ModelConfig().isDefaultSearchParamsCanBeOverridden());
+ myDaoConfig.setUniqueIndexesCheckedBeforeSave(new DaoConfig().isUniqueIndexesCheckedBeforeSave());
+ myDaoConfig.setSchedulingDisabled(new DaoConfig().isSchedulingDisabled());
+ myDaoConfig.setUniqueIndexesEnabled(new DaoConfig().isUniqueIndexesEnabled());
+ myDaoConfig.setReindexThreadCount(new DaoConfig().getReindexThreadCount());
+
+ ResourceReindexingSvcImpl svc = SpringObjectCaster.getTargetObject(myResourceReindexingSvc, ResourceReindexingSvcImpl.class);
+ svc.initExecutor();
+ }
+
+ protected void logCapturedMessages() {
+ ourLog.info("Messages:\n {}", String.join("\n ", myMessages));
+ }
+
+}
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 ac5cb325f7d..cf5550fa954 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
@@ -26,7 +26,8 @@ import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
import ca.uhn.fhir.jpa.dao.data.IPartitionDao;
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTagDao;
-import ca.uhn.fhir.jpa.dao.data.IResourceIndexedCompositeStringUniqueDao;
+import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboStringUniqueDao;
+import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboTokensNonUniqueDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamCoordsDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamDateDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamQuantityDao;
@@ -228,7 +229,9 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
@Autowired
protected IResourceIndexedSearchParamDateDao myResourceIndexedSearchParamDateDao;
@Autowired
- protected IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
+ protected IResourceIndexedComboStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
+ @Autowired
+ protected IResourceIndexedComboTokensNonUniqueDao myResourceIndexedComboTokensNonUniqueDao;
@Autowired
@Qualifier("myAllergyIntoleranceDaoR4")
protected IFhirResourceDao myAllergyIntoleranceDao;
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboNonUniqueParamTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboNonUniqueParamTest.java
new file mode 100644
index 00000000000..f79b3335f98
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboNonUniqueParamTest.java
@@ -0,0 +1,194 @@
+package ca.uhn.fhir.jpa.dao.r4;
+
+import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboTokenNonUnique;
+import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
+import ca.uhn.fhir.rest.api.server.IBundleProvider;
+import ca.uhn.fhir.rest.param.DateParam;
+import ca.uhn.fhir.rest.param.StringParam;
+import ca.uhn.fhir.rest.param.TokenParam;
+import ca.uhn.fhir.util.HapiExtensions;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.hl7.fhir.r4.model.BooleanType;
+import org.hl7.fhir.r4.model.DateType;
+import org.hl7.fhir.r4.model.Enumerations;
+import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.SearchParameter;
+import org.junit.jupiter.api.Test;
+
+import java.util.Comparator;
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class FhirResourceDaoR4ComboNonUniqueParamTest extends BaseComboParamsR4Test {
+
+ private void createNamesAndGenderSp() {
+ SearchParameter sp = new SearchParameter();
+ sp.setId("SearchParameter/patient-family");
+ sp.setType(Enumerations.SearchParamType.STRING);
+ sp.setCode("family");
+ sp.setExpression("Patient.name.family + '|'");
+ sp.setStatus(PublicationStatus.ACTIVE);
+ sp.addBase("Patient");
+ mySearchParameterDao.update(sp);
+
+ sp = new SearchParameter();
+ sp.setId("SearchParameter/patient-given");
+ sp.setType(Enumerations.SearchParamType.STRING);
+ sp.setCode("given");
+ sp.setExpression("Patient.name.given");
+ sp.setStatus(PublicationStatus.ACTIVE);
+ sp.addBase("Patient");
+ mySearchParameterDao.update(sp);
+
+ sp = new SearchParameter();
+ sp.setId("SearchParameter/patient-gender");
+ sp.setType(Enumerations.SearchParamType.TOKEN);
+ sp.setCode("gender");
+ sp.setExpression("Patient.gender");
+ sp.setStatus(PublicationStatus.ACTIVE);
+ sp.addBase("Patient");
+ mySearchParameterDao.update(sp);
+
+ sp = new SearchParameter();
+ sp.setId("SearchParameter/patient-names-and-gender");
+ sp.setType(Enumerations.SearchParamType.COMPOSITE);
+ sp.setStatus(PublicationStatus.ACTIVE);
+ sp.addBase("Patient");
+ sp.addComponent()
+ .setExpression("Patient")
+ .setDefinition("SearchParameter/patient-family");
+ sp.addComponent()
+ .setExpression("Patient")
+ .setDefinition("SearchParameter/patient-given");
+ sp.addComponent()
+ .setExpression("Patient")
+ .setDefinition("SearchParameter/patient-gender");
+ sp.addExtension()
+ .setUrl(HapiExtensions.EXT_SP_UNIQUE)
+ .setValue(new BooleanType(false));
+ mySearchParameterDao.update(sp);
+
+ mySearchParamRegistry.forceRefresh();
+
+ myMessages.clear();
+ }
+
+ @Test
+ public void testCreateAndUse() {
+ createNamesAndGenderSp();
+
+ IIdType id1 = createPatient1();
+ assertNotNull(id1);
+
+ IIdType id2 = createPatient2();
+ assertNotNull(id2);
+
+ logAllNonUniqueIndexes();
+ runInTransaction(() -> {
+ List indexedTokens = myResourceIndexedComboTokensNonUniqueDao.findAll();
+ indexedTokens.sort(Comparator.comparing(t -> t.getId()));
+ assertEquals(2, indexedTokens.size());
+ assertEquals(-7504889232313729794L, indexedTokens.get(0).getHashComplete().longValue());
+ });
+
+ myMessages.clear();
+ SearchParameterMap params = SearchParameterMap.newSynchronous();
+ params.add("family", new StringParam("fAmIlY1|")); // weird casing to test normalization
+ params.add("given", new StringParam("gIVEn1"));
+ params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
+ myCaptureQueriesListener.clear();
+ IBundleProvider results = myPatientDao.search(params, mySrd);
+ List actual = toUnqualifiedVersionlessIdValues(results);
+ myCaptureQueriesListener.logSelectQueries();
+ assertThat(actual, containsInAnyOrder(id1.toUnqualifiedVersionless().getValue()));
+
+ String sql = myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false);
+ assertEquals("SELECT t0.RES_ID FROM HFJ_IDX_CMB_TOK_NU t0 WHERE (t0.IDX_STRING = 'Patient?family=FAMILY1%5C%7C&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale&given=GIVEN1')", sql);
+
+ logCapturedMessages();
+ assertThat(myMessages.toString(), containsString("[INFO Using NON_UNIQUE index for query for search: Patient?family=FAMILY1%5C%7C&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale&given=GIVEN1]"));
+ myMessages.clear();
+
+ // Remove 1, add another
+
+ myPatientDao.delete(id1);
+
+ IIdType id3 = createPatient1();
+ assertNotNull(id3);
+
+ params = SearchParameterMap.newSynchronous();
+ params.add("family", new StringParam("fAmIlY1|")); // weird casing to test normalization
+ params.add("given", new StringParam("gIVEn1"));
+ params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
+ results = myPatientDao.search(params, mySrd);
+ actual = toUnqualifiedVersionlessIdValues(results);
+ myCaptureQueriesListener.logSelectQueries();
+ assertThat(actual, containsInAnyOrder(id3.toUnqualifiedVersionless().getValue()));
+
+ }
+
+ @Test
+ public void testSearchWithExtraParameters() {
+ createNamesAndGenderSp();
+
+ IIdType id1 = createPatient1();
+ assertNotNull(id1);
+
+ IIdType id2 = createPatient2();
+ assertNotNull(id2);
+
+ logAllNonUniqueIndexes();
+ runInTransaction(() -> {
+ List indexedTokens = myResourceIndexedComboTokensNonUniqueDao.findAll();
+ indexedTokens.sort(Comparator.comparing(t -> t.getId()));
+ assertEquals(2, indexedTokens.size());
+ assertEquals(-7504889232313729794L, indexedTokens.get(0).getHashComplete().longValue());
+ });
+
+ myMessages.clear();
+ SearchParameterMap params = SearchParameterMap.newSynchronous();
+ params.add("family", new StringParam("fAmIlY1|")); // weird casing to test normalization
+ params.add("given", new StringParam("gIVEn1"));
+ params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
+ params.add("birthdate", new DateParam("2021-02-02"));
+ myCaptureQueriesListener.clear();
+ IBundleProvider results = myPatientDao.search(params, mySrd);
+ List actual = toUnqualifiedVersionlessIdValues(results);
+ myCaptureQueriesListener.logSelectQueries();
+ assertThat(actual, containsInAnyOrder(id1.toUnqualifiedVersionless().getValue()));
+
+ String sql = myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false);
+ assertEquals("SELECT t1.RES_ID FROM HFJ_RESOURCE t1 LEFT OUTER JOIN HFJ_IDX_CMB_TOK_NU t0 ON (t1.RES_ID = t0.RES_ID) LEFT OUTER JOIN HFJ_SPIDX_DATE t2 ON (t1.RES_ID = t2.RES_ID) WHERE ((t0.IDX_STRING = 'Patient?family=FAMILY1%5C%7C&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale&given=GIVEN1') AND ((t2.HASH_IDENTITY = '5247847184787287691') AND ((t2.SP_VALUE_LOW_DATE_ORDINAL >= '20210202') AND (t2.SP_VALUE_HIGH_DATE_ORDINAL <= '20210202'))))", sql);
+
+ logCapturedMessages();
+ assertThat(myMessages.toString(), containsString("[INFO Using NON_UNIQUE index for query for search: Patient?family=FAMILY1%5C%7C&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale&given=GIVEN1]"));
+ myMessages.clear();
+
+ }
+
+
+ private IIdType createPatient2() {
+ Patient pt2 = new Patient();
+ pt2.getNameFirstRep().setFamily("Family2").addGiven("Given2");
+ pt2.setGender(Enumerations.AdministrativeGender.MALE);
+ pt2.setBirthDateElement(new DateType("2021-02-02"));
+ IIdType id2 = myPatientDao.create(pt2).getId().toUnqualified();
+ return id2;
+ }
+
+ private IIdType createPatient1() {
+ Patient pt1 = new Patient();
+ pt1.getNameFirstRep().setFamily("Family1").addGiven("Given1");
+ pt1.setGender(Enumerations.AdministrativeGender.MALE);
+ pt1.setBirthDateElement(new DateType("2021-02-02"));
+ return myPatientDao.create(pt1).getId().toUnqualified();
+ }
+
+
+}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UniqueSearchParamTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamTest.java
similarity index 91%
rename from hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UniqueSearchParamTest.java
rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamTest.java
index 7f9fc10925a..656781fb8b2 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UniqueSearchParamTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamTest.java
@@ -1,19 +1,12 @@
package ca.uhn.fhir.jpa.dao.r4;
+import ca.uhn.fhir.context.ComboSearchParamType;
import ca.uhn.fhir.context.RuntimeSearchParam;
-import ca.uhn.fhir.interceptor.api.HookParams;
-import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
-import ca.uhn.fhir.interceptor.api.Pointcut;
-import ca.uhn.fhir.jpa.api.config.DaoConfig;
-import ca.uhn.fhir.jpa.model.entity.ModelConfig;
-import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
+import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
-import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
-import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.reindex.ResourceReindexingSvcImpl;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.util.JpaParamUtil;
-import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
import ca.uhn.fhir.jpa.util.SpringObjectCaster;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.DateParam;
@@ -27,17 +20,12 @@ import ca.uhn.fhir.util.HapiExtensions;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentMatchers;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Nonnull;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
@@ -57,56 +45,10 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
+public class FhirResourceDaoR4ComboUniqueParamTest extends BaseComboParamsR4Test {
- private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4UniqueSearchParamTest.class);
- @Autowired
- private ISearchParamRegistry mySearchParamRegistry;
- private IInterceptorBroadcaster myInterceptorBroadcaster;
- private List myMessages = new ArrayList<>();
-
- @AfterEach
- public void after() throws Exception {
- myModelConfig.setDefaultSearchParamsCanBeOverridden(new ModelConfig().isDefaultSearchParamsCanBeOverridden());
- myDaoConfig.setUniqueIndexesCheckedBeforeSave(new DaoConfig().isUniqueIndexesCheckedBeforeSave());
- myDaoConfig.setSchedulingDisabled(new DaoConfig().isSchedulingDisabled());
- myDaoConfig.setUniqueIndexesEnabled(new DaoConfig().isUniqueIndexesEnabled());
- myDaoConfig.setReindexThreadCount(new DaoConfig().getReindexThreadCount());
-
- ResourceReindexingSvcImpl svc = SpringObjectCaster.getTargetObject(myResourceReindexingSvc, ResourceReindexingSvcImpl.class);
- svc.initExecutor();
- }
-
- @BeforeEach
- public void before() {
- myModelConfig.setDefaultSearchParamsCanBeOverridden(true);
- myDaoConfig.setSchedulingDisabled(true);
- myDaoConfig.setUniqueIndexesEnabled(true);
-
- myInterceptorBroadcaster = mock(IInterceptorBroadcaster.class);
- when(mySrd.getInterceptorBroadcaster()).thenReturn(myInterceptorBroadcaster);
- when(mySrd.getServer().getPagingProvider()).thenReturn(new DatabaseBackedPagingProvider());
-
- when(myInterceptorBroadcaster.hasHooks(eq(Pointcut.JPA_PERFTRACE_WARNING))).thenReturn(true);
- when(myInterceptorBroadcaster.hasHooks(eq(Pointcut.JPA_PERFTRACE_INFO))).thenReturn(true);
- when(myInterceptorBroadcaster.callHooks(eq(Pointcut.JPA_PERFTRACE_INFO), ArgumentMatchers.any(HookParams.class))).thenAnswer(t -> {
- HookParams params = t.getArgument(1, HookParams.class);
- myMessages.add("INFO " + params.get(StorageProcessingMessage.class).getMessage());
- return null;
- });
- when(myInterceptorBroadcaster.callHooks(eq(Pointcut.JPA_PERFTRACE_WARNING), ArgumentMatchers.any(HookParams.class))).thenAnswer(t -> {
- HookParams params = t.getArgument(1, HookParams.class);
- myMessages.add("WARN " + params.get(StorageProcessingMessage.class).getMessage());
- return null;
- });
- when(myInterceptorBroadcaster.callHooks(eq(Pointcut.JPA_PERFTRACE_SEARCH_REUSING_CACHED), ArgumentMatchers.any(HookParams.class))).thenAnswer(t -> {
- HookParams params = t.getArgument(1, HookParams.class);
- myMessages.add("REUSING CACHED SEARCH");
- return null;
- });
- }
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4ComboUniqueParamTest.class);
private void createUniqueBirthdateAndGenderSps() {
@@ -647,7 +589,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
// Make sure entries are saved
runInTransaction(() -> {
- List all = myResourceIndexedCompositeStringUniqueDao.findAll();
+ List all = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(2, all.size());
});
@@ -691,7 +633,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus status) {
- List all = myResourceIndexedCompositeStringUniqueDao.findAll();
+ List all = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(2, all.size());
}
});
@@ -706,7 +648,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
ResourceReindexingSvcImpl svc = SpringObjectCaster.getTargetObject(myResourceReindexingSvc, ResourceReindexingSvcImpl.class);
svc.initExecutor();
- List uniqueSearchParams = mySearchParamRegistry.getActiveUniqueSearchParams("Observation");
+ List uniqueSearchParams = mySearchParamRegistry.getActiveComboSearchParams("Observation");
assertEquals(0, uniqueSearchParams.size());
Patient pt1 = new Patient();
@@ -735,7 +677,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
createUniqueObservationSubjectDateCode();
- uniqueSearchParams = mySearchParamRegistry.getActiveUniqueSearchParams("Observation");
+ uniqueSearchParams = mySearchParamRegistry.getActiveComboSearchParams("Observation");
assertEquals(1, uniqueSearchParams.size());
assertEquals(3, uniqueSearchParams.get(0).getComponents().size());
@@ -745,7 +687,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
assertEquals(0, myResourceReindexingSvc.forceReindexingPass());
runInTransaction(() -> {
- List uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
+ List uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(1, uniques.size(), uniques.toString());
assertThat(uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue(), either(equalTo("Observation/" + id2.getIdPart())).or(equalTo("Observation/" + id3.getIdPart())));
assertEquals("Observation?code=foo%7Cbar&date=2011-01-01&subject=Patient%2F" + id1.getIdPart(), uniques.get(0).getIndexString());
@@ -753,7 +695,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
myResourceIndexedCompositeStringUniqueDao.deleteAll();
});
- assertEquals(1, mySearchParamRegistry.getActiveUniqueSearchParams("Observation").size());
+ assertEquals(1, mySearchParamRegistry.getActiveComboSearchParams("Observation").size());
myResourceReindexingSvc.markAllResourcesForReindexing("Observation");
assertEquals(1, myResourceReindexingSvc.forceReindexingPass());
@@ -762,7 +704,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
assertEquals(0, myResourceReindexingSvc.forceReindexingPass());
runInTransaction(() -> {
- List uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
+ List uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(1, uniques.size(), uniques.toString());
assertThat(uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue(), either(equalTo("Observation/" + id2.getIdPart())).or(equalTo("Observation/" + id3.getIdPart())));
assertEquals("Observation?code=foo%7Cbar&date=2011-01-01&subject=Patient%2F" + id1.getIdPart(), uniques.get(0).getIndexString());
@@ -833,7 +775,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus status) {
- List all = myResourceIndexedCompositeStringUniqueDao.findAll();
+ List all = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(2, all.size());
}
});
@@ -873,7 +815,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
});
runInTransaction(() -> {
- List uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
+ List uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
ourLog.info("** Uniques: {}", uniques);
assertEquals(1, uniques.size(), uniques.toString());
assertEquals("Coverage/" + id3.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
@@ -1042,7 +984,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus status) {
- List all = myResourceIndexedCompositeStringUniqueDao.findAll();
+ List all = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(1, all.size(), all.toString());
}
});
@@ -1074,7 +1016,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
assertThat(toUnqualifiedVersionlessIdValues(results), containsInAnyOrder(id1.getValue()));
logCapturedMessages();
- assertThat(myMessages.toString(), containsString("Using unique index for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"));
+ assertThat(myMessages.toString(), containsString("Using UNIQUE index for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"));
myMessages.clear();
}
@@ -1102,7 +1044,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
String searchId = results.getUuid();
assertThat(toUnqualifiedVersionlessIdValues(results), containsInAnyOrder(id1));
logCapturedMessages();
- assertThat(myMessages.toString(), containsString("Using unique index for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"));
+ assertThat(myMessages.toString(), containsString("Using UNIQUE index for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"));
myMessages.clear();
// Other order
@@ -1126,7 +1068,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
results = myPatientDao.search(params, mySrd);
assertThat(toUnqualifiedVersionlessIdValues(results), empty());
logCapturedMessages();
- assertThat(myMessages.toString(), containsString("Using unique index for query for search: Patient?birthdate=2011-01-03&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"));
+ assertThat(myMessages.toString(), containsString("Using UNIQUE index for query for search: Patient?birthdate=2011-01-03&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"));
myMessages.clear();
myMessages.clear();
@@ -1151,7 +1093,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
pt1.setBirthDateElement(new DateType("2011-01-01"));
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
- List uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
+ List uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(1, uniques.size(), uniques.toString());
assertEquals("Patient/" + id1.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale", uniques.get(0).getIndexString());
@@ -1161,7 +1103,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
public void testUniqueValuesAreIndexed_RefAndDateAndToken() {
createUniqueObservationSubjectDateCode();
- List uniques;
+ List uniques;
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(0, uniques.size(), uniques.toString());
@@ -1190,7 +1132,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
@Test
public void testUniqueValuesAreIndexed_Reference_UsingModifierSyntax() {
createUniqueNameAndManagingOrganizationSps();
- List uniques;
+ List uniques;
Organization org = new Organization();
org.setId("Organization/ORG");
@@ -1204,7 +1146,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
IIdType id1 = myPatientDao.update(pt1, "Patient?name=FAMILY1&organization:Organization=ORG", mySrd).getId().toUnqualifiedVersionless();
logCapturedMessages();
- assertThat(myMessages.toString(), containsString("Using unique index for query for search: Patient?name=FAMILY1&organization=Organization%2FORG"));
+ assertThat(myMessages.toString(), containsString("Using UNIQUE index for query for search: Patient?name=FAMILY1&organization=Organization%2FORG"));
myMessages.clear();
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
@@ -1221,7 +1163,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
id1 = myPatientDao.update(pt1, "Patient?name=FAMILY1&organization:Organization=ORG", mySrd).getId().toUnqualifiedVersionless();
logCapturedMessages();
- assertThat(myMessages.toString(), containsString("Using unique index for query for search: Patient?name=FAMILY1&organization=Organization%2FORG"));
+ assertThat(myMessages.toString(), containsString("Using UNIQUE index for query for search: Patient?name=FAMILY1&organization=Organization%2FORG"));
myMessages.clear();
uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
@@ -1231,10 +1173,6 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
}
- private void logCapturedMessages() {
- ourLog.info("Messages:\n {}", String.join("\n ", myMessages));
- }
-
@Test
public void testUniqueValuesAreIndexed_StringAndReference() {
createUniqueNameAndManagingOrganizationSps();
@@ -1253,7 +1191,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
pt1.setManagingOrganization(new Reference("Organization/ORG"));
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
- List uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
+ List uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
Collections.sort(uniques);
assertEquals(3, uniques.size());
@@ -1270,7 +1208,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
@Test
public void testUniqueValuesAreIndexed_StringAndReference_UsingConditional() {
createUniqueNameAndManagingOrganizationSps();
- List uniques;
+ List uniques;
Organization org = new Organization();
org.setId("Organization/ORG");
@@ -1303,7 +1241,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
@Test
public void testUniqueValuesAreIndexed_StringAndReference_UsingConditionalInTransaction() {
createUniqueNameAndManagingOrganizationSps();
- List uniques;
+ List uniques;
Organization org = new Organization();
org.setId("Organization/ORG");
@@ -1385,7 +1323,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
createUniqueBirthdateAndGenderSps();
Patient pt;
- List uniques;
+ List uniques;
pt = new Patient();
pt.setGender(Enumerations.AdministrativeGender.MALE);
@@ -1416,7 +1354,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
org.setName("ORG");
myOrganizationDao.update(org);
- List uniques;
+ List uniques;
Patient pt;
pt = new Patient();
@@ -1464,7 +1402,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
myResourceReindexingSvc.forceReindexingPass();
myResourceReindexingSvc.forceReindexingPass();
- List uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
+ List uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(1, uniques.size(), uniques.toString());
assertEquals("Observation/" + id2.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
assertEquals("Observation?code=foo%7Cbar&date=2011-01-01&subject=Patient%2F" + id1.getIdPart(), uniques.get(0).getIndexString());
@@ -1486,10 +1424,10 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
@Test
public void testDetectUniqueSearchParams() {
createUniqueBirthdateAndGenderSps();
- List params = mySearchParamRegistry.getActiveUniqueSearchParams("Patient");
+ List params = mySearchParamRegistry.getActiveComboSearchParams("Patient");
assertEquals(1, params.size());
- assertEquals(params.get(0).isUnique(), true);
+ assertEquals(ComboSearchParamType.UNIQUE, params.get(0).getComboSearchParamType());
assertEquals(2, params.get(0).getComponents().size());
// Should be alphabetical order
@@ -1565,7 +1503,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
assertThat(toUnqualifiedVersionlessIdValues(results), containsInAnyOrder(id2.getValue()));
logCapturedMessages();
- assertThat(myMessages.toString(), containsString("Using unique index for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"));
+ assertThat(myMessages.toString(), containsString("Using UNIQUE index for query for search: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale"));
myMessages.clear();
}
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 378af850dbf..44a615682f0 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
@@ -12,7 +12,7 @@ import ca.uhn.fhir.jpa.model.entity.ForcedId;
import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTag;
-import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
+import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
@@ -435,7 +435,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
assertEquals(myPartitionDate, presents.get(0).getPartitionId().getPartitionDate());
// HFJ_IDX_CMP_STRING_UNIQ
- List uniques = myResourceIndexedCompositeStringUniqueDao.findAllForResourceIdForUnitTest(patientId);
+ List uniques = myResourceIndexedCompositeStringUniqueDao.findAllForResourceIdForUnitTest(patientId);
assertEquals(1, uniques.size());
assertEquals(myPartitionId, uniques.get(0).getPartitionId().getPartitionId().intValue());
assertEquals(myPartitionDate, uniques.get(0).getPartitionId().getPartitionDate());
@@ -519,7 +519,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test {
assertEquals(myPartitionDate, presents.get(0).getPartitionId().getPartitionDate());
// HFJ_IDX_CMP_STRING_UNIQ
- List uniques = myResourceIndexedCompositeStringUniqueDao.findAllForResourceIdForUnitTest(patientId);
+ List uniques = myResourceIndexedCompositeStringUniqueDao.findAllForResourceIdForUnitTest(patientId);
assertEquals(1, uniques.size());
assertEquals(null, uniques.get(0).getPartitionId().getPartitionId());
assertEquals(myPartitionDate, uniques.get(0).getPartitionId().getPartitionDate());
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 c21285ab398..8dfe6e4a1c7 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
@@ -327,7 +327,7 @@ public class SearchParamExtractorR4Test {
public void testExtensionContainingReference() {
String path = "Patient.extension('http://patext').value.as(Reference)";
- RuntimeSearchParam sp = new RuntimeSearchParam(null, null, "extpat", "Patient SP", path, RestSearchParameterTypeEnum.REFERENCE, new HashSet<>(), Sets.newHashSet("Patient"), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, false, null, null);
+ RuntimeSearchParam sp = new RuntimeSearchParam(null, null, "extpat", "Patient SP", path, RestSearchParameterTypeEnum.REFERENCE, new HashSet<>(), Sets.newHashSet("Patient"), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, null, null, null);
mySearchParamRegistry.addSearchParam(sp);
Patient patient = new Patient();
@@ -440,7 +440,7 @@ public class SearchParamExtractorR4Test {
}
@Override
- public List getActiveUniqueSearchParams(String theResourceName, Set theParamNames) {
+ public List getActiveComboSearchParams(String theResourceName, Set theParamNames) {
throw new UnsupportedOperationException();
}
@@ -451,7 +451,7 @@ public class SearchParamExtractorR4Test {
}
@Override
- public List getActiveUniqueSearchParams(String theResourceName) {
+ public List getActiveComboSearchParams(String theResourceName) {
throw new UnsupportedOperationException();
}
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 e8dd7625310..fa188add3af 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
@@ -22,7 +22,7 @@ import ca.uhn.fhir.jpa.dao.BaseJpaTest;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
-import ca.uhn.fhir.jpa.dao.data.IResourceIndexedCompositeStringUniqueDao;
+import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboStringUniqueDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamDateDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamQuantityDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamStringDao;
@@ -185,7 +185,7 @@ public abstract class BaseJpaR5Test extends BaseJpaTest implements ITestDataBuil
@Autowired
protected IResourceIndexedSearchParamDateDao myResourceIndexedSearchParamDateDao;
@Autowired
- protected IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
+ protected IResourceIndexedComboStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
@Autowired
@Qualifier("myAllergyIntoleranceDaoR5")
protected IFhirResourceDao myAllergyIntoleranceDao;
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java
index 95b5bb9533d..99047a7d948 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java
@@ -5,11 +5,13 @@ import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4SystemTest;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
+import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.TokenOrListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
+import org.hl7.fhir.r4.model.Encounter;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Observation;
@@ -18,6 +20,7 @@ import org.hl7.fhir.r4.model.Patient;
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 static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
@@ -31,9 +34,12 @@ public class PatientIdPartitionInterceptorTest extends BaseJpaR4SystemTest {
private PatientIdPartitionInterceptor mySvc;
private ForceOffsetSearchModeInterceptor myForceOffsetSearchModeInterceptor;
+ @Autowired
+ private ISearchParamExtractor mySearchParamExtractor;
+
@BeforeEach
public void before() {
- mySvc = new PatientIdPartitionInterceptor(myFhirCtx);
+ mySvc = new PatientIdPartitionInterceptor(myFhirCtx, mySearchParamExtractor);
myForceOffsetSearchModeInterceptor = new ForceOffsetSearchModeInterceptor();
myInterceptorRegistry.registerInterceptor(mySvc);
@@ -95,6 +101,24 @@ public class PatientIdPartitionInterceptorTest extends BaseJpaR4SystemTest {
});
}
+ /**
+ * Encounter.subject has a FHIRPath expression with a resolve() on it
+ */
+ @Test
+ public void testCreateEncounter_ValidMembershipInCompartment() {
+ createPatientA();
+
+ Encounter encounter = new Encounter();
+ encounter.getSubject().setReference("Patient/A");
+ Long id = myEncounterDao.create(encounter).getId().getIdPartAsLong();
+
+ runInTransaction(() -> {
+ ResourceTable observation = myResourceTableDao.findById(id).orElseThrow(() -> new IllegalArgumentException());
+ assertEquals("Encounter", observation.getResourceType());
+ assertEquals(65, observation.getPartitionId().getPartitionId());
+ });
+ }
+
/**
* Type is not in the patient compartment
*/
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/JpaPackageCacheTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/JpaPackageCacheTest.java
index 04846a9ef18..29be4e2aebc 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/JpaPackageCacheTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/JpaPackageCacheTest.java
@@ -45,6 +45,7 @@ public class JpaPackageCacheTest extends BaseJpaR4Test {
public void disablePartitioning() {
myPartitionSettings.setPartitioningEnabled(false);
myPartitionSettings.setDefaultPartitionId(new PartitionSettings().getDefaultPartitionId());
+ myPartitionSettings.setUnnamedPartitionMode(false);
myInterceptorService.unregisterInterceptor(myRequestTenantPartitionInterceptor);
}
@@ -103,6 +104,38 @@ public class JpaPackageCacheTest extends BaseJpaR4Test {
assertEquals("Deleting package basisprofil.de#0.2.40", deleteOutcomeMsgs.get(0));
}
+ @Test
+ public void testSaveAndDeletePackageUnnamedPartitionsEnabled() throws IOException {
+ myPartitionSettings.setPartitioningEnabled(true);
+ myPartitionSettings.setDefaultPartitionId(0);
+ myPartitionSettings.setUnnamedPartitionMode(true);
+ myInterceptorService.registerInterceptor(new PatientIdPartitionInterceptor());
+ myInterceptorService.registerInterceptor(myRequestTenantPartitionInterceptor);
+
+ try (InputStream stream = ClasspathUtil.loadResourceAsStream("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz")) {
+ myPackageCacheManager.addPackageToCache("hl7.fhir.uv.shorthand", "0.12.0", stream, "hl7.fhir.uv.shorthand");
+ }
+
+ NpmPackage pkg;
+
+ pkg = myPackageCacheManager.loadPackage("hl7.fhir.uv.shorthand", null);
+ assertEquals("0.12.0", pkg.version());
+
+ pkg = myPackageCacheManager.loadPackage("hl7.fhir.uv.shorthand", "0.12.0");
+ assertEquals("0.12.0", pkg.version());
+
+ try {
+ myPackageCacheManager.loadPackage("hl7.fhir.uv.shorthand", "99");
+ fail();
+ } catch (ResourceNotFoundException e) {
+ assertEquals("Unable to locate package hl7.fhir.uv.shorthand#99", e.getMessage());
+ }
+
+ PackageDeleteOutcomeJson deleteOutcomeJson = myPackageCacheManager.uninstallPackage("hl7.fhir.uv.shorthand", "0.12.0");
+ List deleteOutcomeMsgs = deleteOutcomeJson.getMessage();
+ assertEquals("Deleting package hl7.fhir.uv.shorthand#0.12.0", deleteOutcomeMsgs.get(0));
+ }
+
@Test
public void testSavePackageWithLongDescription() throws IOException {
try (InputStream stream = ClasspathUtil.loadResourceAsStream("/packages/package-davinci-cdex-0.2.0.tgz")) {
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java
index b6c1594d4c3..84ef640e24c 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java
@@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.dao.data.INpmPackageDao;
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao;
import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionResourceDao;
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
+import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.NpmPackageEntity;
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionEntity;
import ca.uhn.fhir.jpa.model.entity.NpmPackageVersionResourceEntity;
@@ -127,6 +128,8 @@ public class NpmR4Test extends BaseJpaR4Test {
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
myDaoConfig.setAutoCreatePlaceholderReferenceTargets(new DaoConfig().isAutoCreatePlaceholderReferenceTargets());
myPartitionSettings.setPartitioningEnabled(false);
+ myPartitionSettings.setUnnamedPartitionMode(false);
+ myPartitionSettings.setDefaultPartitionId(new PartitionSettings().getDefaultPartitionId());
myInterceptorService.unregisterInterceptor(myRequestTenantPartitionInterceptor);
}
@@ -451,6 +454,31 @@ public class NpmR4Test extends BaseJpaR4Test {
}
+ @Test
+ public void testInstallR4Package_Twice_partitioningEnabled() throws Exception {
+ myDaoConfig.setAllowExternalReferences(true);
+ myPartitionSettings.setPartitioningEnabled(true);
+ myInterceptorService.registerInterceptor(myRequestTenantPartitionInterceptor);
+
+ byte[] bytes = loadClasspathBytes("/packages/hl7.fhir.uv.shorthand-0.12.0.tgz");
+ myFakeNpmServlet.myResponses.put("/hl7.fhir.uv.shorthand/0.12.0", bytes);
+
+ PackageInstallOutcomeJson outcome;
+
+ PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.12.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
+ outcome = myPackageInstallerSvc.install(spec);
+ assertEquals(1, outcome.getResourcesInstalled().get("CodeSystem"));
+
+ myPackageInstallerSvc.install(spec);
+ outcome = myPackageInstallerSvc.install(spec);
+ assertEquals(null, outcome.getResourcesInstalled().get("CodeSystem"));
+
+ // Ensure that we loaded the contents
+ IBundleProvider searchResult = myCodeSystemDao.search(SearchParameterMap.newSynchronous("url", new UriParam("http://hl7.org/fhir/uv/shorthand/CodeSystem/shorthand-code-system")));
+ assertEquals(1, searchResult.sizeOrThrowNpe());
+
+ }
+
@Test
public void testInstallR4PackageWithNoDescription() throws Exception {
@@ -801,6 +829,43 @@ public class NpmR4Test extends BaseJpaR4Test {
});
}
+ @Test
+ public void testInstallPkgContainingNonPartitionedResourcesPartitionsEnabled() throws Exception {
+ myDaoConfig.setAllowExternalReferences(true);
+ myPartitionSettings.setPartitioningEnabled(true);
+ myInterceptorService.registerInterceptor(myRequestTenantPartitionInterceptor);
+
+ byte[] bytes = loadClasspathBytes("/packages/test-logical-structuredefinition.tgz");
+ myFakeNpmServlet.myResponses.put("/test-logical-structuredefinition/1.0.0", bytes);
+
+ PackageInstallationSpec spec = new PackageInstallationSpec().setName("test-logical-structuredefinition").setVersion("1.0.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL);
+ PackageInstallOutcomeJson outcome = myPackageInstallerSvc.install(spec);
+ assertEquals(2, outcome.getResourcesInstalled().get("StructureDefinition"));
+
+ // Be sure no further communication with the server
+ JettyUtil.closeServer(myServer);
+
+ // Search for the installed resource
+ runInTransaction(() -> {
+ // Confirm that Laborbefund (a logical StructureDefinition) was created without a snapshot.
+ SearchParameterMap map = SearchParameterMap.newSynchronous();
+ map.add(StructureDefinition.SP_URL, new UriParam("https://www.medizininformatik-initiative.de/fhir/core/modul-labor/StructureDefinition/LogicalModel/Laborbefund"));
+ IBundleProvider result = myStructureDefinitionDao.search(map);
+ assertEquals(1, result.sizeOrThrowNpe());
+ List resources = result.getResources(0,1);
+ assertFalse(((StructureDefinition)resources.get(0)).hasSnapshot());
+
+ // Confirm that DiagnosticLab (a resource StructureDefinition with differential but no snapshot) was created with a generated snapshot.
+ map = SearchParameterMap.newSynchronous();
+ map.add(StructureDefinition.SP_URL, new UriParam("https://www.medizininformatik-initiative.de/fhir/core/modul-labor/StructureDefinition/DiagnosticReportLab"));
+ result = myStructureDefinitionDao.search(map);
+ assertEquals(1, result.sizeOrThrowNpe());
+ resources = result.getResources(0,1);
+ assertTrue(((StructureDefinition)resources.get(0)).hasSnapshot());
+
+ });
+ }
+
static class FakeNpmServlet extends HttpServlet {
private final Map myResponses = new HashMap<>();
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java
index 77f2315c5de..d5eac98d7d4 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ConceptMapTest.java
@@ -7,6 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.BooleanType;
+import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
@@ -27,6 +28,8 @@ import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.rest.api.MethodOutcome;
+import java.io.IOException;
+
public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderR4ConceptMapTest.class);
@@ -171,6 +174,101 @@ public class ResourceProviderR4ConceptMapTest extends BaseResourceProviderR4Test
assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString());
}
+
+ @Test
+ public void testTranslateByCodeSystemsAndSourceCodeOneToOne_InBatchOperation() {
+ ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
+
+ ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
+
+ Bundle bundle = new Bundle();
+ bundle.setType(Bundle.BundleType.BATCH);
+ bundle
+ .addEntry()
+ .getRequest()
+ .setMethod(Bundle.HTTPVerb.GET)
+ .setUrl("ConceptMap/$translate?system=" + CS_URL + "&code=12345" + "&targetsystem=" + CS_URL_2);
+
+ ourLog.info("Request:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
+
+ Bundle respBundle = myClient
+ .transaction()
+ .withBundle(bundle)
+ .execute();
+
+ ourLog.info("Response:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respBundle));
+
+ assertEquals(1, respBundle.getEntry().size());
+ Parameters respParams = (Parameters) respBundle.getEntry().get(0).getResource();
+
+ ParametersParameterComponent param = getParameterByName(respParams, "result");
+ assertTrue(((BooleanType) param.getValue()).booleanValue());
+
+ param = getParameterByName(respParams, "message");
+ assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
+
+ assertEquals(1, getNumberOfParametersByName(respParams, "match"));
+
+ param = getParameterByName(respParams, "match");
+ assertEquals(3, param.getPart().size());
+ ParametersParameterComponent part = getPartByName(param, "equivalence");
+ assertEquals("equal", ((CodeType) part.getValue()).getCode());
+ part = getPartByName(param, "concept");
+ Coding coding = (Coding) part.getValue();
+ assertEquals("34567", coding.getCode());
+ assertEquals("Target Code 34567", coding.getDisplay());
+ assertFalse(coding.getUserSelected());
+ assertEquals(CS_URL_2, coding.getSystem());
+ assertEquals("Version 2", coding.getVersion());
+ part = getPartByName(param, "source");
+ assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString());
+ }
+
+ @Test
+ public void testTranslateByCodeSystemsAndSourceCodeOneToOne_InBatchOperation2() throws IOException {
+ ConceptMap cm = loadResourceFromClasspath(ConceptMap.class, "/r4/conceptmap.json");
+ myConceptMapDao.update(cm);
+
+ Bundle bundle = new Bundle();
+ bundle.setType(Bundle.BundleType.BATCH);
+ bundle
+ .addEntry()
+ .getRequest()
+ .setMethod(Bundle.HTTPVerb.GET)
+ .setUrl("ConceptMap/$translate?url=http://hl7.org/fhir/ConceptMap/CMapHie&system=http://fkcfhir.org/fhir/cs/FMCECCOrderAbbreviation&code=IMed_Janssen&targetsystem=http://fkcfhir.org/fhir/cs/FMCHIEOrderAbbreviation");
+
+ ourLog.info("Request:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
+
+ Bundle respBundle = myClient
+ .transaction()
+ .withBundle(bundle)
+ .execute();
+
+ ourLog.info("Response:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respBundle));
+
+ assertEquals(1, respBundle.getEntry().size());
+ Parameters respParams = (Parameters) respBundle.getEntry().get(0).getResource();
+
+ ParametersParameterComponent param = getParameterByName(respParams, "result");
+ assertTrue(((BooleanType) param.getValue()).booleanValue());
+
+ param = getParameterByName(respParams, "message");
+ assertEquals("Matches found", ((StringType) param.getValue()).getValueAsString());
+
+ assertEquals(1, getNumberOfParametersByName(respParams, "match"));
+
+ param = getParameterByName(respParams, "match");
+ assertEquals(3, param.getPart().size());
+ ParametersParameterComponent part = getPartByName(param, "equivalence");
+ assertEquals("equivalent", ((CodeType) part.getValue()).getCode());
+ part = getPartByName(param, "concept");
+ Coding coding = (Coding) part.getValue();
+ assertEquals("212", coding.getCode());
+ assertEquals("COVID-19 Vaccine,vecton-nr,rS-Ad26,PF,0.5mL", coding.getDisplay());
+ assertFalse(coding.getUserSelected());
+ assertEquals("http://fkcfhir.org/fhir/cs/FMCHIEOrderAbbreviation", coding.getSystem());
+ }
+
@Test
public void testTranslateByCodeSystemsAndSourceCodeUnmapped() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java
index 148ec75843d..a3660d279f1 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java
@@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.jpa.entity.TermConcept;
+import ca.uhn.fhir.jpa.entity.TermConceptDesignation;
import ca.uhn.fhir.jpa.entity.TermConceptProperty;
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
@@ -35,6 +36,7 @@ import org.mockito.Mockito;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -45,6 +47,7 @@ import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERL
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERLIST_LINK_DUPLICATE_FILE_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERLIST_LINK_FILE_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CODESYSTEM_VERSION;
+import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_CONSUMER_NAME_FILE_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_DOCUMENT_ONTOLOGY_FILE_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_DUPLICATE_FILE_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_FILE_DEFAULT;
@@ -53,6 +56,8 @@ import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_GROUP_T
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_HIERARCHY_FILE_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_IEEE_MEDICAL_DEVICE_CODE_MAPPING_TABLE_FILE_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_IMAGING_DOCUMENT_CODES_FILE_DEFAULT;
+import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_LINGUISTIC_VARIANTS_FILE_DEFAULT;
+import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_LINGUISTIC_VARIANTS_PATH_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_PARENT_GROUP_FILE_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_PART_FILE_DEFAULT;
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_PART_LINK_FILE_DEFAULT;
@@ -111,8 +116,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
@Mock
private ITermDeferredStorageSvc myTermDeferredStorageSvc;
- private Parameters parameters = new Parameters();
-
+ private Parameters myParameters = new Parameters();
@BeforeEach
public void before() {
@@ -120,15 +124,13 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
myFiles = new ZipCollectionBuilder();
}
-
-
@Nested
public class TestsUsingDefaultMakeCurrentVerionParameter {
@BeforeEach
public void before() {
- parameters.addParameter().setName(MAKE_CURRENT_VERSION).setValue(new StringType("true"));
- when(mySrd.getResource()).thenReturn(parameters);
+ myParameters.addParameter().setName(MAKE_CURRENT_VERSION).setValue(new StringType("true"));
+ when(mySrd.getResource()).thenReturn(myParameters);
}
@Test
@@ -146,7 +148,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
@Test
public void testLoadLoincWithMandatoryFilesOnly() throws Exception {
addLoincMandatoryFilesWithoutTop2000ToZip(myFiles);
- verifyLoadLoinc(false);
+ verifyLoadLoinc(false, false);
}
@Test
@@ -184,18 +186,24 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
}
-
- private void verifyLoadLoinc() {
- verifyLoadLoinc(true);
+ @Test
+ public void testLoadLoincWithConsumerNameAndLinguisticVariants() throws Exception {
+ addLoincMandatoryFilesAndConsumerNameAndLinguisticVariants(myFiles);
+ verifyLoadLoinc(false, true);
}
- private void verifyLoadLoinc(boolean theIncludeTop2000) {
+
+ private void verifyLoadLoinc() {
+ verifyLoadLoinc(true, false);
+ }
+
+ private void verifyLoadLoinc(boolean theIncludeTop2000, boolean theIncludeConsumerNameAndLinguisticVariants) {
// Actually do the load
mySvc.loadLoinc(myFiles.getFiles(), mySrd);
verify(myTermCodeSystemStorageSvc, times(1)).storeNewCodeSystemVersion(
- mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class), myValueSetsCaptor.capture(),
- myConceptMapCaptor.capture(), eq(true));
+ mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class),
+ myValueSetsCaptor.capture(), myConceptMapCaptor.capture(), eq(true));
Map concepts = extractConcepts();
Map valueSets = extractValueSets();
Map conceptMaps = extractConceptMaps();
@@ -475,6 +483,22 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
assertEquals(2, vs.getCompose().getInclude().get(0).getConcept().size());
assertEquals("17424-3", vs.getCompose().getInclude().get(0).getConcept().get(0).getCode());
assertEquals("13006-2", vs.getCompose().getInclude().get(0).getConcept().get(1).getCode());
+
+ // Consumer Name
+ if (theIncludeConsumerNameAndLinguisticVariants) {
+ code = concepts.get("61438-8");
+ assertEquals(28, code.getDesignations().size());
+ verifyConsumerName(code.getDesignations(), "Consumer Name 61438-8");
+ verifyLinguisticVariant(code.getDesignations(), "de-AT", "Entlassungsbrief Ärztlich","Ergebnis","Zeitpunkt","{Setting}","Dokument","Dermatologie","DOC.ONTOLOGY","de shortname","de long common name","de related names 2","de linguistic variant display name");
+ verifyLinguisticVariant(code.getDesignations(), "fr-CA", "Cellules de Purkinje cytoplasmique type 2 , IgG","Titre","Temps ponctuel","Sérum","Quantitatif","Immunofluorescence","Sérologie","","","","");
+ verifyLinguisticVariant(code.getDesignations(), "zh-CN", "血流速度.收缩期.最大值","速度","时间点","大脑中动脉","定量型","超声.多普勒","产科学检查与测量指标.超声","","", "Cereb 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 大脑(Cerebral) 时刻;随机;随意;瞬间 术语\"cerebral\"指的是主要由中枢半球(大脑皮质和基底神经节)组成的那部分脑结构 流 流量;流速;流体 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)","");
+ code = concepts.get("17787-3");
+ assertEquals(19, code.getDesignations().size());
+ verifyConsumerName(code.getDesignations(), "Consumer Name 17787-3");
+ verifyLinguisticVariant(code.getDesignations(), "de-AT", "","","","","","","","","","CoV OC43 RNA ql/SM P","Coronavirus OC43 RNA ql. /Sondermaterial PCR");
+ verifyLinguisticVariant(code.getDesignations(), "fr-CA", "Virus respiratoire syncytial bovin","Présence-Seuil","Temps ponctuel","XXX","Ordinal","Culture spécifique à un microorganisme","Microbiologie","","","","");
+ verifyLinguisticVariant(code.getDesignations(), "zh-CN", "血流速度.收缩期.最大值","速度","时间点","二尖瓣^胎儿","定量型","超声.多普勒","产科学检查与测量指标.超声","","","僧帽瓣 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 时刻;随机;随意;瞬间 流 流量;流速;流体 胎;超系统 - 胎儿 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)","");
+ }
}
@Test
@@ -661,7 +685,6 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
}
}
-
@Test
public void testLoadLoincMultiaxialHierarchySupport() throws Exception {
addLoincMandatoryFilesToZip(myFiles);
@@ -670,8 +693,8 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
mySvc.loadLoinc(myFiles.getFiles(), mySrd);
verify(myTermCodeSystemStorageSvc, times(1)).storeNewCodeSystemVersion(
- mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class), myValueSetsCaptor.capture(),
- myConceptMapCaptor.capture(), eq(true));
+ mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class),
+ myValueSetsCaptor.capture(), myConceptMapCaptor.capture(), eq(true));
Map concepts = extractConcepts();
TermConcept code;
@@ -769,6 +792,7 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
+
@Nested
public class CurrentVersionParameter {
private TermLoaderSvcImpl testedSvc;
@@ -778,12 +802,11 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
@SuppressWarnings("unchecked")
@Mock private final List mockFileDescriptorList = mock(List.class);
- private final Parameters myParameters = new Parameters();
@BeforeEach
void beforeEachCurrentVersionParameterTest() {
testedSvc = spy(mySvc);
- doReturn(mockFileDescriptors).when(testedSvc).getLoadedFileDescriptors(any());
+ doReturn(mockFileDescriptors).when(testedSvc).getLoadedFileDescriptors(mockFileDescriptorList);
doReturn(testProps).when(testedSvc).getProperties(any(), eq(LOINC_UPLOAD_PROPERTIES_FILE.getCode()));
}
@@ -865,12 +888,27 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
+
+
+
public static void addLoincMandatoryFilesAndSinglePartLinkToZip(ZipCollectionBuilder theFiles) throws IOException {
addBaseLoincMandatoryFilesToZip(theFiles, true);
theFiles.addFileZip("/loinc/", "loincupload_singlepartlink.properties");
theFiles.addFileZip("/loinc/", LOINC_PART_LINK_FILE_DEFAULT.getCode());
}
+ public static void addLoincMandatoryFilesAndConsumerNameAndLinguisticVariants(ZipCollectionBuilder theFiles) throws IOException {
+ addBaseLoincMandatoryFilesToZip(theFiles, true);
+ theFiles.addFileZip("/loinc/", "loincupload_singlepartlink.properties");
+ theFiles.addFileZip("/loinc/", LOINC_PART_LINK_FILE_DEFAULT.getCode());
+ theFiles.addFileZip("/loinc/", LOINC_CONSUMER_NAME_FILE_DEFAULT.getCode());
+ theFiles.addFileZip("/loinc/", LOINC_LINGUISTIC_VARIANTS_FILE_DEFAULT.getCode());
+ theFiles.addFileZip("/loinc/", LOINC_LINGUISTIC_VARIANTS_PATH_DEFAULT.getCode() + "zhCN5LinguisticVariant.csv");
+ theFiles.addFileZip("/loinc/", LOINC_LINGUISTIC_VARIANTS_PATH_DEFAULT.getCode() + "deAT24LinguisticVariant.csv");
+ theFiles.addFileZip("/loinc/", LOINC_LINGUISTIC_VARIANTS_PATH_DEFAULT.getCode() + "frCA8LinguisticVariant.csv");
+ }
+
+
public static void addLoincMandatoryFilesToZip(ZipCollectionBuilder theFiles) throws IOException {
addBaseLoincMandatoryFilesToZip(theFiles, true);
theFiles.addFileZip("/loinc/", LOINC_UPLOAD_PROPERTIES_FILE.getCode());
@@ -916,6 +954,88 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
theFiles.addFileZip("/loinc/", LOINC_TOP2000_COMMON_LAB_RESULTS_SI_FILE_DEFAULT.getCode());
theFiles.addFileZip("/loinc/", LOINC_TOP2000_COMMON_LAB_RESULTS_US_FILE_DEFAULT.getCode());
}
+
}
+ private static void verifyConsumerName(Collection designationList, String theConsumerName) {
+
+ TermConceptDesignation consumerNameDesignation = null;
+ for (TermConceptDesignation designation : designationList) {
+ if ("ConsumerName".equals(designation.getUseDisplay() )) {
+ consumerNameDesignation = designation;
+ }
+ }
+ assertEquals(theConsumerName, consumerNameDesignation.getValue());
+ }
+
+ private static void verifyLinguisticVariant(Collection designationList, String theLanguage,
+ String theComponent, String theProperty, String theTimeAspct, String theSystem, String theScaleTyp,
+ String methodType, String theClass, String theShortName, String theLongCommonName, String theRelatedName2,
+ String theLinguisticVariantDisplayName) {
+
+ TermConceptDesignation componentDes = null;
+ TermConceptDesignation propertyDes = null;
+ TermConceptDesignation timeAspctDes = null;
+ TermConceptDesignation systemDes = null;
+ TermConceptDesignation scaleTypDes = null;
+
+ TermConceptDesignation methodTypDes = null;
+ TermConceptDesignation classDes = null;
+ TermConceptDesignation shortNameDes = null;
+ TermConceptDesignation longCommonNameDes = null;
+ TermConceptDesignation relatedNames2Des = null;
+
+ TermConceptDesignation linguisticVariantDisplayNameDes = null;
+
+ for (TermConceptDesignation designation : designationList) {
+ if (theLanguage.equals(designation.getLanguage())) {
+
+ if ("COMPONENT".equals(designation.getUseDisplay()))
+ componentDes = designation;
+ if ("PROPERTY".equals(designation.getUseDisplay()))
+ propertyDes = designation;
+ if ("TIME_ASPCT".equals(designation.getUseDisplay()))
+ timeAspctDes = designation;
+ if ("SYSTEM".equals(designation.getUseDisplay()))
+ systemDes = designation;
+ if ("SCALE_TYP".equals(designation.getUseDisplay()))
+ scaleTypDes = designation;
+
+ if ("METHOD_TYP".equals(designation.getUseDisplay()))
+ methodTypDes = designation;
+ if ("CLASS".equals(designation.getUseDisplay()))
+ classDes = designation;
+ if ("SHORTNAME".equals(designation.getUseDisplay()))
+ shortNameDes = designation;
+ if ("LONG_COMMON_NAME".equals(designation.getUseDisplay()))
+ longCommonNameDes = designation;
+ if ("RELATEDNAMES2".equals(designation.getUseDisplay()))
+ relatedNames2Des = designation;
+
+ if ("LinguisticVariantDisplayName".equals(designation.getUseDisplay()))
+ linguisticVariantDisplayNameDes = designation;
+ }
+ }
+ verifyDesignation(componentDes, ITermLoaderSvc.LOINC_URI, "COMPONENT", theComponent);
+ verifyDesignation(propertyDes, ITermLoaderSvc.LOINC_URI, "PROPERTY", theProperty);
+ verifyDesignation(timeAspctDes, ITermLoaderSvc.LOINC_URI, "TIME_ASPCT", theTimeAspct);
+ verifyDesignation(systemDes, ITermLoaderSvc.LOINC_URI, "SYSTEM", theSystem);
+ verifyDesignation(scaleTypDes, ITermLoaderSvc.LOINC_URI, "SCALE_TYP", theScaleTyp);
+
+ verifyDesignation(methodTypDes, ITermLoaderSvc.LOINC_URI, "METHOD_TYP", methodType);
+ verifyDesignation(classDes, ITermLoaderSvc.LOINC_URI, "CLASS", theClass);
+ verifyDesignation(shortNameDes, ITermLoaderSvc.LOINC_URI, "SHORTNAME", theShortName);
+ verifyDesignation(longCommonNameDes, ITermLoaderSvc.LOINC_URI, "LONG_COMMON_NAME", theLongCommonName);
+ verifyDesignation(relatedNames2Des, ITermLoaderSvc.LOINC_URI, "RELATEDNAMES2", theRelatedName2);
+
+ verifyDesignation(linguisticVariantDisplayNameDes, ITermLoaderSvc.LOINC_URI, "LinguisticVariantDisplayName", theLinguisticVariantDisplayName);
+ }
+
+ private static void verifyDesignation(TermConceptDesignation theDesignation, String theUseSystem, String theUseCode, String theValue) {
+ if (theDesignation == null)
+ return;
+ assertEquals(theUseSystem, theDesignation.getUseSystem());
+ assertEquals(theUseCode, theDesignation.getUseCode());
+ assertEquals(theValue, theDesignation.getValue());
+ }
}
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/ConsumerName/ConsumerName.csv b/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/ConsumerName/ConsumerName.csv
new file mode 100644
index 00000000000..b4ec2907985
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/ConsumerName/ConsumerName.csv
@@ -0,0 +1,6 @@
+"LoincNumber","ConsumerName"
+"61438-8","Consumer Name 61438-8"
+,"Consumer Name X"
+47239-9",""
+"17787-3","Consumer Name 17787-3"
+"38699-5","1,1-Dichloroethane, Air"
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/LinguisticVariants/LinguisticVariants.csv b/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/LinguisticVariants/LinguisticVariants.csv
new file mode 100644
index 00000000000..c79b3197490
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/LinguisticVariants/LinguisticVariants.csv
@@ -0,0 +1,9 @@
+"ID","ISO_LANGUAGE","ISO_COUNTRY","LANGUAGE_NAME","PRODUCER"
+"5","zh","CN","Chinese (CHINA)","Lin Zhang, A LOINC volunteer from China"
+"7","es","AR","Spanish (ARGENTINA)","Conceptum Medical Terminology Center"
+"8","fr","CA","French (CANADA)","Canada Health Infoway Inc."
+,"de","AT","German (AUSTRIA)","ELGA, Austria"
+"88",,"AT","German (AUSTRIA)","ELGA, Austria"
+"89","de",,"German (AUSTRIA)","ELGA, Austria"
+"90","de","AT",,"ELGA, Austria"
+"24","de","AT","German (AUSTRIA)","ELGA, Austria"
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/LinguisticVariants/deAT24LinguisticVariant.csv b/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/LinguisticVariants/deAT24LinguisticVariant.csv
new file mode 100644
index 00000000000..c6882d47504
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/LinguisticVariants/deAT24LinguisticVariant.csv
@@ -0,0 +1,4 @@
+"LOINC_NUM","COMPONENT","PROPERTY","TIME_ASPCT","SYSTEM","SCALE_TYP","METHOD_TYP","CLASS","SHORTNAME","LONG_COMMON_NAME","RELATEDNAMES2","LinguisticVariantDisplayName"
+"61438-8","Entlassungsbrief Ärztlich","Ergebnis","Zeitpunkt","{Setting}","Dokument","Dermatologie","DOC.ONTOLOGY","de shortname","de long common name","de related names 2","de linguistic variant display name"
+"43730-1","","","","","","","","","","EBV-DNA qn. PCR","EBV-DNA quantitativ PCR"
+"17787-3","","","","","","","","","","CoV OC43 RNA ql/SM P","Coronavirus OC43 RNA ql. /Sondermaterial PCR"
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/LinguisticVariants/frCA8LinguisticVariant.csv b/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/LinguisticVariants/frCA8LinguisticVariant.csv
new file mode 100644
index 00000000000..fa09e0cb242
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/LinguisticVariants/frCA8LinguisticVariant.csv
@@ -0,0 +1,6 @@
+"LOINC_NUM","COMPONENT","PROPERTY","TIME_ASPCT","SYSTEM","SCALE_TYP","METHOD_TYP","CLASS","SHORTNAME","LONG_COMMON_NAME","RELATEDNAMES2","LinguisticVariantDisplayName"
+"61438-8","Cellules de Purkinje cytoplasmique type 2 , IgG","Titre","Temps ponctuel","Sérum","Quantitatif","Immunofluorescence","Sérologie","","","",""
+"11704-4","Gliale nucléaire de type 1 , IgG","Titre","Temps ponctuel","LCR","Quantitatif","Immunofluorescence","Sérologie","","","",""
+,"Cellules de Purkinje cytoplasmique type 2 , IgG","Titre","Temps ponctuel","Sérum","Quantitatif",,,"","","",""
+"17787-3","Virus respiratoire syncytial bovin","Présence-Seuil","Temps ponctuel","XXX","Ordinal","Culture spécifique à un microorganisme","Microbiologie","","","",""
+"17788-1","Cellules de Purkinje cytoplasmique type 2 , IgG","Titre","Temps ponctuel","Sérum","Quantitatif",,,"","","",""
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/LinguisticVariants/zhCN5LinguisticVariant.csv b/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/LinguisticVariants/zhCN5LinguisticVariant.csv
new file mode 100644
index 00000000000..48d07d9fdc3
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/AccessoryFiles/LinguisticVariants/zhCN5LinguisticVariant.csv
@@ -0,0 +1,9 @@
+"LOINC_NUM","COMPONENT","PROPERTY","TIME_ASPCT","SYSTEM","SCALE_TYP","METHOD_TYP","CLASS","SHORTNAME","LONG_COMMON_NAME","RELATEDNAMES2","LinguisticVariantDisplayName"
+"61438-8","血流速度.收缩期.最大值","速度","时间点","大脑中动脉","定量型","超声.多普勒","产科学检查与测量指标.超声","","","Cereb 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 大脑(Cerebral) 时刻;随机;随意;瞬间 术语""cerebral""指的是主要由中枢半球(大脑皮质和基底神经节)组成的那部分脑结构 流 流量;流速;流体 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)",""
+"11704-4","血流速度.收缩期.最大值","速度","时间点","动脉导管","定量型","超声.多普勒","产科学检查与测量指标.超声","","","动态 动脉管 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 时刻;随机;随意;瞬间 流 流量;流速;流体 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)",""
+"17787-3","血流速度.收缩期.最大值","速度","时间点","二尖瓣^胎儿","定量型","超声.多普勒","产科学检查与测量指标.超声","","","僧帽瓣 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 时刻;随机;随意;瞬间 流 流量;流速;流体 胎;超系统 - 胎儿 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)",""
+"61438-6",,"速度","时间点","二尖瓣^胎儿","定量型","超声.多普勒","产科学检查与测量指标.超声","","","僧帽瓣 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 时刻;随机;随意;瞬间 流 流量;流速;流体 胎;超系统 - 胎儿 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)",""
+"10000-8","血流速度.收缩期.最大值",,"时间点","二尖瓣^胎儿","定量型","超声.多普勒","产科学检查与测量指标.超声","","","僧帽瓣 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 时刻;随机;随意;瞬间 流 流量;流速;流体 胎;超系统 - 胎儿 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)",""
+"17788-1","血流速度.收缩期.最大值","速度",,"大脑中动脉","定量型","超声.多普勒","产科学检查与测量指标.超声","","","Cereb 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 大脑(Cerebral) 时刻;随机;随意;瞬间 术语""cerebral""指的是主要由中枢半球(大脑皮质和基底神经节)组成的那部分脑结构 流 流量;流速;流体 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)",""
+"11488-4","血流速度.收缩期.最大值","速度","时间点",,"定量型","超声.多普勒","产科学检查与测量指标.超声","","","Cereb 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 大脑(Cerebral) 时刻;随机;随意;瞬间 术语""cerebral""指的是主要由中枢半球(大脑皮质和基底神经节)组成的那部分脑结构 流 流量;流速;流体 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)",""
+"47239-9","血流速度.收缩期.最大值","速度","时间点","大脑中动脉",,"超声.多普勒","产科学检查与测量指标.超声","","","Cereb 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 大脑(Cerebral) 时刻;随机;随意;瞬间 术语""cerebral""指的是主要由中枢半球(大脑皮质和基底神经节)组成的那部分脑结构 流 流量;流速;流体 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)",""
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/loincupload.properties b/hapi-fhir-jpaserver-base/src/test/resources/loinc/loincupload.properties
index 986d2afdaa0..3d36353cec0 100644
--- a/hapi-fhir-jpaserver-base/src/test/resources/loinc/loincupload.properties
+++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/loincupload.properties
@@ -81,3 +81,13 @@ loinc.group.terms.file=AccessoryFiles/GroupFile/GroupLoincTerms.csv
## Default value if key not provided: AccessoryFiles/GroupFile/ParentGroup.csv
## File may be omitted
loinc.parent.group.file=AccessoryFiles/GroupFile/ParentGroup.csv
+
+# Consumer Names
+## Default value if key not provided: AccessoryFiles/ConsumerName/ConsumerName.csv
+## File may be omitted
+loinc.consumer.name.file=AccessoryFiles/ConsumerName/ConsumerName.csv
+
+# Linguistic Variants
+## Default value if key not provided: AccessoryFiles/LinguisticVariants/LinguisticVariants.csv
+## File may be omitted
+loinc.linguistic.variants.file=AccessoryFiles/LinguisticVariants/LinguisticVariants.csv
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/v267_loincupload.properties b/hapi-fhir-jpaserver-base/src/test/resources/loinc/v267_loincupload.properties
index f9a049fdf06..7ffe8c9f97f 100644
--- a/hapi-fhir-jpaserver-base/src/test/resources/loinc/v267_loincupload.properties
+++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/v267_loincupload.properties
@@ -85,3 +85,13 @@ loinc.group.terms.file=AccessoryFiles/GroupFile/GroupLoincTerms.csv
## Default value if key not provided: AccessoryFiles/GroupFile/ParentGroup.csv
## File may be omitted
loinc.parent.group.file=AccessoryFiles/GroupFile/ParentGroup.csv
+
+# Consumer Names
+## Default value if key not provided: AccessoryFiles/ConsumerName/ConsumerName.csv
+## File may be omitted
+loinc.consumer.name.file=AccessoryFiles/ConsumerName/ConsumerName.csv
+
+# Linguistic Variants
+## Default value if key not provided: AccessoryFiles/LinguisticVariants/LinguisticVariants.csv
+## File may be omitted
+loinc.linguistic.variants.file=AccessoryFiles/LinguisticVariants/LinguisticVariants.csv
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/v268_loincupload.properties b/hapi-fhir-jpaserver-base/src/test/resources/loinc/v268_loincupload.properties
index cb7d1eb0345..dc4cdd60135 100644
--- a/hapi-fhir-jpaserver-base/src/test/resources/loinc/v268_loincupload.properties
+++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/v268_loincupload.properties
@@ -85,3 +85,13 @@ loinc.group.terms.file=AccessoryFiles/GroupFile/GroupLoincTerms.csv
## Default value if key not provided: AccessoryFiles/GroupFile/ParentGroup.csv
## File may be omitted
loinc.parent.group.file=AccessoryFiles/GroupFile/ParentGroup.csv
+
+# Consumer Names
+## Default value if key not provided: AccessoryFiles/ConsumerName/ConsumerName.csv
+## File may be omitted
+loinc.consumer.name.file=AccessoryFiles/ConsumerName/ConsumerName.csv
+
+# Linguistic Variants
+## Default value if key not provided: AccessoryFiles/LinguisticVariants/LinguisticVariants.csv
+## File may be omitted
+loinc.linguistic.variants.file=AccessoryFiles/LinguisticVariants/LinguisticVariants.csv
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/r4/conceptmap.json b/hapi-fhir-jpaserver-base/src/test/resources/r4/conceptmap.json
new file mode 100644
index 00000000000..ec54ef8f81f
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/resources/r4/conceptmap.json
@@ -0,0 +1,98 @@
+{
+ "resourceType": "ConceptMap",
+ "id": "CMapHie",
+ "meta": {
+ "extension": [
+ {
+ "url": "http://hapifhir.io/fhir/StructureDefinition/resource-meta-source",
+ "valueUri": "#VAL8lnninHkvaEWc"
+ }
+ ],
+ "versionId": "1",
+ "lastUpdated": "2021-07-08T14:19:11.748-04:00"
+ },
+ "url": "http://hl7.org/fhir/ConceptMap/CMapHie",
+ "identifier": {
+ "system": "urn:ietf:rfc:3986",
+ "value": "urn:uuid:53cd62ee-033e-414c-9f58-3ca97b5ffc3b"
+ },
+ "version": "4.0.1",
+ "name": "FHIR-v3-Address-Use",
+ "title": "FHIR/v3 Address Use Mapping",
+ "status": "draft",
+ "experimental": true,
+ "date": "2012-06-13",
+ "publisher": "HL7, Inc",
+ "contact": [
+ {
+ "name": "FHIR project team (example)",
+ "telecom": [
+ {
+ "system": "url",
+ "value": "http://hl7.org/fhir"
+ }
+ ]
+ }
+ ],
+ "description": "A mapping between the ECC and HIE Code systems",
+ "useContext": [
+ {
+ "code": {
+ "system": "http://terminology.hl7.org/CodeSystem/usage-context-type",
+ "code": "venue"
+ },
+ "valueCodeableConcept": {
+ "text": "for CCDA Usage"
+ }
+ }
+ ],
+ "jurisdiction": [
+ {
+ "coding": [
+ {
+ "system": "urn:iso:std:iso:3166",
+ "code": "US"
+ }
+ ]
+ }
+ ],
+ "purpose": "To help implementers map from HL7 v3/CDA to FHIR",
+ "copyright": "Creative Commons 0",
+ "sourceUri": "http://fkcfhir.org/fhir/vs/FMCOrderAbbreviation",
+ "targetUri": "http://fkcfhir.org/fhir/vs/FMCHIEAbbreviation",
+ "group": [
+ {
+ "source": "http://fkcfhir.org/fhir/cs/FMCECCOrderAbbreviation",
+ "target": "http://fkcfhir.org/fhir/cs/FMCHIEOrderAbbreviation",
+ "element": [
+ {
+ "code": "IMed_Janssen",
+ "display": "COVID-19 Vaccine-Janssen",
+ "target": [
+ {
+ "code": "212",
+ "display": "COVID-19 Vaccine,vecton-nr,rS-Ad26,PF,0.5mL",
+ "equivalence": "equivalent"
+ }
+ ]
+ },
+ {
+ "code": "IMed_Moderna1",
+ "display": "COVID-19 Vaccine-Moderna (Dose 1 of 2)",
+ "target": [
+ {
+ "code": "207",
+ "display": "COVID-19, mRNA,LNP-S,PF,100 mcg/0.5 mL dose",
+ "equivalence": "equivalent"
+ }
+ ]
+ }
+ ],
+ "unmapped": {
+ "mode": "fixed",
+ "code": "unknown",
+ "display": "unknown"
+ }
+ }
+ ]
+}
diff --git a/hapi-fhir-jpaserver-batch/pom.xml b/hapi-fhir-jpaserver-batch/pom.xml
index 8ca2d1c072a..86a71548d81 100644
--- a/hapi-fhir-jpaserver-batch/pom.xml
+++ b/hapi-fhir-jpaserver-batch/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-cql/pom.xml b/hapi-fhir-jpaserver-cql/pom.xml
index 781bcd0d3a8..fa531134acf 100644
--- a/hapi-fhir-jpaserver-cql/pom.xml
+++ b/hapi-fhir-jpaserver-cql/pom.xml
@@ -7,7 +7,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml
index 4b811ba92db..cb849b120a7 100644
--- a/hapi-fhir-jpaserver-mdm/pom.xml
+++ b/hapi-fhir-jpaserver-mdm/pom.xml
@@ -6,7 +6,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-migrate/pom.xml b/hapi-fhir-jpaserver-migrate/pom.xml
index 1f9c07fa37c..2df96164ac4 100644
--- a/hapi-fhir-jpaserver-migrate/pom.xml
+++ b/hapi-fhir-jpaserver-migrate/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java
index e25b83b4a55..6bed216c9f5 100644
--- a/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java
+++ b/hapi-fhir-jpaserver-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java
@@ -99,6 +99,24 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks {
version.onTable("HFJ_BLK_EXPORT_JOB")
.modifyColumn("20210624.1","REQUEST").nonNullable().withType(ColumnTypeEnum.STRING, 1024);
+
+ version.onTable("HFJ_IDX_CMP_STRING_UNIQ")
+ .modifyColumn("20210713.1","IDX_STRING").nonNullable().withType(ColumnTypeEnum.STRING, 500);
+
+ version.onTable("HFJ_RESOURCE")
+ .addColumn("20210720.1", "SP_CMPTOKS_PRESENT").nullable().type(ColumnTypeEnum.BOOLEAN);
+
+ version.addIdGenerator("20210720.2", "SEQ_IDXCMBTOKNU_ID");
+
+ Builder.BuilderAddTableByColumns cmpToks = version
+ .addTableByColumns("20210720.3", "HFJ_IDX_CMB_TOK_NU", "PID");
+ cmpToks.addColumn("PID").nonNullable().type(ColumnTypeEnum.LONG);
+ cmpToks.addColumn("RES_ID").nonNullable().type(ColumnTypeEnum.LONG);
+ cmpToks.addColumn("HASH_COMPLETE").nonNullable().type(ColumnTypeEnum.LONG);
+ cmpToks.addColumn("IDX_STRING").nonNullable().type(ColumnTypeEnum.STRING, 500);
+ cmpToks.addForeignKey("20210720.4", "FK_IDXCMBTOKNU_RES_ID").toColumn("RES_ID").references("HFJ_RESOURCE", "RES_ID");
+ cmpToks.addIndex("20210720.5", "IDX_IDXCMBTOKNU_STR").unique(false).withColumns("IDX_STRING");
+ cmpToks.addIndex("20210720.6", "IDX_IDXCMBTOKNU_RES").unique(false).withColumns("RES_ID");
}
private void init540() {
diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml
index ff100b79b66..63e2f0101db 100644
--- a/hapi-fhir-jpaserver-model/pom.xml
+++ b/hapi-fhir-jpaserver-model/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java
index 516cee8141f..c826f2c6e36 100644
--- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java
+++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java
@@ -43,6 +43,7 @@ import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import java.util.Date;
+import java.util.List;
@MappedSuperclass
public abstract class BaseResourceIndexedSearchParam extends BaseResourceIndex {
@@ -182,6 +183,17 @@ public abstract class BaseResourceIndexedSearchParam extends BaseResourceIndex {
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName);
}
+ public static long calculateHashIdentity(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, List theAdditionalValues) {
+ String[] values = new String[theAdditionalValues.size() + 2];
+ values[0] = theResourceType;
+ values[1] = theParamName;
+ for (int i = 0; i < theAdditionalValues.size(); i++) {
+ values[i + 2] = theAdditionalValues.get(i);
+ }
+
+ return hash(thePartitionSettings, theRequestPartitionId, values);
+ }
+
/**
* Applies a fast and consistent hashing algorithm to a set of strings
*/
diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionResourceEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionResourceEntity.java
index 8b8e7ebc551..044bef976df 100644
--- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionResourceEntity.java
+++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionResourceEntity.java
@@ -21,6 +21,8 @@ package ca.uhn.fhir.jpa.model.entity;
*/
import ca.uhn.fhir.context.FhirVersionEnum;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -157,4 +159,20 @@ public class NpmPackageVersionResourceEntity {
myCanonicalUrl = theCanonicalUrl;
}
+ @Override
+ public String toString() {
+
+ return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
+ .append("myId", myId)
+ .append("myCanonicalUrl", myCanonicalUrl)
+ .append("myCanonicalVersion", myCanonicalVersion)
+ .append("myResourceType", myResourceType)
+ .append("myDirectory", myDirectory)
+ .append("myFilename", myFilename)
+ .append("myPackageVersion", myPackageVersion)
+ .append("myResSizeBytes", myResSizeBytes)
+ .append("myVersion", myVersion)
+ .toString();
+ }
+
}
diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedCompositeStringUnique.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java
similarity index 82%
rename from hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedCompositeStringUnique.java
rename to hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java
index 50b6161c55b..72fff2c3556 100644
--- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedCompositeStringUnique.java
+++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java
@@ -32,12 +32,12 @@ import javax.persistence.*;
@Entity()
@Table(name = "HFJ_IDX_CMP_STRING_UNIQ", indexes = {
- @Index(name = ResourceIndexedCompositeStringUnique.IDX_IDXCMPSTRUNIQ_STRING, columnList = "IDX_STRING", unique = true),
- @Index(name = ResourceIndexedCompositeStringUnique.IDX_IDXCMPSTRUNIQ_RESOURCE, columnList = "RES_ID", unique = false)
+ @Index(name = ResourceIndexedComboStringUnique.IDX_IDXCMPSTRUNIQ_STRING, columnList = "IDX_STRING", unique = true),
+ @Index(name = ResourceIndexedComboStringUnique.IDX_IDXCMPSTRUNIQ_RESOURCE, columnList = "RES_ID", unique = false)
})
-public class ResourceIndexedCompositeStringUnique extends BasePartitionable implements Comparable {
+public class ResourceIndexedComboStringUnique extends BasePartitionable implements Comparable {
- public static final int MAX_STRING_LENGTH = 200;
+ public static final int MAX_STRING_LENGTH = 500;
public static final String IDX_IDXCMPSTRUNIQ_STRING = "IDX_IDXCMPSTRUNIQ_STRING";
public static final String IDX_IDXCMPSTRUNIQ_RESOURCE = "IDX_IDXCMPSTRUNIQ_RESOURCE";
@@ -66,14 +66,14 @@ public class ResourceIndexedCompositeStringUnique extends BasePartitionable impl
/**
* Constructor
*/
- public ResourceIndexedCompositeStringUnique() {
+ public ResourceIndexedComboStringUnique() {
super();
}
/**
* Constructor
*/
- public ResourceIndexedCompositeStringUnique(ResourceTable theResource, String theIndexString, IIdType theSearchParameterId) {
+ public ResourceIndexedComboStringUnique(ResourceTable theResource, String theIndexString, IIdType theSearchParameterId) {
setResource(theResource);
setIndexString(theIndexString);
setPartitionId(theResource.getPartitionId());
@@ -81,7 +81,7 @@ public class ResourceIndexedCompositeStringUnique extends BasePartitionable impl
}
@Override
- public int compareTo(ResourceIndexedCompositeStringUnique theO) {
+ public int compareTo(ResourceIndexedComboStringUnique theO) {
CompareToBuilder b = new CompareToBuilder();
b.append(myIndexString, theO.getIndexString());
return b.toComparison();
@@ -91,11 +91,11 @@ public class ResourceIndexedCompositeStringUnique extends BasePartitionable impl
public boolean equals(Object theO) {
if (this == theO) return true;
- if (!(theO instanceof ResourceIndexedCompositeStringUnique)) {
+ if (!(theO instanceof ResourceIndexedComboStringUnique)) {
return false;
}
- ResourceIndexedCompositeStringUnique that = (ResourceIndexedCompositeStringUnique) theO;
+ ResourceIndexedComboStringUnique that = (ResourceIndexedComboStringUnique) theO;
return new EqualsBuilder()
.append(myIndexString, that.myIndexString)
diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java
new file mode 100644
index 00000000000..0b79e78a2d2
--- /dev/null
+++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java
@@ -0,0 +1,190 @@
+package ca.uhn.fhir.jpa.model.entity;
+
+/*-
+ * #%L
+ * HAPI FHIR JPA Model
+ * %%
+ * 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.interceptor.model.RequestPartitionId;
+import ca.uhn.fhir.jpa.model.config.PartitionSettings;
+import org.apache.commons.lang3.builder.CompareToBuilder;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.ForeignKey;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Index;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.SequenceGenerator;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+
+import static ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam.hash;
+
+@Entity
+@Table(name = "HFJ_IDX_CMB_TOK_NU", indexes = {
+ @Index(name = "IDX_IDXCMBTOKNU_STR", columnList = "IDX_STRING", unique = false),
+ @Index(name = "IDX_IDXCMBTOKNU_RES", columnList = "RES_ID", unique = false)
+})
+public class ResourceIndexedComboTokenNonUnique extends BaseResourceIndex implements Comparable {
+
+ @SequenceGenerator(name = "SEQ_IDXCMBTOKNU_ID", sequenceName = "SEQ_IDXCMBTOKNU_ID")
+ @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_IDXCMBTOKNU_ID")
+ @Id
+ @Column(name = "PID")
+ private Long myId;
+
+ @ManyToOne
+ @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", foreignKey = @ForeignKey(name = "FK_IDXCMBTOKNU_RES_ID"))
+ private ResourceTable myResource;
+
+ @Column(name = "RES_ID", insertable = false, updatable = false)
+ private Long myResourceId;
+
+ @Column(name = "HASH_COMPLETE", nullable = false)
+ private Long myHashComplete;
+
+ @Column(name = "IDX_STRING", nullable = false, length = ResourceIndexedComboStringUnique.MAX_STRING_LENGTH)
+ private String myIndexString;
+
+ @Transient
+ private transient PartitionSettings myPartitionSettings;
+
+ /**
+ * Constructor
+ */
+ public ResourceIndexedComboTokenNonUnique() {
+ super();
+ }
+
+ public ResourceIndexedComboTokenNonUnique(PartitionSettings thePartitionSettings, ResourceTable theEntity, String theQueryString) {
+ myPartitionSettings = thePartitionSettings;
+ myResource = theEntity;
+ myIndexString = theQueryString;
+ }
+
+ public String getIndexString() {
+ return myIndexString;
+ }
+
+ public void setIndexString(String theIndexString) {
+ myIndexString = theIndexString;
+ }
+
+ @Override
+ public boolean equals(Object theO) {
+ if (this == theO) {
+ return true;
+ }
+
+ if (theO == null || getClass() != theO.getClass()) {
+ return false;
+ }
+
+ ResourceIndexedComboTokenNonUnique that = (ResourceIndexedComboTokenNonUnique) theO;
+
+ return new EqualsBuilder()
+ .append(myResource, that.myResource)
+ .append(myHashComplete, that.myHashComplete)
+ .isEquals();
+ }
+
+ @Override
+ public void copyMutableValuesFrom(T theSource) {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public Long getId() {
+ return myId;
+ }
+
+ @Override
+ public void setId(Long theId) {
+ myId = theId;
+ }
+
+ @Override
+ public void calculateHashes() {
+ PartitionSettings partitionSettings = getPartitionSettings();
+ PartitionablePartitionId partitionId = getPartitionId();
+ String queryString = myIndexString;
+ setHashComplete(calculateHashComplete(partitionSettings, partitionId, queryString));
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37)
+ .append(myResource)
+ .append(myHashComplete)
+ .toHashCode();
+ }
+
+ public PartitionSettings getPartitionSettings() {
+ return myPartitionSettings;
+ }
+
+ public ResourceTable getResource() {
+ return myResource;
+ }
+
+ public void setResource(ResourceTable theResource) {
+ myResource = theResource;
+ }
+
+ public Long getHashComplete() {
+ return myHashComplete;
+ }
+
+ public void setHashComplete(Long theHashComplete) {
+ myHashComplete = theHashComplete;
+ }
+
+ @Override
+ public int compareTo(ResourceIndexedComboTokenNonUnique theO) {
+ CompareToBuilder b = new CompareToBuilder();
+ b.append(myHashComplete, theO.getHashComplete());
+ return b.toComparison();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", myId)
+ .append("resourceId", myResourceId)
+ .append("hashComplete", myHashComplete)
+ .append("indexString", myIndexString)
+ .toString();
+ }
+
+ public static long calculateHashComplete(PartitionSettings partitionSettings, PartitionablePartitionId thePartitionId, String queryString) {
+ RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(thePartitionId);
+ return hash(partitionSettings, requestPartitionId, queryString);
+ }
+
+ public static long calculateHashComplete(PartitionSettings partitionSettings, RequestPartitionId partitionId, String queryString) {
+ return hash(partitionSettings, partitionId, queryString);
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java
index 345f282a0db..171694e4623 100644
--- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java
+++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java
@@ -192,11 +192,20 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
// Added in 3.0.0 - Should make this a primitive Boolean at some point
@OptimisticLock(excluded = true)
@Column(name = "SP_CMPSTR_UNIQ_PRESENT")
- private Boolean myParamsCompositeStringUniquePresent = false;
+ private Boolean myParamsComboStringUniquePresent = false;
@OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
@OptimisticLock(excluded = true)
- private Collection myParamsCompositeStringUnique;
+ private Collection myParamsComboStringUnique;
+
+ // Added in 5.5.0 - Should make this a primitive Boolean at some point
+ @OptimisticLock(excluded = true)
+ @Column(name = "SP_CMPTOKS_PRESENT")
+ private Boolean myParamsComboTokensNonUniquePresent = false;
+
+ @OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
+ @OptimisticLock(excluded = true)
+ private Collection myParamsComboTokensNonUnique;
@OneToMany(mappedBy = "mySourceResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
@OptimisticLock(excluded = true)
@@ -312,11 +321,18 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
myLanguage = theLanguage;
}
- public Collection getParamsCompositeStringUnique() {
- if (myParamsCompositeStringUnique == null) {
- myParamsCompositeStringUnique = new ArrayList<>();
+ public Collection getParamsComboStringUnique() {
+ if (myParamsComboStringUnique == null) {
+ myParamsComboStringUnique = new ArrayList<>();
}
- return myParamsCompositeStringUnique;
+ return myParamsComboStringUnique;
+ }
+
+ public Collection getmyParamsComboTokensNonUnique() {
+ if (myParamsComboTokensNonUnique == null) {
+ myParamsComboTokensNonUnique = new ArrayList<>();
+ }
+ return myParamsComboTokensNonUnique;
}
public Collection getParamsCoords() {
@@ -494,15 +510,26 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
myHasLinks = theHasLinks;
}
- public boolean isParamsCompositeStringUniquePresent() {
- if (myParamsCompositeStringUniquePresent == null) {
+ public boolean isParamsComboStringUniquePresent() {
+ if (myParamsComboStringUniquePresent == null) {
return false;
}
- return myParamsCompositeStringUniquePresent;
+ return myParamsComboStringUniquePresent;
}
- public void setParamsCompositeStringUniquePresent(boolean theParamsCompositeStringUniquePresent) {
- myParamsCompositeStringUniquePresent = theParamsCompositeStringUniquePresent;
+ public void setParamsComboStringUniquePresent(boolean theParamsComboStringUniquePresent) {
+ myParamsComboStringUniquePresent = theParamsComboStringUniquePresent;
+ }
+
+ public boolean isParamsComboTokensNonUniquePresent() {
+ if (myParamsComboTokensNonUniquePresent == null) {
+ return false;
+ }
+ return myParamsComboTokensNonUniquePresent;
+ }
+
+ public void setParamsComboTokensNonUniquePresent(boolean theParamsComboTokensNonUniquePresent) {
+ myParamsComboStringUniquePresent = theParamsComboTokensNonUniquePresent;
}
public boolean isParamsCoordsPopulated() {
diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml
index ea5ca903fd9..1eb524292eb 100755
--- a/hapi-fhir-jpaserver-searchparam/pom.xml
+++ b/hapi-fhir-jpaserver-searchparam/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
hapi-deployable-pom
- 5.5.0-PRE6-SNAPSHOT
+ 5.5.0-PRE7-SNAPSHOT
../hapi-deployable-pom/pom.xml
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 11cbd07882b..0a1283f8027 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
@@ -93,7 +93,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
public static final Set COORDS_INDEX_PATHS;
private static final Pattern SPLIT = Pattern.compile("\\||( or )");
- private static final Pattern SPLIT_R4 = Pattern.compile("\\|");
+ private static final Pattern SPLIT_R4 = Pattern.compile("\\s+\\|");
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseSearchParamExtractor.class);
static {
@@ -534,8 +534,6 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
return values;
}
- protected abstract IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath);
-
protected FhirContext getContext() {
return myContext;
}
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 2871503d11e..fa79bb90f4c 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
@@ -32,6 +32,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.r4.utils.FHIRPathEngine;
import java.util.ArrayList;
import java.util.Collections;
@@ -81,6 +82,8 @@ public interface ISearchParamExtractor {
String getDisplayTextForCoding(IBase theValue);
+ BaseSearchParamExtractor.IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath);
+
List getCodingsFromCodeableConcept(IBase theValue);
String getDisplayTextFromCodeableConcept(IBase theValue);
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 bfaaa306b59..9416e13683b 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
@@ -26,7 +26,8 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
-import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
+import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique;
+import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboTokenNonUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
@@ -70,7 +71,8 @@ public final class ResourceIndexedSearchParams {
final public Collection myUriParams = new ArrayList<>();
final public Collection myCoordsParams = new ArrayList<>();
- final public Collection myCompositeStringUniques = new HashSet<>();
+ final public Collection myComboStringUniques = new HashSet<>();
+ final public Collection myComboTokenNonUnique = new HashSet<>();
final public Collection myLinks = new HashSet<>();
final public Set myPopulatedResourceLinkParameters = new HashSet<>();
@@ -106,8 +108,11 @@ public final class ResourceIndexedSearchParams {
myLinks.addAll(theEntity.getResourceLinks());
}
- if (theEntity.isParamsCompositeStringUniquePresent()) {
- myCompositeStringUniques.addAll(theEntity.getParamsCompositeStringUnique());
+ if (theEntity.isParamsComboStringUniquePresent()) {
+ myComboStringUniques.addAll(theEntity.getParamsComboStringUnique());
+ }
+ if (theEntity.isParamsComboTokensNonUniquePresent()) {
+ myComboTokenNonUnique.addAll(theEntity.getmyParamsComboTokensNonUnique());
}
}
@@ -125,7 +130,7 @@ public final class ResourceIndexedSearchParams {
theEntity.setParamsDatePopulated(myDateParams.isEmpty() == false);
theEntity.setParamsUriPopulated(myUriParams.isEmpty() == false);
theEntity.setParamsCoordsPopulated(myCoordsParams.isEmpty() == false);
- theEntity.setParamsCompositeStringUniquePresent(myCompositeStringUniques.isEmpty() == false);
+ theEntity.setParamsComboStringUniquePresent(myComboStringUniques.isEmpty() == false);
theEntity.setHasLinks(myLinks.isEmpty() == false);
}
@@ -305,7 +310,8 @@ public final class ResourceIndexedSearchParams {
", dateParams=" + myDateParams +
", uriParams=" + myUriParams +
", coordsParams=" + myCoordsParams +
- ", compositeStringUniques=" + myCompositeStringUniques +
+ ", comboStringUniques=" + myComboStringUniques +
+ ", comboTokenNonUniques=" + myComboTokenNonUnique +
", links=" + myLinks +
'}';
}
@@ -431,6 +437,9 @@ public final class ResourceIndexedSearchParams {
List values = new ArrayList<>();
Set queryStringsToPopulate = new HashSet<>();
extractCompositeStringUniquesValueChains(theResourceType, thePartsChoices, values, queryStringsToPopulate);
+
+ values.removeIf(StringUtils::isBlank);
+
return queryStringsToPopulate;
}
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu2.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu2.java
index 798a35b3f52..a50e8d61822 100644
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu2.java
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu2.java
@@ -48,7 +48,7 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
}
@Override
- protected IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath) {
+ public IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath) {
return () -> {
String path = theSinglePath;
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java
index f701a081ad6..75f51484ae8 100644
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java
@@ -56,7 +56,7 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
}
@Override
- protected IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath) {
+ public IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath) {
return () -> {
List values = new ArrayList<>();
List allValues = myFhirPathEngine.evaluate((Base) theResource, theSinglePath);
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java
index c5289ad152f..beb49d8e6a8 100644
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java
@@ -68,7 +68,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
}
@Override
- protected IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath) {
+ public IValueExtractor getPathValueExtractor(IBaseResource theResource, String theSinglePath) {
return () -> {
List allValues = myFhirPathEngine.evaluate((Base) theResource, theSinglePath);
return (List) new ArrayList(allValues);
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java
index 62498092baf..d18a4d11d40 100644
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java
@@ -78,7 +78,7 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
}
@Override
- protected IValueExtractor getPathValueExtractor(IBaseResource theResource, String nextPath) {
+ public IValueExtractor getPathValueExtractor(IBaseResource theResource, String nextPath) {
return () -> myFhirPathEngine.evaluate((Base) theResource, nextPath);
}
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/JpaSearchParamCache.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/JpaSearchParamCache.java
index e529bdcf982..92f26d11e25 100644
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/JpaSearchParamCache.java
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/JpaSearchParamCache.java
@@ -40,26 +40,25 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
-import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class JpaSearchParamCache {
private static final Logger ourLog = LoggerFactory.getLogger(JpaSearchParamCache.class);
- private volatile Map> myActiveUniqueSearchParams = Collections.emptyMap();
- private volatile Map, List>> myActiveParamNamesToUniqueSearchParams = Collections.emptyMap();
+ private volatile Map> myActiveComboSearchParams = Collections.emptyMap();
+ private volatile Map, List>> myActiveParamNamesToComboSearchParams = Collections.emptyMap();
- public List getActiveUniqueSearchParams(String theResourceName) {
- List retval = myActiveUniqueSearchParams.get(theResourceName);
+ public List getActiveComboSearchParams(String theResourceName) {
+ List retval = myActiveComboSearchParams.get(theResourceName);
if (retval == null) {
retval = Collections.emptyList();
}
return retval;
}
- public List getActiveUniqueSearchParams(String theResourceName, Set theParamNames) {
- Map, List> paramNamesToParams = myActiveParamNamesToUniqueSearchParams.get(theResourceName);
+ public List getActiveComboSearchParams(String theResourceName, Set theParamNames) {
+ Map, List> paramNamesToParams = myActiveParamNamesToComboSearchParams.get(theResourceName);
if (paramNamesToParams == null) {
return Collections.emptyList();
}
@@ -72,8 +71,8 @@ public class JpaSearchParamCache {
}
void populateActiveSearchParams(IInterceptorService theInterceptorBroadcaster, IPhoneticEncoder theDefaultPhoneticEncoder, RuntimeSearchParamCache theActiveSearchParams) {
- Map> activeUniqueSearchParams = new HashMap<>();
- Map, List>> activeParamNamesToUniqueSearchParams = new HashMap<>();
+ Map> resourceNameToComboSearchParams = new HashMap<>();
+ Map, List>> activeParamNamesToComboSearchParams = new HashMap<>();
Map idToRuntimeSearchParam = new HashMap<>();
List jpaSearchParams = new ArrayList<>();
@@ -83,7 +82,7 @@ public class JpaSearchParamCache {
*/
for (String theResourceName : theActiveSearchParams.getResourceNameKeys()) {
Map searchParamMap = theActiveSearchParams.getSearchParamMap(theResourceName);
- List uniqueSearchParams = activeUniqueSearchParams.computeIfAbsent(theResourceName, k -> new ArrayList<>());
+ List comboSearchParams = resourceNameToComboSearchParams.computeIfAbsent(theResourceName, k -> new ArrayList<>());
Collection nextSearchParamsForResourceName = searchParamMap.values();
ourLog.trace("Resource {} has {} params", theResourceName, searchParamMap.size());
@@ -99,10 +98,9 @@ public class JpaSearchParamCache {
idToRuntimeSearchParam.put(nextCandidate.getUri(), nextCandidate);
}
- RuntimeSearchParam nextCandidateCasted = nextCandidate;
- jpaSearchParams.add(nextCandidateCasted);
- if (nextCandidateCasted.isUnique()) {
- uniqueSearchParams.add(nextCandidateCasted);
+ jpaSearchParams.add(nextCandidate);
+ if (nextCandidate.getComboSearchParamType() != null) {
+ comboSearchParams.add(nextCandidate);
}
setPhoneticEncoder(theDefaultPhoneticEncoder, nextCandidate);
@@ -137,19 +135,19 @@ public class JpaSearchParamCache {
}
}
- if (next.isUnique()) {
+ if (next.getComboSearchParamType() != null) {
for (String nextBase : next.getBase()) {
- activeParamNamesToUniqueSearchParams.computeIfAbsent(nextBase, v -> new HashMap<>());
- activeParamNamesToUniqueSearchParams.get(nextBase).computeIfAbsent(paramNames, t -> new ArrayList<>());
- activeParamNamesToUniqueSearchParams.get(nextBase).get(paramNames).add(next);
+ activeParamNamesToComboSearchParams.computeIfAbsent(nextBase, v -> new HashMap<>());
+ activeParamNamesToComboSearchParams.get(nextBase).computeIfAbsent(paramNames, t -> new ArrayList<>());
+ activeParamNamesToComboSearchParams.get(nextBase).get(paramNames).add(next);
}
}
}
- ourLog.info("Have {} unique search params", activeParamNamesToUniqueSearchParams.size());
+ ourLog.info("Have {} unique search params", activeParamNamesToComboSearchParams.size());
- myActiveUniqueSearchParams = activeUniqueSearchParams;
- myActiveParamNamesToUniqueSearchParams = activeParamNamesToUniqueSearchParams;
+ myActiveComboSearchParams = resourceNameToComboSearchParams;
+ myActiveParamNamesToComboSearchParams = activeParamNamesToComboSearchParams;
}
void setPhoneticEncoder(IPhoneticEncoder theDefaultPhoneticEncoder, RuntimeSearchParam searchParam) {
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java
index ab80d0784df..d465608e66c 100644
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java
@@ -109,13 +109,13 @@ public class SearchParamRegistryImpl implements ISearchParamRegistry, IResourceC
}
@Override
- public List getActiveUniqueSearchParams(String theResourceName) {
- return myJpaSearchParamCache.getActiveUniqueSearchParams(theResourceName);
+ public List getActiveComboSearchParams(String theResourceName) {
+ return myJpaSearchParamCache.getActiveComboSearchParams(theResourceName);
}
@Override
- public List getActiveUniqueSearchParams(String theResourceName, Set theParamNames) {
- return myJpaSearchParamCache.getActiveUniqueSearchParams(theResourceName, theParamNames);
+ public List getActiveComboSearchParams(String theResourceName, Set theParamNames) {
+ return myJpaSearchParamCache.getActiveComboSearchParams(theResourceName, theParamNames);
}
@Nullable
diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java
index f9fe0532244..da2b3b9a587 100644
--- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java
+++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java
@@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.searchparam.registry;
* #L%
*/
+import ca.uhn.fhir.context.ComboSearchParamType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.phonetic.PhoneticEncoderEnum;
@@ -148,14 +149,16 @@ public class SearchParameterCanonicalizer {
IIdType id = theNextSp.getIdElement();
String uri = "";
- boolean unique = false;
+ ComboSearchParamType unique = null;
List uniqueExts = theNextSp.getUndeclaredExtensionsByUrl(HapiExtensions.EXT_SP_UNIQUE);
if (uniqueExts.size() > 0) {
IPrimitiveType> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
if (uniqueExtsValuePrimitive != null) {
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
- unique = true;
+ unique = ComboSearchParamType.UNIQUE;
+ } else if ("false".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
+ unique = ComboSearchParamType.NON_UNIQUE;
}
}
}
@@ -228,14 +231,16 @@ public class SearchParameterCanonicalizer {
IIdType id = theNextSp.getIdElement();
String uri = "";
- boolean unique = false;
+ ComboSearchParamType unique = null;
List uniqueExts = theNextSp.getExtensionsByUrl(HapiExtensions.EXT_SP_UNIQUE);
if (uniqueExts.size() > 0) {
IPrimitiveType> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
if (uniqueExtsValuePrimitive != null) {
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
- unique = true;
+ unique = ComboSearchParamType.UNIQUE;
+ } else if ("false".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
+ unique = ComboSearchParamType.NON_UNIQUE;
}
}
}
@@ -311,7 +316,7 @@ public class SearchParameterCanonicalizer {
IIdType id = theNextSp.getIdElement();
String uri = terser.getSinglePrimitiveValueOrNull(theNextSp, "url");
- boolean unique = false;
+ ComboSearchParamType unique = null;
String value = ((IBaseHasExtensions) theNextSp).getExtension()
.stream()
@@ -322,7 +327,9 @@ public class SearchParameterCanonicalizer {
.findFirst()
.orElse("");
if ("true".equalsIgnoreCase(value)) {
- unique = true;
+ unique = ComboSearchParamType.UNIQUE;
+ } else if ("false".equalsIgnoreCase(value)) {
+ unique = ComboSearchParamType.NON_UNIQUE;
}
List components = new ArrayList<>();
diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3Test.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3Test.java
index d1f1153d1f1..d3c18e9414f 100644
--- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3Test.java
+++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3Test.java
@@ -132,11 +132,11 @@ public class SearchParamExtractorDstu3Test {
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, searchParamRegistry);
extractor.start();
- searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "", RestSearchParameterTypeEnum.STRING, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, false, null, null));
+ searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "", RestSearchParameterTypeEnum.STRING, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, null, null, null));
Patient resource = new Patient();
extractor.extractSearchParamStrings(resource);
- searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", null, RestSearchParameterTypeEnum.STRING, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, false, null, null));
+ searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", null, RestSearchParameterTypeEnum.STRING, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, null, null, null));
extractor.extractSearchParamStrings(resource);
}
@@ -148,7 +148,7 @@ public class SearchParamExtractorDstu3Test {
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, searchParamRegistry);
extractor.start();
- searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "communication.language.coding.system | communication.language.coding.code", RestSearchParameterTypeEnum.STRING, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, false, null, null));
+ searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "communication.language.coding.system | communication.language.coding.code", RestSearchParameterTypeEnum.STRING, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, null, null, null));
Patient resource = new Patient();
resource.getCommunicationFirstRep().getLanguage().getCodingFirstRep().setCode("blah");
Set strings = extractor.extractSearchParamStrings(resource);
@@ -166,37 +166,37 @@ public class SearchParamExtractorDstu3Test {
extractor.start();
{
- searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "Patient", RestSearchParameterTypeEnum.STRING, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, false, null, null));
+ searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "Patient", RestSearchParameterTypeEnum.STRING, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, null, null, null));
Patient resource = new Patient();
ISearchParamExtractor.SearchParamSet outcome = extractor.extractSearchParamStrings(resource);
assertThat(outcome.getWarnings(), Matchers.contains("Search param foo is of unexpected datatype: class org.hl7.fhir.dstu3.model.Patient"));
}
{
- searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "Patient", RestSearchParameterTypeEnum.TOKEN, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, false, null, null));
+ searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "Patient", RestSearchParameterTypeEnum.TOKEN, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, null, null, null));
Patient resource = new Patient();
ISearchParamExtractor.SearchParamSet outcome = extractor.extractSearchParamTokens(resource);
assertThat(outcome.getWarnings(), Matchers.contains("Search param foo is of unexpected datatype: class org.hl7.fhir.dstu3.model.Patient"));
}
{
- searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "Patient", RestSearchParameterTypeEnum.QUANTITY, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, false, null, null));
+ searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "Patient", RestSearchParameterTypeEnum.QUANTITY, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, null, null, null));
Patient resource = new Patient();
ISearchParamExtractor.SearchParamSet outcome = extractor.extractSearchParamQuantity(resource);
assertThat(outcome.getWarnings(), Matchers.contains("Search param foo is of unexpected datatype: class org.hl7.fhir.dstu3.model.Patient"));
}
{
- searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "Patient", RestSearchParameterTypeEnum.DATE, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, false, null, null));
+ searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "Patient", RestSearchParameterTypeEnum.DATE, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, null, null, null));
Patient resource = new Patient();
ISearchParamExtractor.SearchParamSet outcome = extractor.extractSearchParamDates(resource);
assertThat(outcome.getWarnings(), Matchers.contains("Search param foo is of unexpected datatype: class org.hl7.fhir.dstu3.model.Patient"));
}
{
- searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "Patient", RestSearchParameterTypeEnum.NUMBER, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, false, null, null));
+ searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "Patient", RestSearchParameterTypeEnum.NUMBER, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, null, null, null));
Patient resource = new Patient();
ISearchParamExtractor.SearchParamSet outcome = extractor.extractSearchParamNumber(resource);
assertThat(outcome.getWarnings(), Matchers.contains("Search param foo is of unexpected datatype: class org.hl7.fhir.dstu3.model.Patient"));
}
{
- searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "Patient", RestSearchParameterTypeEnum.URI, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, false, null, null));
+ searchParamRegistry.addSearchParam(new RuntimeSearchParam(null, null, "foo", "foo", "Patient", RestSearchParameterTypeEnum.URI, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, null, null, null));
Patient resource = new Patient();
ISearchParamExtractor.SearchParamSet