Fix changelog syntax. Merge current master.

This commit is contained in:
juan.marchionatto 2021-07-22 10:49:16 -04:00
commit ad6f7a204c
153 changed files with 2291 additions and 466 deletions

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -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
}

View File

@ -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);
}

View File

@ -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<String, List<IBaseExtension<?, ?>>> myExtensions = new HashMap<>();
private final boolean myUnique;
private final ComboSearchParamType myComboSearchParamType;
private final List<Component> 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<String> theProvidesMembershipInCompartments, Set<String> theTargets, RuntimeSearchParamStatusEnum theStatus, Collection<String> 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<String> theProvidesMembershipInCompartments, Set<String> theTargets, RuntimeSearchParamStatusEnum theStatus, boolean theUnique, List<Component> theComponents, Collection<String> theBase) {
public RuntimeSearchParam(IIdType theId, String theUri, String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, Set<String> theProvidesMembershipInCompartments, Set<String> theTargets, RuntimeSearchParamStatusEnum theStatus, ComboSearchParamType theComboSearchParamType, List<Component> theComponents, Collection<String> 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 <code>null</code> if this is not a combo search param type
*/
@Nullable
public ComboSearchParamType getComboSearchParamType() {
return myComboSearchParamType;
}
/**

View File

@ -3,14 +3,14 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-bom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<packaging>pom</packaging>
<name>HAPI FHIR BOM</name>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -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"
}
]
}

View File

@ -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."

View File

@ -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."

View File

@ -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."

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 2808
title: "Loading packages would fail when partitioning was enabled with unnamed partitions. This has been fixed."

View File

@ -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."

View File

@ -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.

View File

@ -11,7 +11,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -85,7 +85,7 @@ public class BulkDataExportProvider {
@OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theOutputFormat,
@OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theType,
@OperationParam(name = JpaConstants.PARAM_EXPORT_SINCE, min = 0, max = 1, typeName = "instant") IPrimitiveType<Date> theSince,
@OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE_FILTER, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theTypeFilter,
@OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") List<IPrimitiveType<String>> 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<String> theOutputFormat,
@OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theType,
@OperationParam(name = JpaConstants.PARAM_EXPORT_SINCE, min = 0, max = 1, typeName = "instant") IPrimitiveType<Date> theSince,
@OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE_FILTER, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theTypeFilter,
@OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") List<IPrimitiveType<String>> theTypeFilter,
@OperationParam(name = JpaConstants.PARAM_EXPORT_MDM, min = 0, max = 1, typeName = "boolean") IPrimitiveType<Boolean> theMdm,
ServletRequestDetails theRequestDetails
) {
@ -151,7 +151,7 @@ public class BulkDataExportProvider {
@OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theOutputFormat,
@OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theType,
@OperationParam(name = JpaConstants.PARAM_EXPORT_SINCE, min = 0, max = 1, typeName = "instant") IPrimitiveType<Date> theSince,
@OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE_FILTER, min = 0, max = 1, typeName = "string") IPrimitiveType<String> theTypeFilter,
@OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE_FILTER, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") List<IPrimitiveType<String>> theTypeFilter,
ServletRequestDetails theRequestDetails
) {
validatePreferAsyncHeader(theRequestDetails);
@ -218,11 +218,11 @@ public class BulkDataExportProvider {
}
}
private BulkDataExportOptions buildSystemBulkExportOptions(IPrimitiveType<String> theOutputFormat, IPrimitiveType<String> theType, IPrimitiveType<Date> theSince, IPrimitiveType<String> theTypeFilter) {
private BulkDataExportOptions buildSystemBulkExportOptions(IPrimitiveType<String> theOutputFormat, IPrimitiveType<String> theType, IPrimitiveType<Date> theSince, List<IPrimitiveType<String>> theTypeFilter) {
return buildBulkDataExportOptions(theOutputFormat, theType, theSince, theTypeFilter, BulkDataExportOptions.ExportStyle.SYSTEM);
}
private BulkDataExportOptions buildGroupBulkExportOptions(IPrimitiveType<String> theOutputFormat, IPrimitiveType<String> theType, IPrimitiveType<Date> theSince, IPrimitiveType<String> theTypeFilter, IIdType theGroupId, IPrimitiveType<Boolean> theExpandMdm) {
private BulkDataExportOptions buildGroupBulkExportOptions(IPrimitiveType<String> theOutputFormat, IPrimitiveType<String> theType, IPrimitiveType<Date> theSince, List<IPrimitiveType<String>> theTypeFilter, IIdType theGroupId, IPrimitiveType<Boolean> 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<String> theOutputFormat, IPrimitiveType<String> theType, IPrimitiveType<Date> theSince, IPrimitiveType<String> theTypeFilter) {
private BulkDataExportOptions buildPatientBulkExportOptions(IPrimitiveType<String> theOutputFormat, IPrimitiveType<String> theType, IPrimitiveType<Date> theSince, List<IPrimitiveType<String>> theTypeFilter) {
return buildBulkDataExportOptions(theOutputFormat, theType, theSince, theTypeFilter, BulkDataExportOptions.ExportStyle.PATIENT);
}
private BulkDataExportOptions buildBulkDataExportOptions(IPrimitiveType<String> theOutputFormat, IPrimitiveType<String> theType, IPrimitiveType<Date> theSince, IPrimitiveType<String> theTypeFilter, BulkDataExportOptions.ExportStyle theExportStyle) {
private BulkDataExportOptions buildBulkDataExportOptions(IPrimitiveType<String> theOutputFormat, IPrimitiveType<String> theType, IPrimitiveType<Date> theSince, List<IPrimitiveType<String>> theTypeFilter, BulkDataExportOptions.ExportStyle theExportStyle) {
String outputFormat = theOutputFormat != null ? theOutputFormat.getValueAsString() : null;
Set<String> resourceTypes = null;
@ -285,17 +285,22 @@ public class BulkDataExportProvider {
}
}
private Set<String> splitTypeFilters(IPrimitiveType<String> theTypeFilter) {
private Set<String> splitTypeFilters(List<IPrimitiveType<String>> 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<String> retVal = new HashSet<>();
for (IPrimitiveType<String> 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;
}
}

View File

@ -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

View File

@ -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)) {

View File

@ -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<T extends IBaseResource> extends BaseStora
}
// Synchronize composite params
mySearchParamWithInlineReferencesExtractor.storeCompositeStringUniques(newParams, entity, existingParams);
mySearchParamWithInlineReferencesExtractor.storeUniqueComboParameters(newParams, entity, existingParams);
}
}

View File

@ -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<RuntimeSearchParam> activeUniqueSearchParams = mySearchParamRegistry.getActiveUniqueSearchParams(myResourceName, theParams.keySet());
List<RuntimeSearchParam> activeUniqueSearchParams = mySearchParamRegistry.getActiveComboSearchParams(myResourceName, theParams.keySet());
if (activeUniqueSearchParams.size() > 0) {
Validate.isTrue(activeUniqueSearchParams.get(0).getComboSearchParamType()== ComboSearchParamType.UNIQUE, "Non unique combo parameters are not supported with the legacy search builder");
StringBuilder sb = new StringBuilder();
sb.append(myResourceName);
sb.append("?");
@ -943,7 +946,7 @@ public class LegacySearchBuilder implements ISearchBuilder {
}
private void addPredicateCompositeStringUnique(@Nonnull SearchParameterMap theParams, String theIndexedString, RequestPartitionId theRequestPartitionId) {
From<?, ResourceIndexedCompositeStringUnique> join = myQueryStack.createJoin(SearchBuilderJoinEnum.COMPOSITE_UNIQUE, null);
From<?, ResourceIndexedComboStringUnique> join = myQueryStack.createJoin(SearchBuilderJoinEnum.COMPOSITE_UNIQUE, null);
if (!theRequestPartitionId.isAllPartitions()) {
Integer partitionId = theRequestPartitionId.getFirstPartitionIdOrNull();

View File

@ -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<ResourceIndexedCompositeStringUnique, Long> {
public interface IResourceIndexedComboStringUniqueDao extends JpaRepository<ResourceIndexedComboStringUnique, Long> {
@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<ResourceIndexedCompositeStringUnique> findAllForResourceIdForUnitTest(@Param("resId") Long theResourceId);
@Query("SELECT r FROM ResourceIndexedComboStringUnique r WHERE r.myResourceId = :resId")
List<ResourceIndexedComboStringUnique> 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);
}

View File

@ -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<ResourceIndexedComboTokenNonUnique, Long> {
@Modifying
@Query("DELETE FROM ResourceIndexedComboTokenNonUnique t WHERE t.myResourceId = :res_id")
void deleteByResourceId(@Param("res_id") Long theResourcePid);
}

View File

@ -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));

View File

@ -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);
}

View File

@ -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"));

View File

@ -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 <code>system|value</code> 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<NameValuePair> params = URLEncodedUtils.parse(theValue.substring(identifierIndex), StandardCharsets.UTF_8, '&', ';');
Optional<NameValuePair> 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;

View File

@ -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);

View File

@ -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<Integer> 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;

View File

@ -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<RuntimeSearchParam> uniqueSearchParams = mySearchParamRegistry.getActiveUniqueSearchParams(resourceType);
List<RuntimeSearchParam> 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<List<String>> partsChoices = new ArrayList<>();
private void extractComboNonUniqueParam(ResourceTable theEntity, ResourceIndexedSearchParams theParams, String theResourceType, RuntimeSearchParam theParam) {
Set<String> queryStringsToPopulate = extractParameterCombinationsForComboParam(theParams, theResourceType, theParam);
List<RuntimeSearchParam> compositeComponents = JpaParamUtil.resolveComponentParameters(mySearchParamRegistry, next);
for (RuntimeSearchParam nextCompositeOf : compositeComponents) {
Collection<? extends BaseResourceIndexedSearchParam> paramsListForCompositePart = null;
Collection<ResourceLink> linksForCompositePart = null;
Collection<String> 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<String> nextChoicesList = new ArrayList<>();
partsChoices.add(nextChoicesList);
private void extractComboUniqueParam(ResourceTable theEntity, ResourceIndexedSearchParams theParams, String theResourceType, RuntimeSearchParam theParam) {
Set<String> 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<String> extractParameterCombinationsForComboParam(ResourceIndexedSearchParams theParams, String theResourceType, RuntimeSearchParam theParam) {
List<List<String>> partsChoices = new ArrayList<>();
List<RuntimeSearchParam> compositeComponents = JpaParamUtil.resolveComponentParameters(mySearchParamRegistry, theParam);
for (RuntimeSearchParam nextCompositeOf : compositeComponents) {
Collection<? extends BaseResourceIndexedSearchParam> paramsListForCompositePart = findParameterIndexes(theParams, nextCompositeOf);
Collection<ResourceLink> 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<String> 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<String> 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<String> queryStringsToPopulate = ResourceIndexedSearchParams.extractCompositeStringUniquesValueChains(resourceType, partsChoices);
for (String nextQueryString : queryStringsToPopulate) {
if (isNotBlank(nextQueryString)) {
ourLog.trace("Adding composite unique SP: {}", nextQueryString);
theParams.myCompositeStringUniques.add(new ResourceIndexedCompositeStringUnique(theEntity, nextQueryString, next.getId()));
if (linksForCompositePart != null) {
for (ResourceLink nextLink : linksForCompositePart) {
if (linksForCompositePartWantPaths.contains(nextLink.getSourcePath())) {
assert isNotBlank(nextLink.getTargetResourceType());
assert isNotBlank(nextLink.getTargetResourceId());
String value = nextLink.getTargetResourceType() + "/" + nextLink.getTargetResourceId();
if (isNotBlank(value)) {
value = UrlUtil.escapeUrlParam(value);
nextChoicesList.add(key + "=" + value);
}
}
}
}
}
return ResourceIndexedSearchParams.extractCompositeStringUniquesValueChains(theResourceType, partsChoices);
}
@Nullable
private Collection<? extends BaseResourceIndexedSearchParam> findParameterIndexes(ResourceIndexedSearchParams theParams, RuntimeSearchParam nextCompositeOf) {
Collection<? extends BaseResourceIndexedSearchParam> paramsListForCompositePart = null;
switch (nextCompositeOf.getParamType()) {
case NUMBER:
paramsListForCompositePart = theParams.myNumberParams;
break;
case DATE:
paramsListForCompositePart = theParams.myDateParams;
break;
case STRING:
paramsListForCompositePart = theParams.myStringParams;
break;
case TOKEN:
paramsListForCompositePart = theParams.myTokenParams;
break;
case QUANTITY:
paramsListForCompositePart = theParams.myQuantityParams;
break;
case URI:
paramsListForCompositePart = theParams.myUriParams;
break;
case REFERENCE:
case SPECIAL:
case COMPOSITE:
case HAS:
break;
}
if (paramsListForCompositePart != null) {
paramsListForCompositePart = paramsListForCompositePart
.stream()
.filter(t->t.getParamName().equals(nextCompositeOf.getName()))
.collect(Collectors.toList());
}
return paramsListForCompositePart;
}
@ -309,19 +383,21 @@ public class SearchParamWithInlineReferencesExtractor {
myDaoSearchParamSynchronizer = theDaoSearchParamSynchronizer;
}
public void storeCompositeStringUniques(ResourceIndexedSearchParams theParams, ResourceTable theEntity, ResourceIndexedSearchParams existingParams) {
public void storeUniqueComboParameters(ResourceIndexedSearchParams theParams, ResourceTable theEntity, ResourceIndexedSearchParams theExistingParams) {
// Store composite string uniques
/*
* String Uniques
*/
if (myDaoConfig.isUniqueIndexesEnabled()) {
for (ResourceIndexedCompositeStringUnique next : myDaoSearchParamSynchronizer.subtract(existingParams.myCompositeStringUniques, theParams.myCompositeStringUniques)) {
for (ResourceIndexedComboStringUnique next : myDaoSearchParamSynchronizer.subtract(theExistingParams.myComboStringUniques, theParams.myComboStringUniques)) {
ourLog.debug("Removing unique index: {}", next);
myEntityManager.remove(next);
theEntity.getParamsCompositeStringUnique().remove(next);
theEntity.getParamsComboStringUnique().remove(next);
}
boolean haveNewParams = false;
for (ResourceIndexedCompositeStringUnique next : myDaoSearchParamSynchronizer.subtract(theParams.myCompositeStringUniques, existingParams.myCompositeStringUniques)) {
boolean haveNewStringUniqueParams = false;
for (ResourceIndexedComboStringUnique next : myDaoSearchParamSynchronizer.subtract(theParams.myComboStringUniques, theExistingParams.myComboStringUniques)) {
if (myDaoConfig.isUniqueIndexesCheckedBeforeSave()) {
ResourceIndexedCompositeStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString());
ResourceIndexedComboStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString());
if (existing != null) {
String searchParameterId = "(unknown)";
@ -335,12 +411,12 @@ public class SearchParamWithInlineReferencesExtractor {
}
ourLog.debug("Persisting unique index: {}", next);
myEntityManager.persist(next);
haveNewParams = true;
haveNewStringUniqueParams = true;
}
if (theParams.myCompositeStringUniques.size() > 0 || haveNewParams) {
theEntity.setParamsCompositeStringUniquePresent(true);
if (theParams.myComboStringUniques.size() > 0 || haveNewStringUniqueParams) {
theEntity.setParamsComboStringUniquePresent(true);
} else {
theEntity.setParamsCompositeStringUniquePresent(false);
theEntity.setParamsComboStringUniquePresent(false);
}
}
}

View File

@ -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);

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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<RuntimeSearchParam> 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<String> comboParamNames = null;
List<RuntimeSearchParam> exactMatchParams = mySearchParamRegistry.getActiveComboSearchParams(myResourceName, theParams.keySet());
if (exactMatchParams.size() > 0) {
comboParam = exactMatchParams.get(0);
comboParamNames = new ArrayList<>(theParams.keySet());
}
List<RuntimeSearchParam> activeUniqueSearchParams = mySearchParamRegistry.getActiveUniqueSearchParams(myResourceName, theParams.keySet());
if (activeUniqueSearchParams.size() > 0) {
if (comboParam == null) {
List<RuntimeSearchParam> candidateComboParams = mySearchParamRegistry.getActiveComboSearchParams(myResourceName);
for (RuntimeSearchParam nextCandidate : candidateComboParams) {
List<String> 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<String> keys = new ArrayList<>(theParams.keySet());
Collections.sort(keys);
for (String nextParamName : keys) {
Collections.sort(comboParamNames);
for (String nextParamName : comboParamNames) {
List<List<IQueryParameterType>> nextValues = theParams.get(nextParamName);
nextParamName = UrlUtil.escapeUrlParam(nextParamName);
@ -1047,6 +1074,13 @@ public class SearchBuilder implements ISearchBuilder {
List<? extends IQueryParameterType> nextAnd = nextValues.remove(0);
IQueryParameterType nextOr = nextAnd.remove(0);
String nextOrValue = nextOr.getValueAsQueryToken(myContext);
if (comboParam.getComboSearchParamType() == ComboSearchParamType.NON_UNIQUE) {
if (nextParamDef.getParamType() == RestSearchParameterTypeEnum.STRING) {
nextOrValue = StringUtil.normalizeStringForSearchIndexing(nextOrValue);
}
}
nextOrValue = UrlUtil.escapeUrlParam(nextOrValue);
if (first) {
@ -1061,18 +1095,25 @@ public class SearchBuilder implements ISearchBuilder {
if (sb != null) {
String indexString = sb.toString();
ourLog.debug("Checking for unique index for query: {}", indexString);
ourLog.debug("Checking for {} combo index for query: {}", comboParam.getComboSearchParamType(), indexString);
// Interceptor broadcast: JPA_PERFTRACE_INFO
StorageProcessingMessage msg = new StorageProcessingMessage()
.setMessage("Using unique index for query for search: " + indexString);
.setMessage("Using " + comboParam.getComboSearchParamType() + " index for query for search: " + indexString);
HookParams params = new HookParams()
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest)
.add(StorageProcessingMessage.class, msg);
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
theQueryStack3.addPredicateCompositeUnique(indexString, myRequestPartitionId);
switch (comboParam.getComboSearchParamType()) {
case UNIQUE:
theQueryStack3.addPredicateCompositeUnique(indexString, myRequestPartitionId);
break;
case NON_UNIQUE:
theQueryStack3.addPredicateCompositeNonUnique(indexString, myRequestPartitionId);
break;
}
// Remove any empty parameters remaining after this
theParams.clean();

View File

@ -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);
}
}

View File

@ -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");

View File

@ -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

View File

@ -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);
}
}

View File

@ -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<ValueSet> valueSets = new ArrayList<>();
final List<ConceptMap> conceptMaps = new ArrayList<>();
final List<LoincLinguisticVariantsHandler.LinguisticVariant> 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);
}

View File

@ -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<String, TermConcept> myCode2Concept;
public LoincConsumerNameHandler(Map<String, TermConcept> 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);
}
}

View File

@ -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<String, TermConcept> myCode2Concept;
private final String myLanguageCode;
public LoincLinguisticVariantHandler(Map<String, TermConcept> 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);
}
}

View File

@ -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<LinguisticVariant> myLinguisticVariants;
public LoincLinguisticVariantsHandler(List<LinguisticVariant> 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;
}
}
}

View File

@ -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
*/

View File

@ -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

View File

@ -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 {

View File

@ -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<String> types = Sets.newHashSet("Patient", "EpisodeOfCare");
Set<String> 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() {

View File

@ -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 * ")));

View File

@ -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<Coverage> myCoverageDao;
@Autowired
protected IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
protected IResourceIndexedComboStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
@Autowired
@Qualifier("myAllergyIntoleranceDaoDstu3")
protected IFhirResourceDao<AllergyIntolerance> myAllergyIntoleranceDao;

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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<String> 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));
}
}

View File

@ -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<AllergyIntolerance> myAllergyIntoleranceDao;

View File

@ -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<ResourceIndexedComboTokenNonUnique> 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<String> 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<ResourceIndexedComboTokenNonUnique> 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<String> 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();
}
}

View File

@ -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<String> 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<ResourceIndexedCompositeStringUnique> all = myResourceIndexedCompositeStringUniqueDao.findAll();
List<ResourceIndexedComboStringUnique> 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<ResourceIndexedCompositeStringUnique> all = myResourceIndexedCompositeStringUniqueDao.findAll();
List<ResourceIndexedComboStringUnique> 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<RuntimeSearchParam> uniqueSearchParams = mySearchParamRegistry.getActiveUniqueSearchParams("Observation");
List<RuntimeSearchParam> 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<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
List<ResourceIndexedComboStringUnique> 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<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
List<ResourceIndexedComboStringUnique> 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<ResourceIndexedCompositeStringUnique> all = myResourceIndexedCompositeStringUniqueDao.findAll();
List<ResourceIndexedComboStringUnique> all = myResourceIndexedCompositeStringUniqueDao.findAll();
assertEquals(2, all.size());
}
});
@ -873,7 +815,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
});
runInTransaction(() -> {
List<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
List<ResourceIndexedComboStringUnique> 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<ResourceIndexedCompositeStringUnique> all = myResourceIndexedCompositeStringUniqueDao.findAll();
List<ResourceIndexedComboStringUnique> 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<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
List<ResourceIndexedComboStringUnique> 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<ResourceIndexedCompositeStringUnique> uniques;
List<ResourceIndexedComboStringUnique> 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<ResourceIndexedCompositeStringUnique> uniques;
List<ResourceIndexedComboStringUnique> 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<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
List<ResourceIndexedComboStringUnique> 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<ResourceIndexedCompositeStringUnique> uniques;
List<ResourceIndexedComboStringUnique> 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<ResourceIndexedCompositeStringUnique> uniques;
List<ResourceIndexedComboStringUnique> uniques;
Organization org = new Organization();
org.setId("Organization/ORG");
@ -1385,7 +1323,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
createUniqueBirthdateAndGenderSps();
Patient pt;
List<ResourceIndexedCompositeStringUnique> uniques;
List<ResourceIndexedComboStringUnique> 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<ResourceIndexedCompositeStringUnique> uniques;
List<ResourceIndexedComboStringUnique> uniques;
Patient pt;
pt = new Patient();
@ -1464,7 +1402,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
myResourceReindexingSvc.forceReindexingPass();
myResourceReindexingSvc.forceReindexingPass();
List<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
List<ResourceIndexedComboStringUnique> 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<RuntimeSearchParam> params = mySearchParamRegistry.getActiveUniqueSearchParams("Patient");
List<RuntimeSearchParam> 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();
}

View File

@ -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<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAllForResourceIdForUnitTest(patientId);
List<ResourceIndexedComboStringUnique> 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<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAllForResourceIdForUnitTest(patientId);
List<ResourceIndexedComboStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAllForResourceIdForUnitTest(patientId);
assertEquals(1, uniques.size());
assertEquals(null, uniques.get(0).getPartitionId().getPartitionId());
assertEquals(myPartitionDate, uniques.get(0).getPartitionId().getPartitionDate());

View File

@ -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<RuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName, Set<String> theParamNames) {
public List<RuntimeSearchParam> getActiveComboSearchParams(String theResourceName, Set<String> theParamNames) {
throw new UnsupportedOperationException();
}
@ -451,7 +451,7 @@ public class SearchParamExtractorR4Test {
}
@Override
public List<RuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName) {
public List<RuntimeSearchParam> getActiveComboSearchParams(String theResourceName) {
throw new UnsupportedOperationException();
}

View File

@ -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<AllergyIntolerance> myAllergyIntoleranceDao;

View File

@ -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
*/

View File

@ -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<String> 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")) {

View File

@ -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<IBaseResource> 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<String, byte[]> myResponses = new HashMap<>();

View File

@ -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);

View File

@ -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<String, TermConcept> concepts = extractConcepts();
Map<String, ValueSet> valueSets = extractValueSets();
Map<String, ConceptMap> 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<String, TermConcept> 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<ITermLoaderSvc.FileDescriptor> 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<TermConceptDesignation> 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<TermConceptDesignation> 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());
}
}

View File

@ -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"
Can't render this file because it contains an unexpected character in line 4 and column 8.

View File

@ -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"
1 ID ISO_LANGUAGE ISO_COUNTRY LANGUAGE_NAME PRODUCER
2 5 zh CN Chinese (CHINA) Lin Zhang, A LOINC volunteer from China
3 7 es AR Spanish (ARGENTINA) Conceptum Medical Terminology Center
4 8 fr CA French (CANADA) Canada Health Infoway Inc.
5 de AT German (AUSTRIA) ELGA, Austria
6 88 AT German (AUSTRIA) ELGA, Austria
7 89 de German (AUSTRIA) ELGA, Austria
8 90 de AT ELGA, Austria
9 24 de AT German (AUSTRIA) ELGA, Austria

View File

@ -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"
1 LOINC_NUM COMPONENT PROPERTY TIME_ASPCT SYSTEM SCALE_TYP METHOD_TYP CLASS SHORTNAME LONG_COMMON_NAME RELATEDNAMES2 LinguisticVariantDisplayName
2 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
3 43730-1 EBV-DNA qn. PCR EBV-DNA quantitativ PCR
4 17787-3 CoV OC43 RNA ql/SM P Coronavirus OC43 RNA ql. /Sondermaterial PCR

View File

@ -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",,,"","","",""
1 LOINC_NUM COMPONENT PROPERTY TIME_ASPCT SYSTEM SCALE_TYP METHOD_TYP CLASS SHORTNAME LONG_COMMON_NAME RELATEDNAMES2 LinguisticVariantDisplayName
2 61438-8 Cellules de Purkinje cytoplasmique type 2 , IgG Titre Temps ponctuel Sérum Quantitatif Immunofluorescence Sérologie
3 11704-4 Gliale nucléaire de type 1 , IgG Titre Temps ponctuel LCR Quantitatif Immunofluorescence Sérologie
4 Cellules de Purkinje cytoplasmique type 2 , IgG Titre Temps ponctuel Sérum Quantitatif
5 17787-3 Virus respiratoire syncytial bovin Présence-Seuil Temps ponctuel XXX Ordinal Culture spécifique à un microorganisme Microbiologie
6 17788-1 Cellules de Purkinje cytoplasmique type 2 , IgG Titre Temps ponctuel Sérum Quantitatif

View File

@ -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""指的是主要由中枢半球(大脑皮质和基底神经节)组成的那部分脑结构 流 流量;流速;流体 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)",""
1 LOINC_NUM COMPONENT PROPERTY TIME_ASPCT SYSTEM SCALE_TYP METHOD_TYP CLASS SHORTNAME LONG_COMMON_NAME RELATEDNAMES2 LinguisticVariantDisplayName
2 61438-8 血流速度.收缩期.最大值 速度 时间点 大脑中动脉 定量型 超声.多普勒 产科学检查与测量指标.超声 Cereb 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 大脑(Cerebral) 时刻;随机;随意;瞬间 术语"cerebral"指的是主要由中枢半球(大脑皮质和基底神经节)组成的那部分脑结构 流 流量;流速;流体 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)
3 11704-4 血流速度.收缩期.最大值 速度 时间点 动脉导管 定量型 超声.多普勒 产科学检查与测量指标.超声 动态 动脉管 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 时刻;随机;随意;瞬间 流 流量;流速;流体 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)
4 17787-3 血流速度.收缩期.最大值 速度 时间点 二尖瓣^胎儿 定量型 超声.多普勒 产科学检查与测量指标.超声 僧帽瓣 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 时刻;随机;随意;瞬间 流 流量;流速;流体 胎;超系统 - 胎儿 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)
5 61438-6 速度 时间点 二尖瓣^胎儿 定量型 超声.多普勒 产科学检查与测量指标.超声 僧帽瓣 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 时刻;随机;随意;瞬间 流 流量;流速;流体 胎;超系统 - 胎儿 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)
6 10000-8 血流速度.收缩期.最大值 时间点 二尖瓣^胎儿 定量型 超声.多普勒 产科学检查与测量指标.超声 僧帽瓣 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 时刻;随机;随意;瞬间 流 流量;流速;流体 胎;超系统 - 胎儿 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)
7 17788-1 血流速度.收缩期.最大值 速度 大脑中动脉 定量型 超声.多普勒 产科学检查与测量指标.超声 Cereb 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 大脑(Cerebral) 时刻;随机;随意;瞬间 术语"cerebral"指的是主要由中枢半球(大脑皮质和基底神经节)组成的那部分脑结构 流 流量;流速;流体 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)
8 11488-4 血流速度.收缩期.最大值 速度 时间点 定量型 超声.多普勒 产科学检查与测量指标.超声 Cereb 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 大脑(Cerebral) 时刻;随机;随意;瞬间 术语"cerebral"指的是主要由中枢半球(大脑皮质和基底神经节)组成的那部分脑结构 流 流量;流速;流体 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)
9 47239-9 血流速度.收缩期.最大值 速度 时间点 大脑中动脉 超声.多普勒 产科学检查与测量指标.超声 Cereb 动态 可用数量表示的;定量性;数值型;数量型;连续数值型标尺 大脑(Cerebral) 时刻;随机;随意;瞬间 术语"cerebral"指的是主要由中枢半球(大脑皮质和基底神经节)组成的那部分脑结构 流 流量;流速;流体 血;全血 血流量;血液流量 速度(距离/时间);速率;速率(距离/时间)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"
}
}
]
}

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -99,6 +99,24 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
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() {

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -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<String> 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
*/

View File

@ -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();
}
}

View File

@ -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<ResourceIndexedCompositeStringUnique> {
public class ResourceIndexedComboStringUnique extends BasePartitionable implements Comparable<ResourceIndexedComboStringUnique> {
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)

View File

@ -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<ResourceIndexedComboTokenNonUnique> {
@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 <T extends BaseResourceIndex> 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);
}
}

View File

@ -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<ResourceIndexedCompositeStringUnique> myParamsCompositeStringUnique;
private Collection<ResourceIndexedComboStringUnique> 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<ResourceIndexedComboTokenNonUnique> 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<ResourceIndexedCompositeStringUnique> getParamsCompositeStringUnique() {
if (myParamsCompositeStringUnique == null) {
myParamsCompositeStringUnique = new ArrayList<>();
public Collection<ResourceIndexedComboStringUnique> getParamsComboStringUnique() {
if (myParamsComboStringUnique == null) {
myParamsComboStringUnique = new ArrayList<>();
}
return myParamsCompositeStringUnique;
return myParamsComboStringUnique;
}
public Collection<ResourceIndexedComboTokenNonUnique> getmyParamsComboTokensNonUnique() {
if (myParamsComboTokensNonUnique == null) {
myParamsComboTokensNonUnique = new ArrayList<>();
}
return myParamsComboTokensNonUnique;
}
public Collection<ResourceIndexedSearchParamCoords> 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() {

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>5.5.0-PRE6-SNAPSHOT</version>
<version>5.5.0-PRE7-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

Some files were not shown because too many files have changed in this diff Show More