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 join = myQueryStack.createJoin(SearchBuilderJoinEnum.COMPOSITE_UNIQUE, null); + From 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 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 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 findParameterIndexes(ResourceIndexedSearchParams theParams, RuntimeSearchParam nextCompositeOf) { + Collection 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 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 outcome = extractor.extractSearchParamUri(resource); assertThat(outcome.getWarnings(), Matchers.contains("Search param foo is of unexpected datatype: class org.hl7.fhir.dstu3.model.Patient")); @@ -269,7 +269,7 @@ public class SearchParamExtractorDstu3Test { } @Override - public List getActiveUniqueSearchParams(String theResourceName, Set theParamNames) { + public List getActiveComboSearchParams(String theResourceName, Set theParamNames) { throw new UnsupportedOperationException(); } @@ -280,7 +280,7 @@ public class SearchParamExtractorDstu3Test { } @Override - public List getActiveUniqueSearchParams(String theResourceName) { + public List getActiveComboSearchParams(String theResourceName) { throw new UnsupportedOperationException(); } diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorMegaTest.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorMegaTest.java index e3a24cc540b..572951945d3 100644 --- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorMegaTest.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorMegaTest.java @@ -282,7 +282,7 @@ public class SearchParamExtractorMegaTest { } @Override - public List getActiveUniqueSearchParams(String theResourceName, Set theParamNames) { + public List getActiveComboSearchParams(String theResourceName, Set theParamNames) { throw new UnsupportedOperationException(); } @@ -293,7 +293,7 @@ public class SearchParamExtractorMegaTest { } @Override - public List getActiveUniqueSearchParams(String theResourceName) { + public List getActiveComboSearchParams(String theResourceName) { throw new UnsupportedOperationException(); } diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java index cfd37c44f68..056c512bbbc 100644 --- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java @@ -59,13 +59,13 @@ public class InMemoryResourceMatcherR5Test { @BeforeEach public void before() { - RuntimeSearchParam dateSearchParam = new RuntimeSearchParam(null, null, null, null, "Observation.effective", RestSearchParameterTypeEnum.DATE, null, null, RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, false, null, null); + RuntimeSearchParam dateSearchParam = new RuntimeSearchParam(null, null, null, null, "Observation.effective", RestSearchParameterTypeEnum.DATE, null, null, RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, null, null, null); when(mySearchParamRegistry.getActiveSearchParam("Observation", "date")).thenReturn(dateSearchParam); - RuntimeSearchParam codeSearchParam = new RuntimeSearchParam(null, null, null, null, "Observation.code", RestSearchParameterTypeEnum.TOKEN, null, null, RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, false, null, null); + RuntimeSearchParam codeSearchParam = new RuntimeSearchParam(null, null, null, null, "Observation.code", RestSearchParameterTypeEnum.TOKEN, null, null, RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, null, null, null); when(mySearchParamRegistry.getActiveSearchParam("Observation", "code")).thenReturn(codeSearchParam); - RuntimeSearchParam encSearchParam = new RuntimeSearchParam(null, null, null, null, "Observation.encounter", RestSearchParameterTypeEnum.REFERENCE, null, null, RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, false, null, null); + RuntimeSearchParam encSearchParam = new RuntimeSearchParam(null, null, null, null, "Observation.encounter", RestSearchParameterTypeEnum.REFERENCE, null, null, RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, null, null, null); when(mySearchParamRegistry.getActiveSearchParam("Observation", "encounter")).thenReturn(encSearchParam); myObservation = new Observation(); diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImplTest.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImplTest.java index 53cbc763578..7c02e9b71db 100644 --- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImplTest.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImplTest.java @@ -293,7 +293,7 @@ public class SearchParamRegistryImplTest { @Test public void testGetActiveUniqueSearchParams_Empty() { - assertThat(mySearchParamRegistry.getActiveUniqueSearchParams("Patient"), is(empty())); + assertThat(mySearchParamRegistry.getActiveComboSearchParams("Patient"), is(empty())); } @Test diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index dadf2bd854c..26a70d3f837 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/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-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index 35f517defdd..a0a3dddba47 100644 --- a/hapi-fhir-jpaserver-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-test-utilities/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-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 80f352f1735..eafc3940eb2 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/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-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index 687f2cabe02..cf82e372485 100644 --- a/hapi-fhir-server-mdm/pom.xml +++ b/hapi-fhir-server-mdm/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-server-mdm/src/test/java/ca/uhn/fhir/mdm/svc/EIDHelperR4Test.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/svc/EIDHelperR4Test.java index 7f7a17bfe78..4c2ae800bed 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/svc/EIDHelperR4Test.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/svc/EIDHelperR4Test.java @@ -45,7 +45,7 @@ public class EIDHelperR4Test extends BaseR4Test { @BeforeEach public void before() { when(mySearchParamRetriever.getActiveSearchParam("Patient", "identifier")) - .thenReturn(new RuntimeSearchParam(null, null, "identifier", "Description", "identifier", RestSearchParameterTypeEnum.STRING, new HashSet<>(), new HashSet<>(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, false, null, null)); + .thenReturn(new RuntimeSearchParam(null, null, "identifier", "Description", "identifier", RestSearchParameterTypeEnum.STRING, new HashSet<>(), new HashSet<>(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, null, null, null)); myMdmSettings = new MdmSettings(new MdmRuleValidator(ourFhirContext, mySearchParamRetriever)) { { diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index 4e7425aabb1..92fc3dd6623 100644 --- a/hapi-fhir-server-openapi/pom.xml +++ b/hapi-fhir-server-openapi/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-server/pom.xml b/hapi-fhir-server/pom.xml index aeba9dd69b6..f85703de5b2 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/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-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 9df9b113306..71381470fa8 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -124,7 +124,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServert.getMethodBindings().stream()) - .forEach(t->t.close()); + .flatMap(t -> t.getMethodBindings().stream()) + .forEach(t -> t.close()); myGlobalBinding .getMethodBindings() - .forEach(t->t.close()); + .forEach(t -> t.close()); myServerBinding .getMethodBindings() - .forEach(t->t.close()); + .forEach(t -> t.close()); } @@ -2033,7 +2039,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer targets = Collections.emptySet(); RuntimeSearchParam.RuntimeSearchParamStatusEnum status = RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE; Collection base = Collections.singletonList(theSearchMethodBinding.getResourceName()); - RuntimeSearchParam param = new RuntimeSearchParam(id, uri, nextParamName, description, path, type, providesMembershipInCompartments, targets, status, false, null, base); + RuntimeSearchParam param = new RuntimeSearchParam(id, uri, nextParamName, description, path, type, providesMembershipInCompartments, targets, status, null, null, base); theMapToPopulate.put(nextParamName, param); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBinding.java index 1930d176f5b..27bb38ca89e 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBinding.java @@ -110,7 +110,7 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding * @see Metadata#cacheMillis() * @since 4.1.0 */ - private void setCacheMillis(long theCacheMillis) { + public void setCacheMillis(long theCacheMillis) { myCacheMillis = theCacheMillis; } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ISearchParamRegistry.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ISearchParamRegistry.java index b78beffb187..fe8d849d097 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ISearchParamRegistry.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ISearchParamRegistry.java @@ -70,11 +70,11 @@ public interface ISearchParamRegistry { default void setPhoneticEncoder(IPhoneticEncoder thePhoneticEncoder) { } - default List getActiveUniqueSearchParams(String theResourceName) { + default List getActiveComboSearchParams(String theResourceName) { return Collections.emptyList(); } - default List getActiveUniqueSearchParams(String theResourceName, Set theParamNames) { + default List getActiveComboSearchParams(String theResourceName, Set theParamNames) { return Collections.emptyList(); } diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index e020964d26b..b36ce8feaca 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/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-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml index 2b83cf773ab..704dbbf5fc7 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT hapi-fhir-spring-boot-sample-client-apache diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml index 8e831918720..8cc8cc8cab5 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT hapi-fhir-spring-boot-sample-client-okhttp diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml index 1aa380f6db1..18115eb2b87 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT hapi-fhir-spring-boot-sample-server-jersey diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml index ee96476f745..f3beb03fa68 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT hapi-fhir-spring-boot-samples diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml index 70db9f63833..a9f45157a3b 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/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-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 5f539d55ba5..4473e7e4c6d 100644 --- a/hapi-fhir-spring-boot/pom.xml +++ b/hapi-fhir-spring-boot/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-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index 2a9755f1e3e..fb9d06a31df 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/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-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index 89772c9c5d2..d0301a3b43c 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/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-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 0d20d066c62..e38052d8518 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/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-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index cd00acad071..efd6c7a915d 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/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-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index c7d11fae560..c89f53634fd 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/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-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 2c0fd98a493..4f8d961d1c2 100644 --- a/hapi-fhir-structures-r5/pom.xml +++ b/hapi-fhir-structures-r5/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-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 157e5ae09cf..f713ecc36f7 100644 --- a/hapi-fhir-test-utilities/pom.xml +++ b/hapi-fhir-test-utilities/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-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index e3037a91c04..a36dfe3bfbd 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/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-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 631c5e84ac6..919393e2a02 100644 --- a/hapi-fhir-validation-resources-dstu2.1/pom.xml +++ b/hapi-fhir-validation-resources-dstu2.1/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-validation-resources-dstu2/pom.xml b/hapi-fhir-validation-resources-dstu2/pom.xml index 807b881d5a6..d1b6baee0bf 100644 --- a/hapi-fhir-validation-resources-dstu2/pom.xml +++ b/hapi-fhir-validation-resources-dstu2/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-validation-resources-dstu3/pom.xml b/hapi-fhir-validation-resources-dstu3/pom.xml index a933f20d9b1..0d1669f88d7 100644 --- a/hapi-fhir-validation-resources-dstu3/pom.xml +++ b/hapi-fhir-validation-resources-dstu3/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-validation-resources-r4/pom.xml b/hapi-fhir-validation-resources-r4/pom.xml index aa4e30aac23..588091ec831 100644 --- a/hapi-fhir-validation-resources-r4/pom.xml +++ b/hapi-fhir-validation-resources-r4/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-validation-resources-r5/pom.xml b/hapi-fhir-validation-resources-r5/pom.xml index 2ac45e333fa..63a22dbf8f7 100644 --- a/hapi-fhir-validation-resources-r5/pom.xml +++ b/hapi-fhir-validation-resources-r5/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-validation/pom.xml b/hapi-fhir-validation/pom.xml index 5601f452255..94bfad3c883 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/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-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index b59764872f4..fdc720e7155 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT ../pom.xml @@ -58,37 +58,37 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r5 - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT org.apache.velocity diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 433091d2fcc..8b526787b72 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/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/pom.xml b/pom.xml index 6359f2b9f31..f3d3064e7ff 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. https://hapifhir.io diff --git a/restful-server-example/pom.xml b/restful-server-example/pom.xml index 0542b16a584..801090855cd 100644 --- a/restful-server-example/pom.xml +++ b/restful-server-example/pom.xml @@ -8,7 +8,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT ../pom.xml diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index b683cec8202..7151713a632 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/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/tests/hapi-fhir-base-test-mindeps-client/pom.xml b/tests/hapi-fhir-base-test-mindeps-client/pom.xml index b0d088507d0..3613124778d 100644 --- a/tests/hapi-fhir-base-test-mindeps-client/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-client/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/tests/hapi-fhir-base-test-mindeps-server/pom.xml b/tests/hapi-fhir-base-test-mindeps-server/pom.xml index bbfc4708d61..be1470a93b0 100644 --- a/tests/hapi-fhir-base-test-mindeps-server/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE6-SNAPSHOT + 5.5.0-PRE7-SNAPSHOT ../../pom.xml