SQL Join Rework (#2086)
* switched to adding annotations. Just did a test with Token, but the sql looks clean. * switched to adding annotations. Just did a test with Token, but the sql looks clean. * added the other six index links * trying different annotations * Start testing * Update resources on package install * Add changelog * Join rework * CLean up SQL builder * Add docs * SP rework * Join work * Work on params * Work on refactor * Work on chains * Work on joins * Rework queries * Work on queries * Many more tests passing * Refs test * Work on sorting * Work on tests * More joins work * Work on tests * Work on queries * Tests passing * More test fixes * Test fixes * Work on SQL * Tests passing * Add some tests * Add some tests * License headers * Use entity manager to get datasourcd * One more fix * Model cleanup * Ongoing work * Fixes * Fixes * Work on joins * Ongoing fixes * Merge conflict * Cleanup * clean up unused fields * Work on join * COmpile fix * Rework querying * Update hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HibernateDialectProvider.java Co-authored-by: Ken Stevens <khstevens@gmail.com> * Address review comments * Resolve fixmes * Test fix * Test fixes * Test fix Co-authored-by: Ken Stevens <khstevens@gmail.com>
This commit is contained in:
parent
4a5f5199b5
commit
8000d2d0cf
|
@ -208,7 +208,7 @@ public class TokenParam extends BaseParam /*implements IQueryParameterType*/ {
|
|||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return StringUtils.isEmpty(myValue);
|
||||
return StringUtils.isBlank(mySystem) && StringUtils.isBlank(myValue) && getMissing() == null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package ca.uhn.fhir.util;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
public class IoUtil {
|
||||
|
||||
/**
|
||||
* Replacement for the deprecated commons-lang method of the same name. Use sparingly
|
||||
* since they are right that most uses of this should be replaced with try-with-resources
|
||||
*/
|
||||
public static void closeQuietly(final AutoCloseable theCloseable) {
|
||||
try {
|
||||
if (theCloseable != null) {
|
||||
theCloseable.close();
|
||||
}
|
||||
} catch (final Exception ioe) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -101,6 +101,7 @@ ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.successfulCreate=Successfully create
|
|||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.successfulUpdate=Successfully updated resource "{0}" in {1}ms
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.successfulDeletes=Successfully deleted {0} resource(s) in {1}ms
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.invalidSearchParameter=Unknown search parameter "{0}" for resource type "{1}". Valid search parameters for this search are: {2}
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.invalidSortParameter=Unknown _sort parameter value "{0}" for resource type "{1}" (Note: sort parameters values must use a valid Search Parameter). Valid values for this search are: {2}
|
||||
|
||||
ca.uhn.fhir.rest.api.PatchTypeEnum.missingPatchContentType=Missing or invalid content type for PATCH operation
|
||||
ca.uhn.fhir.rest.api.PatchTypeEnum.invalidPatchContentType=Invalid Content-Type for PATCH operation: {0}
|
||||
|
@ -115,11 +116,11 @@ ca.uhn.fhir.jpa.patch.FhirPatch.invalidMoveDestinationIndex=Invalid move destina
|
|||
ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor.externalReferenceNotAllowed=Resource contains external reference to URL "{0}" but this server is not configured to allow external references
|
||||
ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor.failedToExtractPaths=Failed to extract values from resource using FHIRPath "{0}": {1}
|
||||
|
||||
ca.uhn.fhir.jpa.dao.SearchBuilder.invalidQuantityPrefix=Unable to handle quantity prefix "{0}" for value: {1}
|
||||
ca.uhn.fhir.jpa.dao.SearchBuilder.invalidNumberPrefix=Unable to handle number prefix "{0}" for value: {1}
|
||||
ca.uhn.fhir.jpa.dao.SearchBuilder.sourceParamDisabled=The _source parameter is disabled on this server
|
||||
ca.uhn.fhir.jpa.dao.SearchBuilder.invalidCodeMissingSystem=Invalid token specified for parameter {0} - No system specified: {1}|{2}
|
||||
ca.uhn.fhir.jpa.dao.SearchBuilder.invalidCodeMissingCode=Invalid token specified for parameter {0} - No code specified: {1}|{2}
|
||||
ca.uhn.fhir.jpa.dao.LegacySearchBuilder.invalidQuantityPrefix=Unable to handle quantity prefix "{0}" for value: {1}
|
||||
ca.uhn.fhir.jpa.dao.LegacySearchBuilder.invalidNumberPrefix=Unable to handle number prefix "{0}" for value: {1}
|
||||
ca.uhn.fhir.jpa.dao.LegacySearchBuilder.sourceParamDisabled=The _source parameter is disabled on this server
|
||||
ca.uhn.fhir.jpa.dao.LegacySearchBuilder.invalidCodeMissingSystem=Invalid token specified for parameter {0} - No system specified: {1}|{2}
|
||||
ca.uhn.fhir.jpa.dao.LegacySearchBuilder.invalidCodeMissingCode=Invalid token specified for parameter {0} - No code specified: {1}|{2}
|
||||
|
||||
ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoConceptMapDstu3.matchesFound=Matches found!
|
||||
ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoConceptMapDstu3.noMatchesFound=No matches found!
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package ca.uhn.fhir.util;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class IoUtilTest {
|
||||
|
||||
@Mock
|
||||
private AutoCloseable myCloseable;
|
||||
|
||||
@Test
|
||||
public void testCloseNull() {
|
||||
// Should throw no exception
|
||||
IoUtil.closeQuietly(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseWithException() throws Exception {
|
||||
doThrow(new Exception()).when(myCloseable).close();
|
||||
// Should throw no exception
|
||||
IoUtil.closeQuietly(myCloseable);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -12,3 +12,17 @@
|
|||
<li>MomentJS (Testpage Overlay): 2.15.1 -> 2.27.0</li>
|
||||
<li>Select2 (Testpage Overlay): 4.0.3 -> 4.0.13</li>
|
||||
</ul>"
|
||||
- item:
|
||||
type: "add"
|
||||
title: "The JPA Server SQL generator for handling FHIR search operations has been completely rewritten to
|
||||
no longer depend on the Hibernate QueryBuilder APIs. This rewrite produces much more efficient SQL in many cases
|
||||
and should dramatically increase performance in such cases. For example, a 10x speedup has been observed on
|
||||
Postgresql when searching a very large database where the search URL has multiple chained search params.
|
||||
<br/><br/>
|
||||
This new SQL builder will be disabled by default in HAPI FHIR 5.2.0 and Smile CDR 2020.11.R01, and can be enabled
|
||||
via a setting in the DaoConfig (HAPI FHIR) and Storage Module config (Smile CDR). Snapshot/prerelease builds will
|
||||
ship with this new SQL builder enabled by default, and the intention is to make this the default and ultimately
|
||||
remove the legacy SQL builder in the next quarterly release.
|
||||
<br/><br/>
|
||||
We highly encourage testing of this new feature, and welcome feedback on how it performs in your environment.
|
||||
"
|
||||
|
|
|
@ -3,7 +3,6 @@ package ca.uhn.fhir.jpa.api.config;
|
|||
import ca.uhn.fhir.jpa.api.model.WarmCacheEntry;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
||||
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
|
||||
import ca.uhn.fhir.util.HapiExtensions;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
@ -83,9 +82,8 @@ public class DaoConfig {
|
|||
private static final Integer DEFAULT_MAXIMUM_TRANSACTION_BUNDLE_SIZE = null;
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(DaoConfig.class);
|
||||
private static final int DEFAULT_EXPUNGE_BATCH_SIZE = 800;
|
||||
private IndexEnabledEnum myIndexMissingFieldsEnabled = IndexEnabledEnum.DISABLED;
|
||||
private static final int DEFAULT_MAXIMUM_DELETE_CONFLICT_COUNT = 60;
|
||||
|
||||
private IndexEnabledEnum myIndexMissingFieldsEnabled = IndexEnabledEnum.DISABLED;
|
||||
/**
|
||||
* Child Configurations
|
||||
*/
|
||||
|
@ -196,6 +194,32 @@ public class DaoConfig {
|
|||
* @since 5.0.0
|
||||
*/
|
||||
private boolean myDeleteEnabled = true;
|
||||
/**
|
||||
* @since 5.1.0
|
||||
*/
|
||||
private boolean myLastNEnabled = false;
|
||||
/**
|
||||
* @since 5.2.0
|
||||
*/
|
||||
private boolean myUseLegacySearchBuilder = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public DaoConfig() {
|
||||
setSubscriptionEnabled(true);
|
||||
setSubscriptionPollDelay(0);
|
||||
setSubscriptionPurgeInactiveAfterMillis(Long.MAX_VALUE);
|
||||
setMarkResourcesForReindexingUponSearchParameterChange(true);
|
||||
setReindexThreadCount(Runtime.getRuntime().availableProcessors());
|
||||
setExpungeThreadCount(Runtime.getRuntime().availableProcessors());
|
||||
setBundleTypesAllowedForStorage(DEFAULT_BUNDLE_TYPES_ALLOWED_FOR_STORAGE);
|
||||
|
||||
if ("true".equalsIgnoreCase(System.getProperty(DISABLE_STATUS_BASED_REINDEX))) {
|
||||
ourLog.info("Status based reindexing is DISABLED");
|
||||
setStatusBasedReindexingDisabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is <code>false</code>) the <code>$lastn</code> operation will be enabled for
|
||||
|
@ -222,26 +246,27 @@ public class DaoConfig {
|
|||
}
|
||||
|
||||
/**
|
||||
* @since 5.1.0
|
||||
* This method controls whether to use the new non-hibernate search SQL builder that was introduced in HAPI FHIR 5.2.0.
|
||||
* By default this will be <code>false</code> meaning that the new SQL builder is used. Set to <code>true</code> to use the
|
||||
* legacy SQL builder based on Hibernate.
|
||||
* <p>Note that this method will be removed in HAPI FHIR 5.4.0</p>
|
||||
*
|
||||
* @since 5.3.0
|
||||
*/
|
||||
private boolean myLastNEnabled = false;
|
||||
public boolean isUseLegacySearchBuilder() {
|
||||
return myUseLegacySearchBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* This method controls whether to use the new non-hibernate search SQL builder that was introduced in HAPI FHIR 5.2.0.
|
||||
* By default this will be <code>false</code> meaning that the new SQL builder is used. Set to <code>true</code> to use the
|
||||
* legacy SQL builder based on Hibernate.
|
||||
* <p>Note that this method will be removed in HAPI FHIR 5.4.0</p>
|
||||
*
|
||||
* @since 5.3.0
|
||||
*/
|
||||
public DaoConfig() {
|
||||
setSubscriptionEnabled(true);
|
||||
setSubscriptionPollDelay(0);
|
||||
setSubscriptionPurgeInactiveAfterMillis(Long.MAX_VALUE);
|
||||
setMarkResourcesForReindexingUponSearchParameterChange(true);
|
||||
setReindexThreadCount(Runtime.getRuntime().availableProcessors());
|
||||
setExpungeThreadCount(Runtime.getRuntime().availableProcessors());
|
||||
setBundleTypesAllowedForStorage(DEFAULT_BUNDLE_TYPES_ALLOWED_FOR_STORAGE);
|
||||
|
||||
if ("true".equalsIgnoreCase(System.getProperty(DISABLE_STATUS_BASED_REINDEX))) {
|
||||
ourLog.info("Status based reindexing is DISABLED");
|
||||
setStatusBasedReindexingDisabled(true);
|
||||
}
|
||||
public void setUseLegacySearchBuilder(boolean theUseLegacySearchBuilder) {
|
||||
myUseLegacySearchBuilder = theUseLegacySearchBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -995,43 +1020,6 @@ public class DaoConfig {
|
|||
myModelConfig.setAllowExternalReferences(theAllowExternalReferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in
|
||||
* {@link ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate} when resolving searches where all predicates are using
|
||||
* precision of {@link ca.uhn.fhir.model.api.TemporalPrecisionEnum#DAY}.
|
||||
*
|
||||
* For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an
|
||||
* ordinal {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
|
||||
* and {@link ResourceIndexedSearchParamDate#getValueHighDateOrdinal()}
|
||||
* </p>
|
||||
* Default is {@literal true} beginning in HAPI FHIR 5.0
|
||||
* </p>
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public void setUseOrdinalDatesForDayPrecisionSearches(boolean theUseOrdinalDates) {
|
||||
myModelConfig.setUseOrdinalDatesForDayPrecisionSearches(theUseOrdinalDates);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in
|
||||
* {@link ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate} when resolving searches where all predicates are using
|
||||
* precision of {@link ca.uhn.fhir.model.api.TemporalPrecisionEnum#DAY}.
|
||||
*
|
||||
* For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an
|
||||
* integer representing the ordinal date {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
|
||||
* and {@link ResourceIndexedSearchParamDate#getValueHighDateOrdinal()}
|
||||
* </p>
|
||||
* Default is {@literal true} beginning in HAPI FHIR 5.0
|
||||
* </p>
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public boolean getUseOrdinalDatesForDayPrecisionSearches() {
|
||||
return myModelConfig.getUseOrdinalDatesForDayPrecisionSearches();
|
||||
}
|
||||
/**
|
||||
* @see #setAllowInlineMatchUrlReferences(boolean)
|
||||
*/
|
||||
|
@ -2033,8 +2021,8 @@ public class DaoConfig {
|
|||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public void setDeleteEnabled(boolean theDeleteEnabled) {
|
||||
myDeleteEnabled = theDeleteEnabled;
|
||||
public boolean isDeleteEnabled() {
|
||||
return myDeleteEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2046,11 +2034,60 @@ public class DaoConfig {
|
|||
*
|
||||
* @since 5.0.0
|
||||
*/
|
||||
public boolean isDeleteEnabled() {
|
||||
return myDeleteEnabled;
|
||||
public void setDeleteEnabled(boolean theDeleteEnabled) {
|
||||
myDeleteEnabled = theDeleteEnabled;
|
||||
}
|
||||
|
||||
public enum StoreMetaSourceInformationEnum {
|
||||
/**
|
||||
* <p>
|
||||
* This determines the maximum number of conflicts that should be fetched and handled while retrying a delete of a resource.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value for this setting is {@code 60}.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public Integer getMaximumDeleteConflictQueryCount() {
|
||||
return myMaximumDeleteConflictQueryCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This determines the maximum number of conflicts that should be fetched and handled while retrying a delete of a resource.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value for this setting is {@code 60}.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public void setMaximumDeleteConflictQueryCount(Integer theMaximumDeleteConflictQueryCount) {
|
||||
myMaximumDeleteConflictQueryCount = theMaximumDeleteConflictQueryCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This determines whether $binary-access-write operations should first load the InputStream into memory before persisting the
|
||||
* contents to the database. This needs to be enabled for MS SQL Server as this DB requires that the blob size be known
|
||||
* in advance.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that this setting should be enabled with caution as it can lead to significant demands on memory.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value for this setting is {@code false}.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.1.0
|
||||
* @deprecated In 5.2.0 this setting no longer does anything
|
||||
*/
|
||||
@Deprecated
|
||||
public void setPreloadBlobFromInputStream(Boolean thePreloadBlobFromInputStream) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
public enum StoreMetaSourceInformationEnum {
|
||||
NONE(false, false),
|
||||
SOURCE_URI(true, false),
|
||||
REQUEST_ID(false, true),
|
||||
|
@ -2120,53 +2157,4 @@ public class DaoConfig {
|
|||
ANY
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This determines the maximum number of conflicts that should be fetched and handled while retrying a delete of a resource.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value for this setting is {@code 60}.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public Integer getMaximumDeleteConflictQueryCount() {
|
||||
return myMaximumDeleteConflictQueryCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This determines the maximum number of conflicts that should be fetched and handled while retrying a delete of a resource.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value for this setting is {@code 60}.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.1.0
|
||||
*/
|
||||
public void setMaximumDeleteConflictQueryCount(Integer theMaximumDeleteConflictQueryCount) {
|
||||
myMaximumDeleteConflictQueryCount = theMaximumDeleteConflictQueryCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This determines whether $binary-access-write operations should first load the InputStream into memory before persisting the
|
||||
* contents to the database. This needs to be enabled for MS SQL Server as this DB requires that the blob size be known
|
||||
* in advance.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that this setting should be enabled with caution as it can lead to significant demands on memory.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value for this setting is {@code false}.
|
||||
* </p>
|
||||
*
|
||||
* @since 5.1.0
|
||||
* @deprecated In 5.2.0 this setting no longer does anything
|
||||
*/
|
||||
@Deprecated
|
||||
public void setPreloadBlobFromInputStream(Boolean thePreloadBlobFromInputStream) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -183,6 +183,13 @@
|
|||
<artifactId>javassist</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SQL Builder -->
|
||||
<dependency>
|
||||
<groupId>com.healthmarketscience.sqlbuilder</groupId>
|
||||
<artifactId>sqlbuilder</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Jackson -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
|
|
|
@ -5,6 +5,7 @@ import ca.uhn.fhir.i18n.HapiLocalizer;
|
|||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
import ca.uhn.fhir.interceptor.executor.InterceptorService;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IDao;
|
||||
import ca.uhn.fhir.jpa.batch.BatchJobsConfig;
|
||||
|
@ -19,7 +20,7 @@ import ca.uhn.fhir.jpa.bulk.svc.BulkDataExportSvcImpl;
|
|||
import ca.uhn.fhir.jpa.dao.HistoryBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.HistoryBuilderFactory;
|
||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
|
||||
import ca.uhn.fhir.jpa.dao.index.DaoResourceLinkResolver;
|
||||
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
|
||||
|
@ -51,6 +52,27 @@ import ca.uhn.fhir.jpa.search.PersistedJpaBundleProviderFactory;
|
|||
import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider;
|
||||
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.CoordsPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.ForcedIdPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.NumberPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.QuantityPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.ResourceIdPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.ResourceLinkPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.ResourceTablePredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.SearchParamPresentPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.SourcePredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.StringPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.TagPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.TokenPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.UriPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.GeneratedSql;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryExecutor;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SqlObjectFactory;
|
||||
import ca.uhn.fhir.jpa.search.cache.DatabaseSearchCacheSvcImpl;
|
||||
import ca.uhn.fhir.jpa.search.cache.DatabaseSearchResultCacheSvcImpl;
|
||||
import ca.uhn.fhir.jpa.search.cache.ISearchCacheSvc;
|
||||
|
@ -414,33 +436,19 @@ public abstract class BaseConfig {
|
|||
return new PersistedJpaBundleProviderFactory();
|
||||
}
|
||||
|
||||
@Bean(name = PERSISTED_JPA_BUNDLE_PROVIDER)
|
||||
@Scope("prototype")
|
||||
public PersistedJpaBundleProvider persistedJpaBundleProvider(RequestDetails theRequest, String theUuid) {
|
||||
return new PersistedJpaBundleProvider(theRequest, theUuid);
|
||||
}
|
||||
|
||||
@Bean(name = PERSISTED_JPA_BUNDLE_PROVIDER_BY_SEARCH)
|
||||
@Scope("prototype")
|
||||
public PersistedJpaBundleProvider persistedJpaBundleProvider(RequestDetails theRequest, Search theSearch) {
|
||||
return new PersistedJpaBundleProvider(theRequest, theSearch);
|
||||
}
|
||||
|
||||
@Bean(name = PERSISTED_JPA_SEARCH_FIRST_PAGE_BUNDLE_PROVIDER)
|
||||
@Scope("prototype")
|
||||
public PersistedJpaSearchFirstPageBundleProvider persistedJpaSearchFirstPageBundleProvider(RequestDetails theRequest, Search theSearch, SearchCoordinatorSvcImpl.SearchTask theSearchTask, ISearchBuilder theSearchBuilder) {
|
||||
return new PersistedJpaSearchFirstPageBundleProvider(theSearch, theSearchTask, theSearchBuilder, theRequest);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SearchBuilderFactory searchBuilderFactory() {
|
||||
return new SearchBuilderFactory();
|
||||
}
|
||||
|
||||
@Bean(name = SEARCH_BUILDER)
|
||||
@Scope("prototype")
|
||||
public SearchBuilder persistedJpaSearchFirstPageBundleProvider(IDao theDao, String theResourceName, Class<? extends IBaseResource> theResourceType) {
|
||||
return new SearchBuilder(theDao, theResourceName, theResourceType);
|
||||
@Bean
|
||||
public SqlObjectFactory sqlBuilderFactory() {
|
||||
return new SqlObjectFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HibernateDialectProvider hibernateDialectProvider() {
|
||||
return new HibernateDialectProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
@ -448,9 +456,136 @@ public abstract class BaseConfig {
|
|||
return new HistoryBuilderFactory();
|
||||
}
|
||||
|
||||
/* **************************************************************** *
|
||||
* Prototype Beans Below *
|
||||
* **************************************************************** */
|
||||
|
||||
@Bean(name = PERSISTED_JPA_BUNDLE_PROVIDER)
|
||||
@Scope("prototype")
|
||||
public PersistedJpaBundleProvider newPersistedJpaBundleProvider(RequestDetails theRequest, String theUuid) {
|
||||
return new PersistedJpaBundleProvider(theRequest, theUuid);
|
||||
}
|
||||
|
||||
@Bean(name = PERSISTED_JPA_BUNDLE_PROVIDER_BY_SEARCH)
|
||||
@Scope("prototype")
|
||||
public PersistedJpaBundleProvider newPersistedJpaBundleProvider(RequestDetails theRequest, Search theSearch) {
|
||||
return new PersistedJpaBundleProvider(theRequest, theSearch);
|
||||
}
|
||||
|
||||
@Bean(name = PERSISTED_JPA_SEARCH_FIRST_PAGE_BUNDLE_PROVIDER)
|
||||
@Scope("prototype")
|
||||
public PersistedJpaSearchFirstPageBundleProvider newPersistedJpaSearchFirstPageBundleProvider(RequestDetails theRequest, Search theSearch, SearchCoordinatorSvcImpl.SearchTask theSearchTask, ISearchBuilder theSearchBuilder) {
|
||||
return new PersistedJpaSearchFirstPageBundleProvider(theSearch, theSearchTask, theSearchBuilder, theRequest);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public CompositeUniqueSearchParameterPredicateBuilder newCompositeUniqueSearchParameterPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return new CompositeUniqueSearchParameterPredicateBuilder(theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public CoordsPredicateBuilder newCoordsPredicateBuilder(SearchQueryBuilder theSearchBuilder) {
|
||||
return new CoordsPredicateBuilder(theSearchBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public DatePredicateBuilder newDatePredicateBuilder(SearchQueryBuilder theSearchBuilder) {
|
||||
return new DatePredicateBuilder(theSearchBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public ForcedIdPredicateBuilder newForcedIdPredicateBuilder(SearchQueryBuilder theSearchBuilder) {
|
||||
return new ForcedIdPredicateBuilder(theSearchBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public NumberPredicateBuilder newNumberPredicateBuilder(SearchQueryBuilder theSearchBuilder) {
|
||||
return new NumberPredicateBuilder(theSearchBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public QuantityPredicateBuilder newQuantityPredicateBuilder(SearchQueryBuilder theSearchBuilder) {
|
||||
return new QuantityPredicateBuilder(theSearchBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public ResourceLinkPredicateBuilder newResourceLinkPredicateBuilder(QueryStack theQueryStack, SearchQueryBuilder theSearchBuilder, boolean theReversed) {
|
||||
return new ResourceLinkPredicateBuilder(theQueryStack, theSearchBuilder, theReversed);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public ResourceTablePredicateBuilder newResourceTablePredicateBuilder(SearchQueryBuilder theSearchBuilder) {
|
||||
return new ResourceTablePredicateBuilder(theSearchBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public TagPredicateBuilder newTagPredicateBuilder(SearchQueryBuilder theSearchBuilder) {
|
||||
return new TagPredicateBuilder(theSearchBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public ResourceIdPredicateBuilder newResourceIdPredicateBuilder(SearchQueryBuilder theSearchBuilder) {
|
||||
return new ResourceIdPredicateBuilder(theSearchBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public SearchParamPresentPredicateBuilder newSearchParamPresentPredicateBuilder(SearchQueryBuilder theSearchBuilder) {
|
||||
return new SearchParamPresentPredicateBuilder(theSearchBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public StringPredicateBuilder newStringPredicateBuilder(SearchQueryBuilder theSearchBuilder) {
|
||||
return new StringPredicateBuilder(theSearchBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public TokenPredicateBuilder newTokenPredicateBuilder(SearchQueryBuilder theSearchBuilder) {
|
||||
return new TokenPredicateBuilder(theSearchBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public SourcePredicateBuilder newSourcePredicateBuilder(SearchQueryBuilder theSearchBuilder) {
|
||||
return new SourcePredicateBuilder(theSearchBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public UriPredicateBuilder newUriPredicateBuilder(SearchQueryBuilder theSearchBuilder) {
|
||||
return new UriPredicateBuilder(theSearchBuilder);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public SearchQueryExecutor newSearchQueryExecutor(GeneratedSql theGeneratedSql, Integer theMaxResultsToFetch) {
|
||||
return new SearchQueryExecutor(theGeneratedSql, theMaxResultsToFetch);
|
||||
}
|
||||
|
||||
@Bean(name = SEARCH_BUILDER)
|
||||
@Scope("prototype")
|
||||
public ISearchBuilder newSearchBuilder(IDao theDao, String theResourceName, Class<? extends IBaseResource> theResourceType, DaoConfig theDaoConfig) {
|
||||
if (theDaoConfig.isUseLegacySearchBuilder()) {
|
||||
return new LegacySearchBuilder(theDao, theResourceName, theResourceType);
|
||||
}
|
||||
return new SearchBuilder(theDao, theResourceName, theResourceType);
|
||||
}
|
||||
|
||||
@Bean(name = HISTORY_BUILDER)
|
||||
@Scope("prototype")
|
||||
public HistoryBuilder persistedJpaSearchFirstPageBundleProvider(@Nullable String theResourceType, @Nullable Long theResourceId, @Nullable Date theRangeStartInclusive, @Nullable Date theRangeEndInclusive) {
|
||||
public HistoryBuilder newPersistedJpaSearchFirstPageBundleProvider(@Nullable String theResourceType, @Nullable Long theResourceId, @Nullable Date theRangeStartInclusive, @Nullable Date theRangeEndInclusive) {
|
||||
return new HistoryBuilder(theResourceType, theResourceId, theRangeStartInclusive, theRangeEndInclusive);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package ca.uhn.fhir.jpa.config;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
|
||||
public class HibernateDialectProvider {
|
||||
|
||||
@Autowired
|
||||
private LocalContainerEntityManagerFactoryBean myEntityManagerFactory;
|
||||
private Dialect myDialect;
|
||||
|
||||
public Dialect getDialect() {
|
||||
Dialect dialect = myDialect;
|
||||
if (dialect == null) {
|
||||
String dialectClass = (String) myEntityManagerFactory.getJpaPropertyMap().get("hibernate.dialect");
|
||||
dialect = ReflectionUtil.newInstanceOrReturnNull(dialectClass, Dialect.class);
|
||||
Validate.notNull(dialect, "Unable to create instance of class: %s", dialectClass);
|
||||
myDialect = dialect;
|
||||
}
|
||||
return dialect;
|
||||
}
|
||||
|
||||
}
|
|
@ -65,11 +65,11 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import static ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.OO_SEVERITY_ERROR;
|
||||
import static ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.OO_SEVERITY_INFO;
|
||||
|
@ -248,7 +248,8 @@ public abstract class BaseStorageDao {
|
|||
QualifierDetails qualifiedParamName = QualifierDetails.extractQualifiersFromParameterName(nextParamName);
|
||||
RuntimeSearchParam param = searchParams.get(qualifiedParamName.getParamName());
|
||||
if (param == null) {
|
||||
String msg = getContext().getLocalizer().getMessageSanitized(BaseHapiFhirResourceDao.class, "invalidSearchParameter", qualifiedParamName.getParamName(), new TreeSet<>(searchParams.keySet()));
|
||||
Collection<String> validNames = mySearchParamRegistry.getValidSearchParameterNamesIncludingMeta(getResourceName());
|
||||
String msg = getContext().getLocalizer().getMessageSanitized(BaseHapiFhirResourceDao.class, "invalidSearchParameter", qualifiedParamName.getParamName(), getResourceName(), validNames);
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static ca.uhn.fhir.jpa.dao.SearchBuilder.toPredicateArray;
|
||||
import static ca.uhn.fhir.jpa.dao.LegacySearchBuilder.toPredicateArray;
|
||||
|
||||
/**
|
||||
* The HistoryBuilder is responsible for building history queries
|
||||
|
|
|
@ -116,6 +116,7 @@ import java.util.Map;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static ca.uhn.fhir.jpa.search.builder.SearchBuilder.getMaximumPageSize;
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -124,19 +125,10 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
* The SearchBuilder is responsible for actually forming the SQL query that handles
|
||||
* searches for resources
|
||||
*/
|
||||
public class SearchBuilder implements ISearchBuilder {
|
||||
|
||||
/**
|
||||
* See loadResourcesByPid
|
||||
* for an explanation of why we use the constant 800
|
||||
*/
|
||||
// NB: keep public
|
||||
public static final int MAXIMUM_PAGE_SIZE = 800;
|
||||
public static final int MAXIMUM_PAGE_SIZE_FOR_TESTING = 50;
|
||||
public static boolean myUseMaxPageSize50ForTest = false;
|
||||
public class LegacySearchBuilder implements ISearchBuilder {
|
||||
|
||||
private static final List<ResourcePersistentId> EMPTY_LONG_LIST = Collections.unmodifiableList(new ArrayList<>());
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(SearchBuilder.class);
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(LegacySearchBuilder.class);
|
||||
private static final ResourcePersistentId NO_MORE = new ResourcePersistentId(-1L);
|
||||
private final String myResourceName;
|
||||
private final Class<? extends IBaseResource> myResourceType;
|
||||
|
@ -179,24 +171,12 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public SearchBuilder(IDao theDao, String theResourceName, Class<? extends IBaseResource> theResourceType) {
|
||||
public LegacySearchBuilder(IDao theDao, String theResourceName, Class<? extends IBaseResource> theResourceType) {
|
||||
myCallingDao = theDao;
|
||||
myResourceName = theResourceName;
|
||||
myResourceType = theResourceType;
|
||||
}
|
||||
|
||||
public static int getMaximumPageSize() {
|
||||
if (myUseMaxPageSize50ForTest) {
|
||||
return MAXIMUM_PAGE_SIZE_FOR_TESTING;
|
||||
} else {
|
||||
return MAXIMUM_PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setMaxPageSize50ForTest(boolean theIsTest) {
|
||||
myUseMaxPageSize50ForTest = theIsTest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxResultsToFetch(Integer theMaxResultsToFetch) {
|
||||
myMaxResultsToFetch = theMaxResultsToFetch;
|
||||
|
@ -1408,7 +1388,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(theLastUpdated, builder, from);
|
||||
lastUpdatedPredicates.add(from.get("myId").as(Long.class).in(ResourcePersistentId.toLongList(thePids)));
|
||||
|
||||
cq.where(SearchBuilder.toPredicateArray(lastUpdatedPredicates));
|
||||
cq.where(LegacySearchBuilder.toPredicateArray(lastUpdatedPredicates));
|
||||
TypedQuery<Long> query = theEntityManager.createQuery(cq);
|
||||
|
||||
return ResourcePersistentId.fromLongList(query.getResultList());
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.IDao;
|
||||
import ca.uhn.fhir.jpa.config.BaseConfig;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -30,9 +31,11 @@ public class SearchBuilderFactory {
|
|||
|
||||
@Autowired
|
||||
private ApplicationContext myApplicationContext;
|
||||
@Autowired
|
||||
private DaoConfig myDaoConfig;
|
||||
|
||||
public ISearchBuilder newSearchBuilder(IDao theDao, String theResourceName, Class<? extends IBaseResource> theResourceType) {
|
||||
return (ISearchBuilder) myApplicationContext.getBean(BaseConfig.SEARCH_BUILDER, theDao, theResourceName, theResourceType);
|
||||
return (ISearchBuilder) myApplicationContext.getBean(BaseConfig.SEARCH_BUILDER, theDao, theResourceName, theResourceType, myDaoConfig);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.querystack.QueryStack;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.BasePartitionable;
|
||||
|
@ -63,7 +63,7 @@ abstract class BasePredicateBuilder {
|
|||
@Autowired
|
||||
private PartitionSettings myPartitionSettings;
|
||||
|
||||
BasePredicateBuilder(SearchBuilder theSearchBuilder) {
|
||||
BasePredicateBuilder(LegacySearchBuilder theSearchBuilder) {
|
||||
myCriteriaBuilder = theSearchBuilder.getBuilder();
|
||||
myQueryStack = theSearchBuilder.getQueryStack();
|
||||
myResourceType = theSearchBuilder.getResourceType();
|
||||
|
@ -172,7 +172,7 @@ abstract class BasePredicateBuilder {
|
|||
case ENDS_BEFORE:
|
||||
case STARTS_AFTER:
|
||||
default:
|
||||
String msg = myContext.getLocalizer().getMessage(SearchBuilder.class, invalidMessageName, thePrefix.getValue(), theParam.getValueAsQueryToken(myContext));
|
||||
String msg = myContext.getLocalizer().getMessage(LegacySearchBuilder.class, invalidMessageName, thePrefix.getValue(), theParam.getValueAsQueryToken(myContext));
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
|||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||
|
@ -52,7 +52,7 @@ public class PredicateBuilder {
|
|||
private final PredicateBuilderToken myPredicateBuilderToken;
|
||||
private final PredicateBuilderUri myPredicateBuilderUri;
|
||||
|
||||
public PredicateBuilder(SearchBuilder theSearchBuilder, PredicateBuilderFactory thePredicateBuilderFactory) {
|
||||
public PredicateBuilder(LegacySearchBuilder theSearchBuilder, PredicateBuilderFactory thePredicateBuilderFactory) {
|
||||
myPredicateBuilderCoords = thePredicateBuilderFactory.newPredicateBuilderCoords(theSearchBuilder);
|
||||
myPredicateBuilderDate = thePredicateBuilderFactory.newPredicateBuilderDate(theSearchBuilder);
|
||||
myPredicateBuilderNumber = thePredicateBuilderFactory.newPredicateBuilderNumber(theSearchBuilder);
|
||||
|
|
|
@ -22,8 +22,9 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
|||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||
import ca.uhn.fhir.jpa.util.CoordCalculator;
|
||||
import ca.uhn.fhir.jpa.util.SearchBox;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
|
@ -50,7 +51,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
public class PredicateBuilderCoords extends BasePredicateBuilder implements IPredicateBuilder {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(PredicateBuilderCoords.class);
|
||||
|
||||
PredicateBuilderCoords(SearchBuilder theSearchBuilder) {
|
||||
PredicateBuilderCoords(LegacySearchBuilder theSearchBuilder) {
|
||||
super(theSearchBuilder);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
|||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
|
@ -50,7 +50,7 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
|
|||
private static final Logger ourLog = LoggerFactory.getLogger(PredicateBuilderDate.class);
|
||||
|
||||
|
||||
PredicateBuilderDate(SearchBuilder theSearchBuilder) {
|
||||
PredicateBuilderDate(LegacySearchBuilder theSearchBuilder) {
|
||||
super(theSearchBuilder);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,30 +20,30 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import org.springframework.beans.factory.annotation.Lookup;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public abstract class PredicateBuilderFactory {
|
||||
@Lookup
|
||||
public abstract PredicateBuilderCoords newPredicateBuilderCoords(SearchBuilder theSearchBuilder);
|
||||
public abstract PredicateBuilderCoords newPredicateBuilderCoords(LegacySearchBuilder theSearchBuilder);
|
||||
@Lookup
|
||||
public abstract PredicateBuilderDate newPredicateBuilderDate(SearchBuilder theSearchBuilder);
|
||||
public abstract PredicateBuilderDate newPredicateBuilderDate(LegacySearchBuilder theSearchBuilder);
|
||||
@Lookup
|
||||
public abstract PredicateBuilderNumber newPredicateBuilderNumber(SearchBuilder theSearchBuilder);
|
||||
public abstract PredicateBuilderNumber newPredicateBuilderNumber(LegacySearchBuilder theSearchBuilder);
|
||||
@Lookup
|
||||
public abstract PredicateBuilderQuantity newPredicateBuilderQuantity(SearchBuilder theSearchBuilder);
|
||||
public abstract PredicateBuilderQuantity newPredicateBuilderQuantity(LegacySearchBuilder theSearchBuilder);
|
||||
@Lookup
|
||||
public abstract PredicateBuilderReference newPredicateBuilderReference(SearchBuilder theSearchBuilder, PredicateBuilder thePredicateBuilder);
|
||||
public abstract PredicateBuilderReference newPredicateBuilderReference(LegacySearchBuilder theSearchBuilder, PredicateBuilder thePredicateBuilder);
|
||||
@Lookup
|
||||
public abstract PredicateBuilderResourceId newPredicateBuilderResourceId(SearchBuilder theSearchBuilder);
|
||||
public abstract PredicateBuilderResourceId newPredicateBuilderResourceId(LegacySearchBuilder theSearchBuilder);
|
||||
@Lookup
|
||||
public abstract PredicateBuilderString newPredicateBuilderString(SearchBuilder theSearchBuilder);
|
||||
public abstract PredicateBuilderString newPredicateBuilderString(LegacySearchBuilder theSearchBuilder);
|
||||
@Lookup
|
||||
public abstract PredicateBuilderTag newPredicateBuilderTag(SearchBuilder theSearchBuilder);
|
||||
public abstract PredicateBuilderTag newPredicateBuilderTag(LegacySearchBuilder theSearchBuilder);
|
||||
@Lookup
|
||||
public abstract PredicateBuilderToken newPredicateBuilderToken(SearchBuilder theSearchBuilder, PredicateBuilder thePredicateBuilder);
|
||||
public abstract PredicateBuilderToken newPredicateBuilderToken(LegacySearchBuilder theSearchBuilder, PredicateBuilder thePredicateBuilder);
|
||||
@Lookup
|
||||
public abstract PredicateBuilderUri newPredicateBuilderUri(SearchBuilder theSearchBuilder);
|
||||
public abstract PredicateBuilderUri newPredicateBuilderUri(LegacySearchBuilder theSearchBuilder);
|
||||
}
|
||||
|
|
|
@ -22,8 +22,9 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
|||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.NumberParam;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
|
@ -46,7 +47,7 @@ import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
|||
class PredicateBuilderNumber extends BasePredicateBuilder implements IPredicateBuilder {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(PredicateBuilderNumber.class);
|
||||
|
||||
PredicateBuilderNumber(SearchBuilder theSearchBuilder) {
|
||||
PredicateBuilderNumber(LegacySearchBuilder theSearchBuilder) {
|
||||
super(theSearchBuilder);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,10 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
|||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.base.composite.BaseQuantityDt;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
|
@ -47,7 +48,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
@Scope("prototype")
|
||||
class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicateBuilder {
|
||||
|
||||
PredicateBuilderQuantity(SearchBuilder theSearchBuilder) {
|
||||
PredicateBuilderQuantity(LegacySearchBuilder theSearchBuilder) {
|
||||
super(theSearchBuilder);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
|||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IDao;
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity;
|
||||
|
@ -109,6 +109,7 @@ import static org.apache.commons.lang3.StringUtils.trim;
|
|||
|
||||
@Component
|
||||
@Scope("prototype")
|
||||
public
|
||||
class PredicateBuilderReference extends BasePredicateBuilder {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(PredicateBuilderReference.class);
|
||||
private final PredicateBuilder myPredicateBuilder;
|
||||
|
@ -125,7 +126,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
@Autowired
|
||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
|
||||
PredicateBuilderReference(SearchBuilder theSearchBuilder, PredicateBuilder thePredicateBuilder) {
|
||||
PredicateBuilderReference(LegacySearchBuilder theSearchBuilder, PredicateBuilder thePredicateBuilder) {
|
||||
super(theSearchBuilder);
|
||||
myPredicateBuilder = thePredicateBuilder;
|
||||
}
|
||||
|
@ -682,7 +683,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
}
|
||||
|
||||
} else {
|
||||
String validNames = new TreeSet<>(mySearchParamRegistry.getActiveSearchParams(theResourceName).keySet()).toString();
|
||||
Collection<String> validNames = mySearchParamRegistry.getValidSearchParameterNamesIncludingMeta(theResourceName);
|
||||
String msg = myContext.getLocalizer().getMessageSanitized(BaseHapiFhirResourceDao.class, "invalidSearchParameter", theParamName, theResourceName, validNames);
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
@ -881,7 +882,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
assert theOperation == SearchFilterParser.CompareOperation.eq;
|
||||
|
||||
if (myDaoConfig.getStoreMetaSourceInformation() == DaoConfig.StoreMetaSourceInformationEnum.NONE) {
|
||||
String msg = myContext.getLocalizer().getMessage(SearchBuilder.class, "sourceParamDisabled");
|
||||
String msg = myContext.getLocalizer().getMessage(LegacySearchBuilder.class, "sourceParamDisabled");
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||
|
@ -51,7 +51,7 @@ class PredicateBuilderResourceId extends BasePredicateBuilder {
|
|||
@Autowired
|
||||
IdHelperService myIdHelperService;
|
||||
|
||||
PredicateBuilderResourceId(SearchBuilder theSearchBuilder) {
|
||||
PredicateBuilderResourceId(LegacySearchBuilder theSearchBuilder) {
|
||||
super(theSearchBuilder);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,9 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
|||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
|
@ -43,7 +44,7 @@ import java.util.List;
|
|||
@Component
|
||||
@Scope("prototype")
|
||||
class PredicateBuilderString extends BasePredicateBuilder implements IPredicateBuilder {
|
||||
PredicateBuilderString(SearchBuilder theSearchBuilder) {
|
||||
PredicateBuilderString(LegacySearchBuilder theSearchBuilder) {
|
||||
super(theSearchBuilder);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
|
||||
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
|
||||
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
|
||||
|
@ -53,7 +53,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
class PredicateBuilderTag extends BasePredicateBuilder {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(PredicateBuilderTag.class);
|
||||
|
||||
PredicateBuilderTag(SearchBuilder theSearchBuilder) {
|
||||
PredicateBuilderTag(LegacySearchBuilder theSearchBuilder) {
|
||||
super(theSearchBuilder);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ import ca.uhn.fhir.context.BaseRuntimeDeclaredChildDefinition;
|
|||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
||||
|
@ -65,6 +65,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
|
||||
@Component
|
||||
@Scope("prototype")
|
||||
public
|
||||
class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBuilder {
|
||||
private final PredicateBuilder myPredicateBuilder;
|
||||
@Autowired
|
||||
|
@ -72,7 +73,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
|||
@Autowired
|
||||
private ModelConfig myModelConfig;
|
||||
|
||||
PredicateBuilderToken(SearchBuilder theSearchBuilder, PredicateBuilder thePredicateBuilder) {
|
||||
PredicateBuilderToken(LegacySearchBuilder theSearchBuilder, PredicateBuilder thePredicateBuilder) {
|
||||
super(theSearchBuilder);
|
||||
myPredicateBuilder = thePredicateBuilder;
|
||||
}
|
||||
|
@ -105,13 +106,13 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
|||
String msg;
|
||||
if (myModelConfig.isSuppressStringIndexingInTokens()) {
|
||||
msg = myContext.getLocalizer().getMessage(PredicateBuilderToken.class, "textModifierDisabledForServer");
|
||||
}else{
|
||||
} else {
|
||||
msg = myContext.getLocalizer().getMessage(PredicateBuilderToken.class, "textModifierDisabledForSearchParam");
|
||||
}
|
||||
throw new MethodNotAllowedException(msg);
|
||||
}
|
||||
|
||||
myPredicateBuilder.addPredicateString(theResourceName, theSearchParam, theList, theRequestPartitionId);
|
||||
myPredicateBuilder.addPredicateString(theResourceName, theSearchParam, theList, theOperation, theRequestPartitionId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -289,11 +290,11 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
|||
String systemDesc = defaultIfBlank(theSystem, "(missing)");
|
||||
String codeDesc = defaultIfBlank(theCode, "(missing)");
|
||||
if (isBlank(theCode)) {
|
||||
String msg = myContext.getLocalizer().getMessage(SearchBuilder.class, "invalidCodeMissingSystem", theParamName, systemDesc, codeDesc);
|
||||
String msg = myContext.getLocalizer().getMessage(LegacySearchBuilder.class, "invalidCodeMissingSystem", theParamName, systemDesc, codeDesc);
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
if (isBlank(theSystem)) {
|
||||
String msg = myContext.getLocalizer().getMessage(SearchBuilder.class, "invalidCodeMissingCode", theParamName, systemDesc, codeDesc);
|
||||
String msg = myContext.getLocalizer().getMessage(LegacySearchBuilder.class, "invalidCodeMissingCode", theParamName, systemDesc, codeDesc);
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
|||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
|
||||
|
@ -48,7 +48,7 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
|
|||
@Autowired
|
||||
private IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
|
||||
|
||||
PredicateBuilderUri(SearchBuilder theSearchBuilder) {
|
||||
PredicateBuilderUri(LegacySearchBuilder theSearchBuilder) {
|
||||
super(theSearchBuilder);
|
||||
}
|
||||
|
||||
|
|
|
@ -386,7 +386,10 @@ public class SearchFilterParser {
|
|||
ss,
|
||||
sb,
|
||||
in,
|
||||
re
|
||||
re,
|
||||
ap,
|
||||
sa,
|
||||
eb
|
||||
}
|
||||
|
||||
public enum FilterLogicalOperation {
|
||||
|
@ -508,7 +511,7 @@ public class SearchFilterParser {
|
|||
private String FValue;
|
||||
private FilterValueType FValueType;
|
||||
|
||||
FilterParameterPath getParamPath() {
|
||||
public FilterParameterPath getParamPath() {
|
||||
|
||||
return FParamPath;
|
||||
}
|
||||
|
@ -566,7 +569,7 @@ public class SearchFilterParser {
|
|||
private Filter FFilter2;
|
||||
|
||||
|
||||
Filter getFilter1() {
|
||||
public Filter getFilter1() {
|
||||
|
||||
return FFilter1;
|
||||
}
|
||||
|
@ -586,7 +589,7 @@ public class SearchFilterParser {
|
|||
this.FOperation = FOperation;
|
||||
}
|
||||
|
||||
Filter getFilter2() {
|
||||
public Filter getFilter2() {
|
||||
|
||||
return FFilter2;
|
||||
}
|
||||
|
|
|
@ -166,7 +166,7 @@ public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<Se
|
|||
try {
|
||||
theContext.newFluentPath().evaluate(temporaryInstance, nextPath, IBase.class);
|
||||
} catch (Exception e) {
|
||||
String msg = theContext.getLocalizer().getMessage(FhirResourceDaoSearchParameterR4.class, "invalidSearchParamExpression", nextPath, e.getMessage());
|
||||
String msg = theContext.getLocalizer().getMessageSanitized(FhirResourceDaoSearchParameterR4.class, "invalidSearchParamExpression", nextPath, e.getMessage());
|
||||
throw new UnprocessableEntityException(msg, e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ import java.util.Map;
|
|||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.jpa.dao.SearchBuilder.toPredicateArray;
|
||||
import static ca.uhn.fhir.jpa.dao.LegacySearchBuilder.toPredicateArray;
|
||||
import static ca.uhn.fhir.util.StringUtil.toUtf8String;
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
|
|
@ -90,6 +90,7 @@ import org.springframework.transaction.TransactionStatus;
|
|||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
@ -535,8 +536,11 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
* individually for pages as we return them to clients
|
||||
*/
|
||||
final Set<ResourcePersistentId> includedPids = new HashSet<>();
|
||||
if (theParams.getEverythingMode() == null) {
|
||||
includedPids.addAll(theSb.loadIncludes(myContext, myEntityManager, pids, theParams.getIncludes(), false, theParams.getLastUpdated(), "(synchronous)", theRequestDetails));
|
||||
}
|
||||
includedPids.addAll(theSb.loadIncludes(myContext, myEntityManager, pids, theParams.getRevIncludes(), true, theParams.getLastUpdated(), "(synchronous)", theRequestDetails));
|
||||
includedPids.addAll(theSb.loadIncludes(myContext, myEntityManager, pids, theParams.getIncludes(), false, theParams.getLastUpdated(), "(synchronous)", theRequestDetails));
|
||||
|
||||
List<ResourcePersistentId> includedPidsList = new ArrayList<>(includedPids);
|
||||
|
||||
List<IBaseResource> resources = new ArrayList<>();
|
||||
|
@ -945,6 +949,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
// Create an initial search in the DB and give it an ID
|
||||
saveSearch();
|
||||
|
||||
assert !TransactionSynchronizationManager.isActualTransactionActive();
|
||||
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myManagedTxManager);
|
||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,99 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import 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.NotCondition;
|
||||
import com.healthmarketscience.sqlbuilder.UnaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toAndPredicate;
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toEqualToOrInPredicate;
|
||||
|
||||
public abstract class BaseJoiningPredicateBuilder extends BasePredicateBuilder {
|
||||
|
||||
private final DbTable myTable;
|
||||
private final DbColumn myColumnPartitionId;
|
||||
|
||||
BaseJoiningPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder, DbTable theTable) {
|
||||
super(theSearchSqlBuilder);
|
||||
myTable = theTable;
|
||||
myColumnPartitionId = theTable.addColumn("PARTITION_ID");
|
||||
}
|
||||
|
||||
public DbTable getTable() {
|
||||
return myTable;
|
||||
}
|
||||
|
||||
public abstract DbColumn getResourceIdColumn();
|
||||
|
||||
DbColumn getPartitionIdColumn() {
|
||||
return myColumnPartitionId;
|
||||
}
|
||||
|
||||
public Condition combineWithRequestPartitionIdPredicate(RequestPartitionId theRequestPartitionId, Condition theCondition) {
|
||||
Condition partitionIdPredicate = createPartitionIdPredicate(theRequestPartitionId);
|
||||
if (partitionIdPredicate == null) {
|
||||
return theCondition;
|
||||
}
|
||||
return toAndPredicate(partitionIdPredicate, theCondition);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public Condition createPartitionIdPredicate(RequestPartitionId theRequestPartitionId) {
|
||||
if (theRequestPartitionId != null && !theRequestPartitionId.isAllPartitions()) {
|
||||
Condition condition;
|
||||
Integer partitionId = theRequestPartitionId.getPartitionId();
|
||||
if (partitionId != null) {
|
||||
Object placeholder = generatePlaceholder(partitionId);
|
||||
condition = BinaryCondition.equalTo(getPartitionIdColumn(), placeholder);
|
||||
} else {
|
||||
condition = UnaryCondition.isNull(getPartitionIdColumn());
|
||||
}
|
||||
return condition;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Condition createPredicateResourceIds(boolean theInverse, List<Long> theResourceIds) {
|
||||
Validate.notNull(theResourceIds, "theResourceIds must not be null");
|
||||
|
||||
// Handle the _id parameter by adding it to the tail
|
||||
Condition inResourceIds = toEqualToOrInPredicate(getResourceIdColumn(), generatePlaceholders(theResourceIds));
|
||||
if (theInverse) {
|
||||
inResourceIds = new NotCondition(inResourceIds);
|
||||
}
|
||||
return inResourceIds;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
import com.healthmarketscience.sqlbuilder.BinaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class BasePredicateBuilder {
|
||||
|
||||
private final SearchQueryBuilder mySearchSqlBuilder;
|
||||
|
||||
public BasePredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
mySearchSqlBuilder = theSearchSqlBuilder;
|
||||
}
|
||||
|
||||
|
||||
PartitionSettings getPartitionSettings() {
|
||||
return mySearchSqlBuilder.getPartitionSettings();
|
||||
}
|
||||
|
||||
RequestPartitionId getRequestPartitionId() {
|
||||
return mySearchSqlBuilder.getRequestPartitionId();
|
||||
}
|
||||
|
||||
String getResourceType() {
|
||||
return mySearchSqlBuilder.getResourceType();
|
||||
}
|
||||
|
||||
ModelConfig getModelConfig() {
|
||||
return mySearchSqlBuilder.getModelConfig();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
String generatePlaceholder(Object theInput) {
|
||||
return mySearchSqlBuilder.generatePlaceholder(theInput);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
List<String> generatePlaceholders(Collection<?> theValues) {
|
||||
return mySearchSqlBuilder.generatePlaceholders(theValues);
|
||||
}
|
||||
|
||||
protected FhirContext getFhirContext() {
|
||||
return mySearchSqlBuilder.getFhirContext();
|
||||
}
|
||||
|
||||
protected void setMatchNothing() {
|
||||
mySearchSqlBuilder.setMatchNothing();
|
||||
}
|
||||
|
||||
|
||||
protected BinaryCondition createConditionForValueWithComparator(ParamPrefixEnum theComparator, DbColumn theColumn, Object theValue) {
|
||||
return mySearchSqlBuilder.createConditionForValueWithComparator(theComparator, theColumn, theValue);
|
||||
}
|
||||
|
||||
protected BaseJoiningPredicateBuilder getOrCreateQueryRootTable() {
|
||||
return mySearchSqlBuilder.getOrCreateFirstPredicateBuilder();
|
||||
}
|
||||
|
||||
public void addJoin(DbTable theFromTable, DbTable theToTable, DbColumn theFromColumn, DbColumn theToColumn) {
|
||||
mySearchSqlBuilder.addJoin(theFromTable, theToTable, theFromColumn, theToColumn);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import com.healthmarketscience.sqlbuilder.BinaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.ComboCondition;
|
||||
import com.healthmarketscience.sqlbuilder.Condition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toAndPredicate;
|
||||
|
||||
public abstract class BaseSearchParamPredicateBuilder extends BaseJoiningPredicateBuilder {
|
||||
|
||||
private final DbColumn myColumnMissing;
|
||||
private final DbColumn myColumnResType;
|
||||
private final DbColumn myColumnParamName;
|
||||
private final DbColumn myColumnResId;
|
||||
private final DbColumn myColumnHashIdentity;
|
||||
|
||||
public BaseSearchParamPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder, DbTable theTable) {
|
||||
super(theSearchSqlBuilder, theTable);
|
||||
|
||||
myColumnResId = getTable().addColumn("RES_ID");
|
||||
myColumnMissing = theTable.addColumn("SP_MISSING");
|
||||
myColumnResType = theTable.addColumn("RES_TYPE");
|
||||
myColumnParamName = theTable.addColumn("SP_NAME");
|
||||
myColumnHashIdentity = theTable.addColumn("HASH_IDENTITY");
|
||||
}
|
||||
|
||||
public DbColumn getColumnHashIdentity() {
|
||||
return myColumnHashIdentity;
|
||||
}
|
||||
|
||||
public DbColumn getResourceTypeColumn() {
|
||||
return myColumnResType;
|
||||
}
|
||||
|
||||
public DbColumn getColumnParamName() {
|
||||
return myColumnParamName;
|
||||
}
|
||||
|
||||
public DbColumn getMissingColumn() {
|
||||
return myColumnMissing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DbColumn getResourceIdColumn() {
|
||||
return myColumnResId;
|
||||
}
|
||||
|
||||
public Condition combineWithHashIdentityPredicate(String theResourceName, String theParamName, Condition thePredicate) {
|
||||
List<Condition> andPredicates = new ArrayList<>();
|
||||
|
||||
Condition hashIdentityPredicate = createHashIdentityPredicate(theResourceName, theParamName);
|
||||
andPredicates.add(hashIdentityPredicate);
|
||||
andPredicates.add(thePredicate);
|
||||
|
||||
return toAndPredicate(andPredicates);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Condition createHashIdentityPredicate(String theResourceType, String theParamName) {
|
||||
long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(getPartitionSettings(), getRequestPartitionId(), theResourceType, theParamName);
|
||||
String hashIdentityVal = generatePlaceholder(hashIdentity);
|
||||
return BinaryCondition.equalTo(myColumnHashIdentity, hashIdentityVal);
|
||||
}
|
||||
|
||||
public Condition createPredicateParamMissingForNonReference(String theResourceName, String theParamName, Boolean theMissing, RequestPartitionId theRequestPartitionId) {
|
||||
ComboCondition condition = ComboCondition.and(
|
||||
BinaryCondition.equalTo(getResourceTypeColumn(), generatePlaceholder(theResourceName)),
|
||||
BinaryCondition.equalTo(getColumnParamName(), generatePlaceholder(theParamName)),
|
||||
BinaryCondition.equalTo(getMissingColumn(), generatePlaceholder(theMissing))
|
||||
);
|
||||
return combineWithRequestPartitionIdPredicate(theRequestPartitionId, condition);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import 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 CompositeUniqueSearchParameterPredicateBuilder extends BaseSearchParamPredicateBuilder {
|
||||
|
||||
private final DbColumn myColumnString;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public CompositeUniqueSearchParameterPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_IDX_CMP_STRING_UNIQ"));
|
||||
|
||||
myColumnString = getTable().addColumn("IDX_STRING");
|
||||
}
|
||||
|
||||
|
||||
public Condition createPredicateIndexString(RequestPartitionId theRequestPartitionId, String theIndexString) {
|
||||
BinaryCondition predicate = BinaryCondition.equalTo(myColumnString, generatePlaceholder(theIndexString));
|
||||
return combineWithRequestPartitionIdPredicate(theRequestPartitionId, predicate);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.util.CoordCalculator;
|
||||
import ca.uhn.fhir.jpa.util.SearchBox;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Location;
|
||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||
import ca.uhn.fhir.rest.param.SpecialParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import com.healthmarketscience.sqlbuilder.BinaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.ComboCondition;
|
||||
import com.healthmarketscience.sqlbuilder.Condition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
public class CoordsPredicateBuilder extends BaseSearchParamPredicateBuilder {
|
||||
|
||||
private final DbColumn myColumnLatitude;
|
||||
private final DbColumn myColumnLongitude;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public CoordsPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_SPIDX_COORDS"));
|
||||
|
||||
myColumnLatitude = getTable().addColumn("SP_LATITUDE");
|
||||
myColumnLongitude = getTable().addColumn("SP_LONGITUDE");
|
||||
}
|
||||
|
||||
|
||||
public Condition createPredicateCoords(SearchParameterMap theParams,
|
||||
IQueryParameterType theParam,
|
||||
String theResourceName,
|
||||
RuntimeSearchParam theSearchParam,
|
||||
CoordsPredicateBuilder theFrom,
|
||||
RequestPartitionId theRequestPartitionId) {
|
||||
String latitudeValue;
|
||||
String longitudeValue;
|
||||
double distanceKm = 0.0;
|
||||
|
||||
if (theParam instanceof TokenParam) { // DSTU3
|
||||
TokenParam param = (TokenParam) theParam;
|
||||
String value = param.getValue();
|
||||
String[] parts = value.split(":");
|
||||
if (parts.length != 2) {
|
||||
throw new IllegalArgumentException("Invalid position format '" + value + "'. Required format is 'latitude:longitude'");
|
||||
}
|
||||
latitudeValue = parts[0];
|
||||
longitudeValue = parts[1];
|
||||
if (isBlank(latitudeValue) || isBlank(longitudeValue)) {
|
||||
throw new IllegalArgumentException("Invalid position format '" + value + "'. Both latitude and longitude must be provided.");
|
||||
}
|
||||
QuantityParam distanceParam = theParams.getNearDistanceParam();
|
||||
if (distanceParam != null) {
|
||||
distanceKm = distanceParam.getValue().doubleValue();
|
||||
}
|
||||
} else if (theParam instanceof SpecialParam) { // R4
|
||||
SpecialParam param = (SpecialParam) theParam;
|
||||
String value = param.getValue();
|
||||
String[] parts = value.split("\\|");
|
||||
if (parts.length < 2 || parts.length > 4) {
|
||||
throw new IllegalArgumentException("Invalid position format '" + value + "'. Required format is 'latitude|longitude' or 'latitude|longitude|distance' or 'latitude|longitude|distance|units'");
|
||||
}
|
||||
latitudeValue = parts[0];
|
||||
longitudeValue = parts[1];
|
||||
if (isBlank(latitudeValue) || isBlank(longitudeValue)) {
|
||||
throw new IllegalArgumentException("Invalid position format '" + value + "'. Both latitude and longitude must be provided.");
|
||||
}
|
||||
if (parts.length >= 3) {
|
||||
String distanceString = parts[2];
|
||||
if (!isBlank(distanceString)) {
|
||||
distanceKm = Double.parseDouble(distanceString);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid position type: " + theParam.getClass());
|
||||
}
|
||||
|
||||
Condition latitudePredicate;
|
||||
Condition longitudePredicate;
|
||||
if (distanceKm == 0.0) {
|
||||
latitudePredicate = theFrom.createPredicateLatitudeExact(latitudeValue);
|
||||
longitudePredicate = theFrom.createPredicateLongitudeExact(longitudeValue);
|
||||
} else if (distanceKm < 0.0) {
|
||||
throw new IllegalArgumentException("Invalid " + Location.SP_NEAR_DISTANCE + " parameter '" + distanceKm + "' must be >= 0.0");
|
||||
} else if (distanceKm > CoordCalculator.MAX_SUPPORTED_DISTANCE_KM) {
|
||||
throw new IllegalArgumentException("Invalid " + Location.SP_NEAR_DISTANCE + " parameter '" + distanceKm + "' must be <= " + CoordCalculator.MAX_SUPPORTED_DISTANCE_KM);
|
||||
} else {
|
||||
double latitudeDegrees = Double.parseDouble(latitudeValue);
|
||||
double longitudeDegrees = Double.parseDouble(longitudeValue);
|
||||
|
||||
SearchBox box = CoordCalculator.getBox(latitudeDegrees, longitudeDegrees, distanceKm);
|
||||
latitudePredicate = theFrom.createLatitudePredicateFromBox(box);
|
||||
longitudePredicate = theFrom.createLongitudePredicateFromBox(box);
|
||||
}
|
||||
ComboCondition singleCode = ComboCondition.and(latitudePredicate, longitudePredicate);
|
||||
return combineWithHashIdentityPredicate(theResourceName, theSearchParam.getName(), singleCode);
|
||||
}
|
||||
|
||||
|
||||
public Condition createPredicateLatitudeExact(String theLatitudeValue) {
|
||||
return BinaryCondition.equalTo(myColumnLatitude, generatePlaceholder(theLatitudeValue));
|
||||
}
|
||||
|
||||
public Condition createPredicateLongitudeExact(String theLongitudeValue) {
|
||||
return BinaryCondition.equalTo(myColumnLongitude, generatePlaceholder(theLongitudeValue));
|
||||
}
|
||||
|
||||
public Condition createLatitudePredicateFromBox(SearchBox theBox) {
|
||||
return ComboCondition.and(
|
||||
BinaryCondition.greaterThanOrEq(myColumnLatitude, generatePlaceholder(theBox.getSouthWest().getLatitude())),
|
||||
BinaryCondition.lessThanOrEq(myColumnLatitude, generatePlaceholder(theBox.getNorthEast().getLatitude()))
|
||||
);
|
||||
}
|
||||
|
||||
public Condition createLongitudePredicateFromBox(SearchBox theBox) {
|
||||
if (theBox.crossesAntiMeridian()) {
|
||||
return ComboCondition.or(
|
||||
BinaryCondition.greaterThanOrEq(myColumnLongitude, generatePlaceholder(theBox.getNorthEast().getLongitude())),
|
||||
BinaryCondition.lessThanOrEq(myColumnLongitude, generatePlaceholder(theBox.getSouthWest().getLongitude()))
|
||||
);
|
||||
}
|
||||
return ComboCondition.and(
|
||||
BinaryCondition.greaterThanOrEq(myColumnLongitude, generatePlaceholder(theBox.getSouthWest().getLongitude())),
|
||||
BinaryCondition.lessThanOrEq(myColumnLongitude, generatePlaceholder(theBox.getNorthEast().getLongitude()))
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import com.healthmarketscience.sqlbuilder.ComboCondition;
|
||||
import com.healthmarketscience.sqlbuilder.Condition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class DatePredicateBuilder extends BaseSearchParamPredicateBuilder {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(DatePredicateBuilder.class);
|
||||
private final DbColumn myColumnValueHigh;
|
||||
private final DbColumn myColumnValueLow;
|
||||
private final DbColumn myColumnValueLowDateOrdinal;
|
||||
private final DbColumn myColumnValueHighDateOrdinal;
|
||||
|
||||
@Autowired
|
||||
private DaoConfig myDaoConfig;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public DatePredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_SPIDX_DATE"));
|
||||
|
||||
myColumnValueLow = getTable().addColumn("SP_VALUE_LOW");
|
||||
myColumnValueHigh = getTable().addColumn("SP_VALUE_HIGH");
|
||||
myColumnValueLowDateOrdinal = getTable().addColumn("SP_VALUE_LOW_DATE_ORDINAL");
|
||||
myColumnValueHighDateOrdinal = getTable().addColumn("SP_VALUE_HIGH_DATE_ORDINAL");
|
||||
}
|
||||
|
||||
|
||||
public Condition createPredicateDateWithoutIdentityPredicate(IQueryParameterType theParam,
|
||||
String theResourceName,
|
||||
String theParamName,
|
||||
DatePredicateBuilder theFrom,
|
||||
SearchFilterParser.CompareOperation theOperation,
|
||||
RequestPartitionId theRequestPartitionId) {
|
||||
|
||||
Condition p;
|
||||
if (theParam instanceof DateParam) {
|
||||
DateParam date = (DateParam) theParam;
|
||||
if (!date.isEmpty()) {
|
||||
DateRangeParam range = new DateRangeParam(date);
|
||||
p = createPredicateDateFromRange(theFrom, range, theOperation);
|
||||
} else {
|
||||
// TODO: handle missing date param?
|
||||
p = null;
|
||||
}
|
||||
} else if (theParam instanceof DateRangeParam) {
|
||||
DateRangeParam range = (DateRangeParam) theParam;
|
||||
p = createPredicateDateFromRange(
|
||||
theFrom,
|
||||
range,
|
||||
theOperation);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid token type: " + theParam.getClass());
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private Condition createPredicateDateFromRange(DatePredicateBuilder theFrom,
|
||||
DateRangeParam theRange,
|
||||
SearchFilterParser.CompareOperation theOperation) {
|
||||
Date lowerBoundInstant = theRange.getLowerBoundAsInstant();
|
||||
Date upperBoundInstant = theRange.getUpperBoundAsInstant();
|
||||
|
||||
DateParam lowerBound = theRange.getLowerBound();
|
||||
DateParam upperBound = theRange.getUpperBound();
|
||||
Integer lowerBoundAsOrdinal = theRange.getLowerBoundAsDateInteger();
|
||||
Integer upperBoundAsOrdinal = theRange.getUpperBoundAsDateInteger();
|
||||
Comparable genericLowerBound;
|
||||
Comparable genericUpperBound;
|
||||
/**
|
||||
* If all present search parameters are of DAY precision, and {@link ca.uhn.fhir.jpa.model.entity.ModelConfig#getUseOrdinalDatesForDayPrecisionSearches()} is true,
|
||||
* then we attempt to use the ordinal field for date comparisons instead of the date field.
|
||||
*/
|
||||
boolean isOrdinalComparison = isNullOrDayPrecision(lowerBound) && isNullOrDayPrecision(upperBound) && myDaoConfig.getModelConfig().getUseOrdinalDatesForDayPrecisionSearches();
|
||||
|
||||
Condition lt;
|
||||
Condition gt = null;
|
||||
Condition lb = null;
|
||||
Condition ub = null;
|
||||
DatePredicateBuilder.ColumnEnum lowValueField;
|
||||
DatePredicateBuilder.ColumnEnum highValueField;
|
||||
|
||||
if (isOrdinalComparison) {
|
||||
lowValueField = DatePredicateBuilder.ColumnEnum.LOW_DATE_ORDINAL;
|
||||
highValueField = DatePredicateBuilder.ColumnEnum.HIGH_DATE_ORDINAL;
|
||||
genericLowerBound = lowerBoundAsOrdinal;
|
||||
genericUpperBound = upperBoundAsOrdinal;
|
||||
} else {
|
||||
lowValueField = DatePredicateBuilder.ColumnEnum.LOW;
|
||||
highValueField = DatePredicateBuilder.ColumnEnum.HIGH;
|
||||
genericLowerBound = lowerBoundInstant;
|
||||
genericUpperBound = upperBoundInstant;
|
||||
}
|
||||
|
||||
if (theOperation == SearchFilterParser.CompareOperation.lt) {
|
||||
if (lowerBoundInstant == null) {
|
||||
throw new InvalidRequestException("lowerBound value not correctly specified for compare theOperation");
|
||||
}
|
||||
//im like 80% sure this should be ub and not lb, as it is an UPPER bound.
|
||||
lb = theFrom.createPredicate(lowValueField, ParamPrefixEnum.LESSTHAN, genericLowerBound);
|
||||
} else if (theOperation == SearchFilterParser.CompareOperation.le) {
|
||||
if (upperBoundInstant == null) {
|
||||
throw new InvalidRequestException("upperBound value not correctly specified for compare theOperation");
|
||||
}
|
||||
//im like 80% sure this should be ub and not lb, as it is an UPPER bound.
|
||||
lb = theFrom.createPredicate(highValueField, ParamPrefixEnum.LESSTHAN_OR_EQUALS, genericUpperBound);
|
||||
} else if (theOperation == SearchFilterParser.CompareOperation.gt) {
|
||||
if (upperBoundInstant == null) {
|
||||
throw new InvalidRequestException("upperBound value not correctly specified for compare theOperation");
|
||||
}
|
||||
lb = theFrom.createPredicate(highValueField, ParamPrefixEnum.GREATERTHAN, genericUpperBound);
|
||||
} else if (theOperation == SearchFilterParser.CompareOperation.ge) {
|
||||
if (lowerBoundInstant == null) {
|
||||
throw new InvalidRequestException("lowerBound value not correctly specified for compare theOperation");
|
||||
}
|
||||
lb = theFrom.createPredicate(lowValueField, ParamPrefixEnum.GREATERTHAN_OR_EQUALS, genericLowerBound);
|
||||
} else if (theOperation == SearchFilterParser.CompareOperation.ne) {
|
||||
if ((lowerBoundInstant == null) ||
|
||||
(upperBoundInstant == null)) {
|
||||
throw new InvalidRequestException("lowerBound and/or upperBound value not correctly specified for compare theOperation");
|
||||
}
|
||||
lt = theFrom.createPredicate(lowValueField, ParamPrefixEnum.LESSTHAN, genericLowerBound);
|
||||
gt = theFrom.createPredicate(highValueField, ParamPrefixEnum.GREATERTHAN, genericUpperBound);
|
||||
lb = ComboCondition.or(lt, gt);
|
||||
} else if ((theOperation == SearchFilterParser.CompareOperation.eq) || (theOperation == null)) {
|
||||
if (lowerBoundInstant != null) {
|
||||
gt = theFrom.createPredicate(lowValueField, ParamPrefixEnum.GREATERTHAN_OR_EQUALS, genericLowerBound);
|
||||
lt = theFrom.createPredicate(highValueField, ParamPrefixEnum.GREATERTHAN_OR_EQUALS, genericLowerBound);
|
||||
|
||||
if (lowerBound.getPrefix() == ParamPrefixEnum.STARTS_AFTER || lowerBound.getPrefix() == ParamPrefixEnum.EQUAL) {
|
||||
lb = gt;
|
||||
} else {
|
||||
lb = ComboCondition.or(gt, lt);
|
||||
}
|
||||
}
|
||||
|
||||
if (upperBoundInstant != null) {
|
||||
gt = theFrom.createPredicate(lowValueField, ParamPrefixEnum.LESSTHAN_OR_EQUALS, genericUpperBound);
|
||||
lt = theFrom.createPredicate(highValueField, ParamPrefixEnum.LESSTHAN_OR_EQUALS, genericUpperBound);
|
||||
|
||||
|
||||
if (theRange.getUpperBound().getPrefix() == ParamPrefixEnum.ENDS_BEFORE || theRange.getUpperBound().getPrefix() == ParamPrefixEnum.EQUAL) {
|
||||
ub = lt;
|
||||
} else {
|
||||
ub = ComboCondition.or(gt, lt);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new InvalidRequestException(String.format("Unsupported operator specified, operator=%s",
|
||||
theOperation.name()));
|
||||
}
|
||||
if (isOrdinalComparison) {
|
||||
ourLog.trace("Ordinal date range is {} - {} ", lowerBoundAsOrdinal, upperBoundAsOrdinal);
|
||||
} else {
|
||||
ourLog.trace("Date range is {} - {}", lowerBoundInstant, upperBoundInstant);
|
||||
}
|
||||
|
||||
if (lb != null && ub != null) {
|
||||
return (ComboCondition.and(lb, ub));
|
||||
} else if (lb != null) {
|
||||
return (lb);
|
||||
} else {
|
||||
return (ub);
|
||||
}
|
||||
}
|
||||
|
||||
public DbColumn getColumnValueLow() {
|
||||
return myColumnValueLow;
|
||||
}
|
||||
|
||||
private boolean isNullOrDayPrecision(DateParam theDateParam) {
|
||||
return theDateParam == null || theDateParam.getPrecision().ordinal() == TemporalPrecisionEnum.DAY.ordinal();
|
||||
}
|
||||
|
||||
private Condition createPredicate(ColumnEnum theColumn, ParamPrefixEnum theComparator, Object theValue) {
|
||||
|
||||
DbColumn column;
|
||||
switch (theColumn) {
|
||||
case LOW:
|
||||
column = myColumnValueLow;
|
||||
break;
|
||||
case LOW_DATE_ORDINAL:
|
||||
column = myColumnValueLowDateOrdinal;
|
||||
break;
|
||||
case HIGH:
|
||||
column = myColumnValueHigh;
|
||||
break;
|
||||
case HIGH_DATE_ORDINAL:
|
||||
column = myColumnValueHighDateOrdinal;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
return createConditionForValueWithComparator(theComparator, column, theValue);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public enum ColumnEnum {
|
||||
|
||||
LOW,
|
||||
LOW_DATE_ORDINAL,
|
||||
HIGH,
|
||||
HIGH_DATE_ORDINAL
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ForcedIdPredicateBuilder extends BaseJoiningPredicateBuilder {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(ForcedIdPredicateBuilder.class);
|
||||
private final DbColumn myColumnResourceId;
|
||||
private final DbColumn myColumnForcedId;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ForcedIdPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_FORCED_ID"));
|
||||
|
||||
myColumnResourceId = getTable().addColumn("RESOURCE_PID");
|
||||
myColumnForcedId = getTable().addColumn("FORCED_ID");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DbColumn getResourceIdColumn() {
|
||||
return myColumnResourceId;
|
||||
}
|
||||
|
||||
|
||||
public DbColumn getColumnForcedId() {
|
||||
return myColumnForcedId;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.SearchFuzzUtil;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import com.healthmarketscience.sqlbuilder.BinaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.ComboCondition;
|
||||
import com.healthmarketscience.sqlbuilder.Condition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
|
||||
public class NumberPredicateBuilder extends BaseSearchParamPredicateBuilder {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(NumberPredicateBuilder.class);
|
||||
private final DbColumn myColumnValue;
|
||||
@Autowired
|
||||
private FhirContext myFhirContext;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public NumberPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_SPIDX_NUMBER"));
|
||||
|
||||
myColumnValue = getTable().addColumn("SP_VALUE");
|
||||
}
|
||||
|
||||
public Condition createPredicateNumeric(String theResourceName, String theParamName, SearchFilterParser.CompareOperation theOperation, BigDecimal theValue, RequestPartitionId theRequestPartitionId, IQueryParameterType theActualParam) {
|
||||
Condition numericPredicate = createPredicateNumeric(this, theOperation, theValue, myColumnValue, "invalidNumberPrefix", myFhirContext, theActualParam);
|
||||
return combineWithHashIdentityPredicate(theResourceName, theParamName, numericPredicate);
|
||||
}
|
||||
|
||||
public DbColumn getColumnValue() {
|
||||
return myColumnValue;
|
||||
}
|
||||
|
||||
|
||||
static Condition createPredicateNumeric(BaseSearchParamPredicateBuilder theIndexTable, SearchFilterParser.CompareOperation theOperation, BigDecimal theValue, DbColumn theColumn, String theInvalidValueKey, FhirContext theFhirContext, IQueryParameterType theActualParam) {
|
||||
Condition num;
|
||||
|
||||
// Per discussions with Grahame Grieve and James Agnew on 11/13/19, modified logic for EQUAL and NOT_EQUAL operators below so as to
|
||||
// use exact value matching. The "fuzz amount" matching is still used with the APPROXIMATE operator.
|
||||
SearchFilterParser.CompareOperation operation = defaultIfNull(theOperation, SearchFilterParser.CompareOperation.eq);
|
||||
switch (operation) {
|
||||
case gt:
|
||||
num = BinaryCondition.greaterThan(theColumn, theIndexTable.generatePlaceholder(theValue));
|
||||
break;
|
||||
case ge:
|
||||
num = BinaryCondition.greaterThanOrEq(theColumn, theIndexTable.generatePlaceholder(theValue));
|
||||
break;
|
||||
case lt:
|
||||
num = BinaryCondition.lessThan(theColumn, theIndexTable.generatePlaceholder(theValue));
|
||||
break;
|
||||
case le:
|
||||
num = BinaryCondition.lessThanOrEq(theColumn, theIndexTable.generatePlaceholder(theValue));
|
||||
break;
|
||||
case eq:
|
||||
num = BinaryCondition.equalTo(theColumn, theIndexTable.generatePlaceholder(theValue));
|
||||
break;
|
||||
case ne:
|
||||
num = BinaryCondition.notEqualTo(theColumn, theIndexTable.generatePlaceholder(theValue));
|
||||
break;
|
||||
case ap:
|
||||
BigDecimal mul = SearchFuzzUtil.calculateFuzzAmount(ParamPrefixEnum.APPROXIMATE, theValue);
|
||||
BigDecimal low = theValue.subtract(mul, MathContext.DECIMAL64);
|
||||
BigDecimal high = theValue.add(mul, MathContext.DECIMAL64);
|
||||
Condition lowPred = BinaryCondition.greaterThanOrEq(theColumn, theIndexTable.generatePlaceholder(low));
|
||||
Condition highPred = BinaryCondition.lessThanOrEq(theColumn, theIndexTable.generatePlaceholder(high));
|
||||
num = ComboCondition.and(lowPred, highPred);
|
||||
ourLog.trace("Searching for {} <= val <= {}", low, high);
|
||||
break;
|
||||
default:
|
||||
String paramValue = theActualParam.getValueAsQueryToken(theFhirContext);
|
||||
String msg = theIndexTable.getFhirContext().getLocalizer().getMessage(LegacySearchBuilder.class, theInvalidValueKey, operation, paramValue);
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
|
||||
import ca.uhn.fhir.jpa.search.builder.QueryStack;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.base.composite.BaseQuantityDt;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||
import com.healthmarketscience.sqlbuilder.BinaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.ComboCondition;
|
||||
import com.healthmarketscience.sqlbuilder.Condition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
public class QuantityPredicateBuilder extends BaseSearchParamPredicateBuilder {
|
||||
|
||||
private final DbColumn myColumnHashIdentitySystemUnits;
|
||||
private final DbColumn myColumnHashIdentityUnits;
|
||||
private final DbColumn myColumnValue;
|
||||
@Autowired
|
||||
private FhirContext myFhirContext;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public QuantityPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_SPIDX_QUANTITY"));
|
||||
|
||||
myColumnHashIdentitySystemUnits = getTable().addColumn("HASH_IDENTITY_SYS_UNITS");
|
||||
myColumnHashIdentityUnits = getTable().addColumn("HASH_IDENTITY_AND_UNITS");
|
||||
myColumnValue = getTable().addColumn("SP_VALUE");
|
||||
}
|
||||
|
||||
|
||||
public Condition createPredicateQuantity(IQueryParameterType theParam, String theResourceName, String theParamName, CriteriaBuilder theBuilder, QuantityPredicateBuilder theFrom, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
|
||||
|
||||
String systemValue;
|
||||
String unitsValue;
|
||||
ParamPrefixEnum cmpValue;
|
||||
BigDecimal valueValue;
|
||||
|
||||
if (theParam instanceof BaseQuantityDt) {
|
||||
BaseQuantityDt param = (BaseQuantityDt) theParam;
|
||||
systemValue = param.getSystemElement().getValueAsString();
|
||||
unitsValue = param.getUnitsElement().getValueAsString();
|
||||
cmpValue = ParamPrefixEnum.forValue(param.getComparatorElement().getValueAsString());
|
||||
valueValue = param.getValueElement().getValue();
|
||||
} else if (theParam instanceof QuantityParam) {
|
||||
QuantityParam param = (QuantityParam) theParam;
|
||||
systemValue = param.getSystem();
|
||||
unitsValue = param.getUnits();
|
||||
cmpValue = param.getPrefix();
|
||||
valueValue = param.getValue();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid quantity type: " + theParam.getClass());
|
||||
}
|
||||
|
||||
Condition hashPredicate;
|
||||
if (!isBlank(systemValue) && !isBlank(unitsValue)) {
|
||||
long hash = ResourceIndexedSearchParamQuantity.calculateHashSystemAndUnits(getPartitionSettings(), theRequestPartitionId, theResourceName, theParamName, systemValue, unitsValue);
|
||||
hashPredicate = BinaryCondition.equalTo(myColumnHashIdentitySystemUnits, generatePlaceholder(hash));
|
||||
} else if (!isBlank(unitsValue)) {
|
||||
long hash = ResourceIndexedSearchParamQuantity.calculateHashUnits(getPartitionSettings(), theRequestPartitionId, theResourceName, theParamName, unitsValue);
|
||||
hashPredicate = BinaryCondition.equalTo(myColumnHashIdentityUnits, generatePlaceholder(hash));
|
||||
} else {
|
||||
long hash = BaseResourceIndexedSearchParam.calculateHashIdentity(getPartitionSettings(), theRequestPartitionId, theResourceName, theParamName);
|
||||
hashPredicate = BinaryCondition.equalTo(getColumnHashIdentity(), generatePlaceholder(hash));
|
||||
}
|
||||
|
||||
SearchFilterParser.CompareOperation operation = theOperation;
|
||||
if (operation == null && cmpValue != null) {
|
||||
operation = QueryStack.toOperation(cmpValue);
|
||||
}
|
||||
operation = defaultIfNull(operation, SearchFilterParser.CompareOperation.eq);
|
||||
Condition numericPredicate = NumberPredicateBuilder.createPredicateNumeric(this, operation, valueValue, myColumnValue, "invalidQuantityPrefix", myFhirContext, theParam);
|
||||
|
||||
return ComboCondition.and(hashPredicate, numericPredicate);
|
||||
}
|
||||
|
||||
|
||||
public DbColumn getColumnValue() {
|
||||
return myColumnValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
|
||||
import ca.uhn.fhir.jpa.search.builder.QueryStack;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import com.healthmarketscience.sqlbuilder.Condition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class ResourceIdPredicateBuilder extends BasePredicateBuilder {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(ResourceIdPredicateBuilder.class);
|
||||
|
||||
@Autowired
|
||||
private IdHelperService myIdHelperService;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResourceIdPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
super(theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public Condition createPredicateResourceId(@Nullable DbColumn theSourceJoinColumn, String theResourceName, List<List<IQueryParameterType>> theValues, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
|
||||
|
||||
Set<ResourcePersistentId> allOrPids = null;
|
||||
SearchFilterParser.CompareOperation defaultOperation = SearchFilterParser.CompareOperation.eq;
|
||||
|
||||
for (List<? extends IQueryParameterType> nextValue : theValues) {
|
||||
Set<ResourcePersistentId> orPids = new HashSet<>();
|
||||
boolean haveValue = false;
|
||||
for (IQueryParameterType next : nextValue) {
|
||||
String value = next.getValueAsQueryToken(getFhirContext());
|
||||
if (value != null && value.startsWith("|")) {
|
||||
value = value.substring(1);
|
||||
}
|
||||
|
||||
IdType valueAsId = new IdType(value);
|
||||
if (isNotBlank(value)) {
|
||||
haveValue = true;
|
||||
try {
|
||||
ResourcePersistentId pid = myIdHelperService.resolveResourcePersistentIds(theRequestPartitionId, theResourceName, valueAsId.getIdPart());
|
||||
orPids.add(pid);
|
||||
} catch (ResourceNotFoundException e) {
|
||||
// This is not an error in a search, it just results in no matches
|
||||
ourLog.debug("Resource ID {} was requested but does not exist", valueAsId.getIdPart());
|
||||
}
|
||||
}
|
||||
|
||||
if (next instanceof TokenParam) {
|
||||
if (((TokenParam) next).getModifier() == TokenParamModifier.NOT) {
|
||||
defaultOperation = SearchFilterParser.CompareOperation.ne;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (haveValue) {
|
||||
if (allOrPids == null) {
|
||||
allOrPids = orPids;
|
||||
} else {
|
||||
allOrPids.retainAll(orPids);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (allOrPids != null && allOrPids.isEmpty()) {
|
||||
|
||||
setMatchNothing();
|
||||
|
||||
} else if (allOrPids != null) {
|
||||
|
||||
SearchFilterParser.CompareOperation operation = defaultIfNull(theOperation, defaultOperation);
|
||||
assert operation == SearchFilterParser.CompareOperation.eq || operation == SearchFilterParser.CompareOperation.ne;
|
||||
|
||||
List<Long> resourceIds = ResourcePersistentId.toLongList(allOrPids);
|
||||
if (theSourceJoinColumn == null) {
|
||||
BaseJoiningPredicateBuilder queryRootTable = super.getOrCreateQueryRootTable();
|
||||
switch (operation) {
|
||||
default:
|
||||
case eq:
|
||||
return queryRootTable.createPredicateResourceIds(false, resourceIds);
|
||||
case ne:
|
||||
return queryRootTable.createPredicateResourceIds(true, resourceIds);
|
||||
}
|
||||
} else {
|
||||
return QueryStack.toEqualToOrInPredicate(theSourceJoinColumn, generatePlaceholders(resourceIds), operation == SearchFilterParser.CompareOperation.ne);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,614 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
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.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IDao;
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderReference;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
|
||||
import ca.uhn.fhir.jpa.search.builder.QueryStack;
|
||||
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||
import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
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.param.CompositeParam;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.NumberParam;
|
||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
import ca.uhn.fhir.rest.param.SpecialParam;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.healthmarketscience.sqlbuilder.BinaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.ComboCondition;
|
||||
import com.healthmarketscience.sqlbuilder.Condition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toAndPredicate;
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toEqualToOrInPredicate;
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toOrPredicate;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.trim;
|
||||
|
||||
public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(ResourceLinkPredicateBuilder.class);
|
||||
private final DbColumn myColumnSrcType;
|
||||
private final DbColumn myColumnSrcPath;
|
||||
private final DbColumn myColumnTargetResourceId;
|
||||
private final DbColumn myColumnTargetResourceUrl;
|
||||
private final DbColumn myColumnSrcResourceId;
|
||||
private final DbColumn myColumnTargetResourceType;
|
||||
private final QueryStack myQueryStack;
|
||||
private final boolean myReversed;
|
||||
|
||||
@Autowired
|
||||
private DaoConfig myDaoConfig;
|
||||
@Autowired
|
||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
@Autowired
|
||||
private ISearchParamRegistry mySearchParamRegistry;
|
||||
@Autowired
|
||||
private IdHelperService myIdHelperService;
|
||||
@Autowired
|
||||
private DaoRegistry myDaoRegistry;
|
||||
@Autowired
|
||||
private MatchUrlService myMatchUrlService;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResourceLinkPredicateBuilder(QueryStack theQueryStack, SearchQueryBuilder theSearchSqlBuilder, boolean theReversed) {
|
||||
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_RES_LINK"));
|
||||
myColumnSrcResourceId = getTable().addColumn("SRC_RESOURCE_ID");
|
||||
myColumnSrcType = getTable().addColumn("SOURCE_RESOURCE_TYPE");
|
||||
myColumnSrcPath = getTable().addColumn("SRC_PATH");
|
||||
myColumnTargetResourceId = getTable().addColumn("TARGET_RESOURCE_ID");
|
||||
myColumnTargetResourceUrl = getTable().addColumn("TARGET_RESOURCE_URL");
|
||||
myColumnTargetResourceType = getTable().addColumn("TARGET_RESOURCE_TYPE");
|
||||
|
||||
myReversed = theReversed;
|
||||
myQueryStack = theQueryStack;
|
||||
}
|
||||
|
||||
public DbColumn getColumnSourcePath() {
|
||||
return myColumnSrcPath;
|
||||
}
|
||||
|
||||
public DbColumn getColumnTargetResourceId() {
|
||||
return myColumnTargetResourceId;
|
||||
}
|
||||
|
||||
public DbColumn getColumnSrcResourceId() {
|
||||
return myColumnSrcResourceId;
|
||||
}
|
||||
|
||||
public DbColumn getColumnTargetResourceType() {
|
||||
return myColumnTargetResourceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DbColumn getResourceIdColumn() {
|
||||
if (myReversed) {
|
||||
return myColumnTargetResourceId;
|
||||
} else {
|
||||
return myColumnSrcResourceId;
|
||||
}
|
||||
}
|
||||
|
||||
public Condition createPredicate(RequestDetails theRequest, String theResourceType, String theParamName, List<? extends IQueryParameterType> theReferenceOrParamList, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
|
||||
|
||||
List<IIdType> targetIds = new ArrayList<>();
|
||||
List<String> targetQualifiedUrls = new ArrayList<>();
|
||||
|
||||
for (int orIdx = 0; orIdx < theReferenceOrParamList.size(); orIdx++) {
|
||||
IQueryParameterType nextOr = theReferenceOrParamList.get(orIdx);
|
||||
|
||||
if (nextOr instanceof ReferenceParam) {
|
||||
ReferenceParam ref = (ReferenceParam) nextOr;
|
||||
|
||||
if (isBlank(ref.getChain())) {
|
||||
|
||||
/*
|
||||
* Handle non-chained search, e.g. Patient?organization=Organization/123
|
||||
*/
|
||||
|
||||
IIdType dt = new IdDt(ref.getBaseUrl(), ref.getResourceType(), ref.getIdPart(), null);
|
||||
|
||||
if (dt.hasBaseUrl()) {
|
||||
if (myDaoConfig.getTreatBaseUrlsAsLocal().contains(dt.getBaseUrl())) {
|
||||
dt = dt.toUnqualified();
|
||||
targetIds.add(dt);
|
||||
} else {
|
||||
targetQualifiedUrls.add(dt.getValue());
|
||||
}
|
||||
} else {
|
||||
targetIds.add(dt);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/*
|
||||
* Handle chained search, e.g. Patient?organization.name=Kwik-e-mart
|
||||
*/
|
||||
|
||||
return addPredicateReferenceWithChain(theResourceType, theParamName, theReferenceOrParamList, ref, theRequest, theRequestPartitionId);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid token type (expecting ReferenceParam): " + nextOr.getClass());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (IIdType next : targetIds) {
|
||||
if (!next.hasResourceType()) {
|
||||
warnAboutPerformanceOnUnqualifiedResources(theParamName, theRequest, null);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> pathsToMatch = createResourceLinkPaths(theResourceType, theParamName);
|
||||
boolean inverse;
|
||||
if ((theOperation == null) || (theOperation == SearchFilterParser.CompareOperation.eq)) {
|
||||
inverse = false;
|
||||
} else {
|
||||
inverse = true;
|
||||
}
|
||||
|
||||
List<ResourcePersistentId> targetPids = myIdHelperService.resolveResourcePersistentIdsWithCache(theRequestPartitionId, targetIds);
|
||||
List<Long> targetPidList = ResourcePersistentId.toLongList(targetPids);
|
||||
|
||||
if (targetPidList.isEmpty() && targetQualifiedUrls.isEmpty()) {
|
||||
setMatchNothing();
|
||||
return null;
|
||||
} else {
|
||||
Condition retVal = createPredicateReference(inverse, pathsToMatch, targetPidList, targetQualifiedUrls);
|
||||
return combineWithRequestPartitionIdPredicate(getRequestPartitionId(), retVal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Condition createPredicateReference(boolean theInverse, List<String> thePathsToMatch, List<Long> theTargetPidList, List<String> theTargetQualifiedUrls) {
|
||||
|
||||
Condition targetPidCondition = null;
|
||||
if (!theTargetPidList.isEmpty()) {
|
||||
List<String> placeholders = generatePlaceholders(theTargetPidList);
|
||||
targetPidCondition = toEqualToOrInPredicate(myColumnTargetResourceId, placeholders, theInverse);
|
||||
}
|
||||
|
||||
Condition targetUrlsCondition = null;
|
||||
if (!theTargetQualifiedUrls.isEmpty()) {
|
||||
List<String> placeholders = generatePlaceholders(theTargetQualifiedUrls);
|
||||
targetUrlsCondition = toEqualToOrInPredicate(myColumnTargetResourceUrl, placeholders, theInverse);
|
||||
}
|
||||
|
||||
Condition joinedCondition;
|
||||
if (targetPidCondition != null && targetUrlsCondition != null) {
|
||||
joinedCondition = ComboCondition.or(targetPidCondition, targetUrlsCondition);
|
||||
} else if (targetPidCondition != null) {
|
||||
joinedCondition = targetPidCondition;
|
||||
} else {
|
||||
joinedCondition = targetUrlsCondition;
|
||||
}
|
||||
|
||||
Condition pathPredicate = createPredicateSourcePaths(thePathsToMatch);
|
||||
joinedCondition = ComboCondition.and(pathPredicate, joinedCondition);
|
||||
|
||||
return joinedCondition;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private Condition createPredicateSourcePaths(List<String> thePathsToMatch) {
|
||||
return toEqualToOrInPredicate(myColumnSrcPath, generatePlaceholders(thePathsToMatch));
|
||||
}
|
||||
|
||||
public Condition createPredicateSourcePaths(String theResourceName, String theParamName) {
|
||||
List<String> pathsToMatch = createResourceLinkPaths(theResourceName, theParamName);
|
||||
return createPredicateSourcePaths(pathsToMatch);
|
||||
}
|
||||
|
||||
|
||||
private void warnAboutPerformanceOnUnqualifiedResources(String theParamName, RequestDetails theRequest, @Nullable List<String> theCandidateTargetTypes) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("This search uses an unqualified resource(a parameter in a chain without a resource type). ");
|
||||
builder.append("This is less efficient than using a qualified type. ");
|
||||
if (theCandidateTargetTypes != null) {
|
||||
builder.append("[" + theParamName + "] resolves to [" + theCandidateTargetTypes.stream().collect(Collectors.joining(",")) + "].");
|
||||
builder.append("If you know what you're looking for, try qualifying it using the form ");
|
||||
builder.append(theCandidateTargetTypes.stream().map(cls -> "[" + cls + ":" + theParamName + "]").collect(Collectors.joining(" or ")));
|
||||
} else {
|
||||
builder.append("If you know what you're looking for, try qualifying it using the form: '");
|
||||
builder.append(theParamName).append(":[resourceType]");
|
||||
builder.append("'");
|
||||
}
|
||||
String message = builder
|
||||
.toString();
|
||||
StorageProcessingMessage msg = new StorageProcessingMessage()
|
||||
.setMessage(message);
|
||||
HookParams params = new HookParams()
|
||||
.add(RequestDetails.class, theRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequest)
|
||||
.add(StorageProcessingMessage.class, msg);
|
||||
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_WARNING, params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is for handling queries like the following: /Observation?device.identifier=urn:system|foo in which we use a chain
|
||||
* on the device.
|
||||
*/
|
||||
private Condition addPredicateReferenceWithChain(String theResourceName, String theParamName, List<? extends IQueryParameterType> theList, ReferenceParam theReferenceParam, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
|
||||
|
||||
/*
|
||||
* Which resource types can the given chained parameter actually link to? This might be a list
|
||||
* where the chain is unqualified, as in: Observation?subject.identifier=(...)
|
||||
* since subject can link to several possible target types.
|
||||
*
|
||||
* If the user has qualified the chain, as in: Observation?subject:Patient.identifier=(...)
|
||||
* this is just a simple 1-entry list.
|
||||
*/
|
||||
final List<String> resourceTypes = determineCandidateResourceTypesForChain(theResourceName, theParamName, theReferenceParam);
|
||||
|
||||
/*
|
||||
* Handle chain on _type
|
||||
*/
|
||||
if (Constants.PARAM_TYPE.equals(theReferenceParam.getChain())) {
|
||||
|
||||
List<String> pathsToMatch = createResourceLinkPaths(theResourceName, theParamName);
|
||||
Condition typeCondition = createPredicateSourcePaths(pathsToMatch);
|
||||
|
||||
String typeValue = theReferenceParam.getValue();
|
||||
|
||||
try {
|
||||
getFhirContext().getResourceDefinition(typeValue).getImplementingClass();
|
||||
} catch (DataFormatException e) {
|
||||
throw newInvalidResourceTypeException(typeValue);
|
||||
}
|
||||
if (!resourceTypes.contains(typeValue)) {
|
||||
throw newInvalidTargetTypeForChainException(theResourceName, theParamName, typeValue);
|
||||
}
|
||||
|
||||
Condition condition = BinaryCondition.equalTo(myColumnTargetResourceType, generatePlaceholder(theReferenceParam.getValue()));
|
||||
|
||||
return toAndPredicate(typeCondition, condition);
|
||||
}
|
||||
|
||||
boolean foundChainMatch = false;
|
||||
List<String> candidateTargetTypes = new ArrayList<>();
|
||||
List<Condition> orPredicates = new ArrayList<>();
|
||||
QueryStack childQueryFactory = myQueryStack.newChildQueryFactoryWithFullBuilderReuse();
|
||||
for (String nextType : resourceTypes) {
|
||||
String chain = theReferenceParam.getChain();
|
||||
|
||||
String remainingChain = null;
|
||||
int chainDotIndex = chain.indexOf('.');
|
||||
if (chainDotIndex != -1) {
|
||||
remainingChain = chain.substring(chainDotIndex + 1);
|
||||
chain = chain.substring(0, chainDotIndex);
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition typeDef = getFhirContext().getResourceDefinition(nextType);
|
||||
String subResourceName = typeDef.getName();
|
||||
|
||||
IDao dao = myDaoRegistry.getResourceDao(nextType);
|
||||
if (dao == null) {
|
||||
ourLog.debug("Don't have a DAO for type {}", nextType);
|
||||
continue;
|
||||
}
|
||||
|
||||
int qualifierIndex = chain.indexOf(':');
|
||||
String qualifier = null;
|
||||
if (qualifierIndex != -1) {
|
||||
qualifier = chain.substring(qualifierIndex);
|
||||
chain = chain.substring(0, qualifierIndex);
|
||||
}
|
||||
|
||||
boolean isMeta = ResourceMetaParams.RESOURCE_META_PARAMS.containsKey(chain);
|
||||
RuntimeSearchParam param = null;
|
||||
if (!isMeta) {
|
||||
param = mySearchParamRegistry.getSearchParamByName(typeDef, chain);
|
||||
if (param == null) {
|
||||
ourLog.debug("Type {} doesn't have search param {}", nextType, param);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<IQueryParameterType> orValues = Lists.newArrayList();
|
||||
|
||||
for (IQueryParameterType next : theList) {
|
||||
String nextValue = next.getValueAsQueryToken(getFhirContext());
|
||||
IQueryParameterType chainValue = mapReferenceChainToRawParamType(remainingChain, param, theParamName, qualifier, nextType, chain, isMeta, nextValue);
|
||||
if (chainValue == null) {
|
||||
continue;
|
||||
}
|
||||
foundChainMatch = true;
|
||||
orValues.add(chainValue);
|
||||
}
|
||||
|
||||
if (!foundChainMatch) {
|
||||
throw new InvalidRequestException(getFhirContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "invalidParameterChain", theParamName + '.' + theReferenceParam.getChain()));
|
||||
}
|
||||
|
||||
candidateTargetTypes.add(nextType);
|
||||
|
||||
List<Condition> andPredicates = new ArrayList<>();
|
||||
|
||||
List<List<IQueryParameterType>> chainParamValues = Collections.singletonList(orValues);
|
||||
andPredicates.add(childQueryFactory.searchForIdsWithAndOr(myColumnTargetResourceId, subResourceName, chain, chainParamValues, theRequest, theRequestPartitionId));
|
||||
|
||||
orPredicates.add(toAndPredicate(andPredicates));
|
||||
|
||||
}
|
||||
|
||||
if (candidateTargetTypes.isEmpty()) {
|
||||
throw new InvalidRequestException(getFhirContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "invalidParameterChain", theParamName + '.' + theReferenceParam.getChain()));
|
||||
}
|
||||
|
||||
if (candidateTargetTypes.size() > 1) {
|
||||
warnAboutPerformanceOnUnqualifiedResources(theParamName, theRequest, candidateTargetTypes);
|
||||
}
|
||||
|
||||
Condition multiTypeOrPredicate = toOrPredicate(orPredicates);
|
||||
List<String> pathsToMatch = createResourceLinkPaths(theResourceName, theParamName);
|
||||
Condition pathPredicate = createPredicateSourcePaths(pathsToMatch);
|
||||
return toAndPredicate(pathPredicate, multiTypeOrPredicate);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private List<String> determineCandidateResourceTypesForChain(String theResourceName, String theParamName, ReferenceParam theReferenceParam) {
|
||||
final List<Class<? extends IBaseResource>> resourceTypes;
|
||||
if (!theReferenceParam.hasResourceType()) {
|
||||
|
||||
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam(theResourceName, theParamName);
|
||||
resourceTypes = new ArrayList<>();
|
||||
|
||||
if (param.hasTargets()) {
|
||||
Set<String> targetTypes = param.getTargets();
|
||||
for (String next : targetTypes) {
|
||||
resourceTypes.add(getFhirContext().getResourceDefinition(next).getImplementingClass());
|
||||
}
|
||||
}
|
||||
|
||||
if (resourceTypes.isEmpty()) {
|
||||
RuntimeResourceDefinition resourceDef = getFhirContext().getResourceDefinition(theResourceName);
|
||||
RuntimeSearchParam searchParamByName = mySearchParamRegistry.getSearchParamByName(resourceDef, theParamName);
|
||||
if (searchParamByName == null) {
|
||||
throw new InternalErrorException("Could not find parameter " + theParamName);
|
||||
}
|
||||
String paramPath = searchParamByName.getPath();
|
||||
if (paramPath.endsWith(".as(Reference)")) {
|
||||
paramPath = paramPath.substring(0, paramPath.length() - ".as(Reference)".length()) + "Reference";
|
||||
}
|
||||
|
||||
if (paramPath.contains(".extension(")) {
|
||||
int startIdx = paramPath.indexOf(".extension(");
|
||||
int endIdx = paramPath.indexOf(')', startIdx);
|
||||
if (startIdx != -1 && endIdx != -1) {
|
||||
paramPath = paramPath.substring(0, startIdx + 10) + paramPath.substring(endIdx + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Class<? extends IBaseResource> resourceType = getFhirContext().getResourceDefinition(theResourceName).getImplementingClass();
|
||||
BaseRuntimeChildDefinition def = getFhirContext().newTerser().getDefinition(resourceType, paramPath);
|
||||
if (def instanceof RuntimeChildChoiceDefinition) {
|
||||
RuntimeChildChoiceDefinition choiceDef = (RuntimeChildChoiceDefinition) def;
|
||||
resourceTypes.addAll(choiceDef.getResourceTypes());
|
||||
} else if (def instanceof RuntimeChildResourceDefinition) {
|
||||
RuntimeChildResourceDefinition resDef = (RuntimeChildResourceDefinition) def;
|
||||
resourceTypes.addAll(resDef.getResourceTypes());
|
||||
if (resourceTypes.size() == 1) {
|
||||
if (resourceTypes.get(0).isInterface()) {
|
||||
throw new InvalidRequestException("Unable to perform search for unqualified chain '" + theParamName + "' as this SearchParameter does not declare any target types. Add a qualifier of the form '" + theParamName + ":[ResourceType]' to perform this search.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ConfigurationException("Property " + paramPath + " of type " + getResourceType() + " is not a resource: " + def.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
if (resourceTypes.isEmpty()) {
|
||||
for (BaseRuntimeElementDefinition<?> next : getFhirContext().getElementDefinitions()) {
|
||||
if (next instanceof RuntimeResourceDefinition) {
|
||||
RuntimeResourceDefinition nextResDef = (RuntimeResourceDefinition) next;
|
||||
resourceTypes.add(nextResDef.getImplementingClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
try {
|
||||
RuntimeResourceDefinition resDef = getFhirContext().getResourceDefinition(theReferenceParam.getResourceType());
|
||||
resourceTypes = new ArrayList<>(1);
|
||||
resourceTypes.add(resDef.getImplementingClass());
|
||||
} catch (DataFormatException e) {
|
||||
throw newInvalidResourceTypeException(theReferenceParam.getResourceType());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return resourceTypes
|
||||
.stream()
|
||||
.map(t -> getFhirContext().getResourceType(t))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<String> createResourceLinkPaths(String theResourceName, String theParamName) {
|
||||
RuntimeResourceDefinition resourceDef = getFhirContext().getResourceDefinition(theResourceName);
|
||||
RuntimeSearchParam param = mySearchParamRegistry.getSearchParamByName(resourceDef, theParamName);
|
||||
List<String> path = param.getPathsSplit();
|
||||
|
||||
/*
|
||||
* SearchParameters can declare paths on multiple resource
|
||||
* types. Here we only want the ones that actually apply.
|
||||
*/
|
||||
path = new ArrayList<>(path);
|
||||
|
||||
ListIterator<String> iter = path.listIterator();
|
||||
while (iter.hasNext()) {
|
||||
String nextPath = trim(iter.next());
|
||||
if (!nextPath.contains(theResourceName + ".")) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
private IQueryParameterType mapReferenceChainToRawParamType(String remainingChain, RuntimeSearchParam param, String theParamName, String qualifier, String nextType, String chain, boolean isMeta, String resourceId) {
|
||||
IQueryParameterType chainValue;
|
||||
if (remainingChain != null) {
|
||||
if (param == null || param.getParamType() != RestSearchParameterTypeEnum.REFERENCE) {
|
||||
ourLog.debug("Type {} parameter {} is not a reference, can not chain {}", nextType, chain, remainingChain);
|
||||
return null;
|
||||
}
|
||||
|
||||
chainValue = new ReferenceParam();
|
||||
chainValue.setValueAsQueryToken(getFhirContext(), theParamName, qualifier, resourceId);
|
||||
((ReferenceParam) chainValue).setChain(remainingChain);
|
||||
} else if (isMeta) {
|
||||
IQueryParameterType type = myMatchUrlService.newInstanceType(chain);
|
||||
type.setValueAsQueryToken(getFhirContext(), theParamName, qualifier, resourceId);
|
||||
chainValue = type;
|
||||
} else {
|
||||
chainValue = toParameterType(param, qualifier, resourceId);
|
||||
}
|
||||
|
||||
return chainValue;
|
||||
}
|
||||
|
||||
private IQueryParameterType toParameterType(RuntimeSearchParam theParam) {
|
||||
IQueryParameterType qp;
|
||||
switch (theParam.getParamType()) {
|
||||
case DATE:
|
||||
qp = new DateParam();
|
||||
break;
|
||||
case NUMBER:
|
||||
qp = new NumberParam();
|
||||
break;
|
||||
case QUANTITY:
|
||||
qp = new QuantityParam();
|
||||
break;
|
||||
case STRING:
|
||||
qp = new StringParam();
|
||||
break;
|
||||
case TOKEN:
|
||||
qp = new TokenParam();
|
||||
break;
|
||||
case COMPOSITE:
|
||||
List<RuntimeSearchParam> compositeOf = theParam.getCompositeOf();
|
||||
if (compositeOf.size() != 2) {
|
||||
throw new InternalErrorException("Parameter " + theParam.getName() + " has " + compositeOf.size() + " composite parts. Don't know how handlt this.");
|
||||
}
|
||||
IQueryParameterType leftParam = toParameterType(compositeOf.get(0));
|
||||
IQueryParameterType rightParam = toParameterType(compositeOf.get(1));
|
||||
qp = new CompositeParam<>(leftParam, rightParam);
|
||||
break;
|
||||
case REFERENCE:
|
||||
qp = new ReferenceParam();
|
||||
break;
|
||||
case SPECIAL:
|
||||
if ("Location.position".equals(theParam.getPath())) {
|
||||
qp = new SpecialParam();
|
||||
break;
|
||||
}
|
||||
throw new InternalErrorException("Don't know how to convert param type: " + theParam.getParamType());
|
||||
case URI:
|
||||
case HAS:
|
||||
default:
|
||||
throw new InternalErrorException("Don't know how to convert param type: " + theParam.getParamType());
|
||||
}
|
||||
return qp;
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
private InvalidRequestException newInvalidTargetTypeForChainException(String theResourceName, String theParamName, String theTypeValue) {
|
||||
String searchParamName = theResourceName + ":" + theParamName;
|
||||
String msg = getFhirContext().getLocalizer().getMessage(PredicateBuilderReference.class, "invalidTargetTypeForChain", theTypeValue, searchParamName);
|
||||
return new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
private IQueryParameterType toParameterType(RuntimeSearchParam theParam, String theQualifier, String theValueAsQueryToken) {
|
||||
IQueryParameterType qp = toParameterType(theParam);
|
||||
|
||||
qp.setValueAsQueryToken(getFhirContext(), theParam.getName(), theQualifier, theValueAsQueryToken);
|
||||
return qp;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private InvalidRequestException newInvalidResourceTypeException(String theResourceType) {
|
||||
String msg = getFhirContext().getLocalizer().getMessageSanitized(PredicateBuilderReference.class, "invalidResourceType", theResourceType);
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Condition createEverythingPredicate(String theResourceName, Long theTargetPid) {
|
||||
if (theTargetPid != null) {
|
||||
return BinaryCondition.equalTo(myColumnTargetResourceId, generatePlaceholder(theTargetPid));
|
||||
} else {
|
||||
return BinaryCondition.equalTo(myColumnTargetResourceType, generatePlaceholder(theResourceName));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import com.healthmarketscience.sqlbuilder.BinaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.Condition;
|
||||
import com.healthmarketscience.sqlbuilder.NotCondition;
|
||||
import com.healthmarketscience.sqlbuilder.UnaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toAndPredicate;
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toEqualToOrInPredicate;
|
||||
|
||||
public class ResourceTablePredicateBuilder extends BaseJoiningPredicateBuilder {
|
||||
private final DbColumn myColumnResId;
|
||||
private final DbColumn myColumnResDeletedAt;
|
||||
private final DbColumn myColumnResType;
|
||||
private final DbColumn myColumnLastUpdated;
|
||||
private final DbColumn myColumnLanguage;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResourceTablePredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_RESOURCE"));
|
||||
myColumnResId = getTable().addColumn("RES_ID");
|
||||
myColumnResType = getTable().addColumn("RES_TYPE");
|
||||
myColumnResDeletedAt = getTable().addColumn("RES_DELETED_AT");
|
||||
myColumnLastUpdated = getTable().addColumn("RES_UPDATED");
|
||||
myColumnLanguage = getTable().addColumn("RES_LANGUAGE");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DbColumn getResourceIdColumn() {
|
||||
return myColumnResId;
|
||||
}
|
||||
|
||||
public Condition createResourceTypeAndNonDeletedPredicates() {
|
||||
BinaryCondition typePredicate = null;
|
||||
if (getResourceType() != null) {
|
||||
typePredicate = BinaryCondition.equalTo(myColumnResType, generatePlaceholder(getResourceType()));
|
||||
}
|
||||
return toAndPredicate(
|
||||
typePredicate,
|
||||
UnaryCondition.isNull(myColumnResDeletedAt)
|
||||
);
|
||||
}
|
||||
|
||||
public DbColumn getLastUpdatedColumn() {
|
||||
return myColumnLastUpdated;
|
||||
}
|
||||
|
||||
public Condition createLanguagePredicate(Set<String> theValues, boolean theNegated) {
|
||||
Condition condition = toEqualToOrInPredicate(myColumnLanguage, generatePlaceholders(theValues));
|
||||
if (theNegated) {
|
||||
condition = new NotCondition(condition);
|
||||
}
|
||||
return condition;
|
||||
}
|
||||
|
||||
public DbColumn getColumnLastUpdated() {
|
||||
return myColumnLastUpdated;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
|
||||
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;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public class SearchParamPresentPredicateBuilder extends BaseJoiningPredicateBuilder {
|
||||
|
||||
private final DbColumn myColumnResourceId;
|
||||
private final DbColumn myColumnHashPresence;
|
||||
|
||||
@Autowired
|
||||
private PartitionSettings myPartitionSettings;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public SearchParamPresentPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_RES_PARAM_PRESENT"));
|
||||
myColumnResourceId = getTable().addColumn("RES_ID");
|
||||
myColumnHashPresence = getTable().addColumn("HASH_PRESENCE");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DbColumn getResourceIdColumn() {
|
||||
return myColumnResourceId;
|
||||
}
|
||||
|
||||
|
||||
public Condition createPredicateParamMissingForReference(String theResourceName, String theParamName, boolean theMissing, RequestPartitionId theRequestPartitionId) {
|
||||
Long hash = SearchParamPresent.calculateHashPresence(myPartitionSettings, theRequestPartitionId, theResourceName, theParamName, !theMissing);
|
||||
BinaryCondition predicate = BinaryCondition.equalTo(myColumnHashPresence, generatePlaceholder(hash));
|
||||
return combineWithRequestPartitionIdPredicate(theRequestPartitionId, predicate);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import 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;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class SourcePredicateBuilder extends BaseJoiningPredicateBuilder {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(SourcePredicateBuilder.class);
|
||||
private final DbColumn myColumnSourceUri;
|
||||
private final DbColumn myColumnRequestId;
|
||||
private final DbColumn myResourceIdColumn;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public SourcePredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_RES_VER_PROV"));
|
||||
|
||||
myResourceIdColumn = getTable().addColumn("RES_PID");
|
||||
myColumnSourceUri = getTable().addColumn("SOURCE_URI");
|
||||
myColumnRequestId = getTable().addColumn("REQUEST_ID");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DbColumn getResourceIdColumn() {
|
||||
return myResourceIdColumn;
|
||||
}
|
||||
|
||||
public Condition createPredicateSourceUri(String theSourceUri) {
|
||||
return BinaryCondition.equalTo(myColumnSourceUri, generatePlaceholder(theSourceUri));
|
||||
}
|
||||
|
||||
public Condition createPredicateRequestId(String theRequestId) {
|
||||
return BinaryCondition.equalTo(myColumnRequestId, generatePlaceholder(theRequestId));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,237 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
||||
import ca.uhn.fhir.util.StringUtil;
|
||||
import com.healthmarketscience.sqlbuilder.BinaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.ComboCondition;
|
||||
import com.healthmarketscience.sqlbuilder.Condition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class StringPredicateBuilder extends BaseSearchParamPredicateBuilder {
|
||||
|
||||
private final DbColumn myColumnResId;
|
||||
private final DbColumn myColumnValueExact;
|
||||
private final DbColumn myColumnValueNormalized;
|
||||
private final DbColumn myColumnHashNormPrefix;
|
||||
private final DbColumn myColumnHashIdentity;
|
||||
private final DbColumn myColumnHashExact;
|
||||
@Autowired
|
||||
private DaoConfig myDaoConfig;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public StringPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_SPIDX_STRING"));
|
||||
myColumnResId = getTable().addColumn("RES_ID");
|
||||
myColumnValueExact = getTable().addColumn("SP_VALUE_EXACT");
|
||||
myColumnValueNormalized = getTable().addColumn("SP_VALUE_NORMALIZED");
|
||||
myColumnHashNormPrefix = getTable().addColumn("HASH_NORM_PREFIX");
|
||||
myColumnHashIdentity = getTable().addColumn("HASH_IDENTITY");
|
||||
myColumnHashExact = getTable().addColumn("HASH_EXACT");
|
||||
}
|
||||
|
||||
public DbColumn getColumnValueNormalized() {
|
||||
return myColumnValueNormalized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DbColumn getResourceIdColumn() {
|
||||
return myColumnResId;
|
||||
}
|
||||
|
||||
public Condition createPredicateString(IQueryParameterType theParameter,
|
||||
String theResourceName,
|
||||
RuntimeSearchParam theSearchParam,
|
||||
StringPredicateBuilder theFrom,
|
||||
SearchFilterParser.CompareOperation operation) {
|
||||
String rawSearchTerm;
|
||||
String paramName = theSearchParam.getName();
|
||||
if (theParameter instanceof TokenParam) {
|
||||
TokenParam id = (TokenParam) theParameter;
|
||||
if (!id.isText()) {
|
||||
throw new IllegalStateException("Trying to process a text search on a non-text token parameter");
|
||||
}
|
||||
rawSearchTerm = id.getValue();
|
||||
} else if (theParameter instanceof StringParam) {
|
||||
StringParam id = (StringParam) theParameter;
|
||||
rawSearchTerm = id.getValue();
|
||||
if (id.isContains()) {
|
||||
if (!myDaoConfig.isAllowContainsSearches()) {
|
||||
throw new MethodNotAllowedException(":contains modifier is disabled on this server");
|
||||
}
|
||||
} else {
|
||||
rawSearchTerm = theSearchParam.encode(rawSearchTerm);
|
||||
}
|
||||
} else if (theParameter instanceof IPrimitiveDatatype<?>) {
|
||||
IPrimitiveDatatype<?> id = (IPrimitiveDatatype<?>) theParameter;
|
||||
rawSearchTerm = id.getValueAsString();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid token type: " + theParameter.getClass());
|
||||
}
|
||||
|
||||
if (rawSearchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||
throw new InvalidRequestException("Parameter[" + paramName + "] has length (" + rawSearchTerm.length() + ") that is longer than maximum allowed ("
|
||||
+ ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm);
|
||||
}
|
||||
|
||||
boolean exactMatch = theParameter instanceof StringParam && ((StringParam) theParameter).isExact();
|
||||
if (exactMatch) {
|
||||
// Exact match
|
||||
return theFrom.createPredicateExact(theResourceName, paramName, rawSearchTerm);
|
||||
} else {
|
||||
// Normalized Match
|
||||
String normalizedString = StringUtil.normalizeStringForSearchIndexing(rawSearchTerm);
|
||||
String likeExpression;
|
||||
if ((theParameter instanceof StringParam) &&
|
||||
(((((StringParam) theParameter).isContains()) &&
|
||||
(myDaoConfig.isAllowContainsSearches())) ||
|
||||
(operation == SearchFilterParser.CompareOperation.co))) {
|
||||
likeExpression = createLeftAndRightMatchLikeExpression(normalizedString);
|
||||
} else if ((operation != SearchFilterParser.CompareOperation.ne) &&
|
||||
(operation != SearchFilterParser.CompareOperation.gt) &&
|
||||
(operation != SearchFilterParser.CompareOperation.lt) &&
|
||||
(operation != SearchFilterParser.CompareOperation.ge) &&
|
||||
(operation != SearchFilterParser.CompareOperation.le)) {
|
||||
if (operation == SearchFilterParser.CompareOperation.ew) {
|
||||
likeExpression = createRightMatchLikeExpression(normalizedString);
|
||||
} else {
|
||||
likeExpression = createLeftMatchLikeExpression(normalizedString);
|
||||
}
|
||||
} else {
|
||||
likeExpression = normalizedString;
|
||||
}
|
||||
|
||||
Condition predicate;
|
||||
if ((operation == null) ||
|
||||
(operation == SearchFilterParser.CompareOperation.sw)) {
|
||||
predicate = theFrom.createPredicateNormalLike(theResourceName, paramName, normalizedString, likeExpression);
|
||||
} else if ((operation == SearchFilterParser.CompareOperation.ew) || (operation == SearchFilterParser.CompareOperation.co)) {
|
||||
predicate = theFrom.createPredicateLikeExpressionOnly(theResourceName, paramName, likeExpression, false);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.eq) {
|
||||
predicate = theFrom.createPredicateNormal(theResourceName, paramName, normalizedString);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.ne) {
|
||||
predicate = theFrom.createPredicateLikeExpressionOnly(theResourceName, paramName, likeExpression, true);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.gt) {
|
||||
predicate = theFrom.createPredicateNormalGreaterThan(theResourceName, paramName, likeExpression);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.ge) {
|
||||
predicate = theFrom.createPredicateNormalGreaterThanOrEqual(theResourceName, paramName, likeExpression);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.lt) {
|
||||
predicate = theFrom.createPredicateNormalLessThan(theResourceName, paramName, likeExpression);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.le) {
|
||||
predicate = theFrom.createPredicateNormalLessThanOrEqual(theResourceName, paramName, likeExpression);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't yet know how to handle operation " + operation + " on a string");
|
||||
}
|
||||
|
||||
return predicate;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Condition createPredicateExact(String theResourceType, String theParamName, String theTheValueExact) {
|
||||
long hash = ResourceIndexedSearchParamString.calculateHashExact(getPartitionSettings(), getRequestPartitionId(), theResourceType, theParamName, theTheValueExact);
|
||||
String placeholderValue = generatePlaceholder(hash);
|
||||
return BinaryCondition.equalTo(myColumnHashExact, placeholderValue);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Condition createPredicateNormalLike(String theResourceType, String theParamName, String theNormalizedString, String theLikeExpression) {
|
||||
Long hash = ResourceIndexedSearchParamString.calculateHashNormalized(getPartitionSettings(), getRequestPartitionId(), getModelConfig(), theResourceType, theParamName, theNormalizedString);
|
||||
Condition hashPredicate = BinaryCondition.equalTo(myColumnHashNormPrefix, generatePlaceholder(hash));
|
||||
Condition valuePredicate = BinaryCondition.like(myColumnValueNormalized, generatePlaceholder(theLikeExpression));
|
||||
return ComboCondition.and(hashPredicate, valuePredicate);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Condition createPredicateNormal(String theResourceType, String theParamName, String theNormalizedString) {
|
||||
Long hash = ResourceIndexedSearchParamString.calculateHashNormalized(getPartitionSettings(), getRequestPartitionId(), getModelConfig(), theResourceType, theParamName, theNormalizedString);
|
||||
Condition hashPredicate = BinaryCondition.equalTo(myColumnHashNormPrefix, generatePlaceholder(hash));
|
||||
Condition valuePredicate = BinaryCondition.equalTo(myColumnValueNormalized, generatePlaceholder(theNormalizedString));
|
||||
return ComboCondition.and(hashPredicate, valuePredicate);
|
||||
}
|
||||
|
||||
private Condition createPredicateNormalGreaterThanOrEqual(String theResourceType, String theParamName, String theNormalizedString) {
|
||||
Condition hashPredicate = createHashIdentityPredicate(theResourceType, theParamName);
|
||||
Condition valuePredicate = BinaryCondition.greaterThanOrEq(myColumnValueNormalized, generatePlaceholder(theNormalizedString));
|
||||
return ComboCondition.and(hashPredicate, valuePredicate);
|
||||
}
|
||||
|
||||
private Condition createPredicateNormalGreaterThan(String theResourceType, String theParamName, String theNormalizedString) {
|
||||
Condition hashPredicate = createHashIdentityPredicate(theResourceType, theParamName);
|
||||
Condition valuePredicate = BinaryCondition.greaterThan(myColumnValueNormalized, generatePlaceholder(theNormalizedString));
|
||||
return ComboCondition.and(hashPredicate, valuePredicate);
|
||||
}
|
||||
|
||||
private Condition createPredicateNormalLessThanOrEqual(String theResourceType, String theParamName, String theNormalizedString) {
|
||||
Condition hashPredicate = createHashIdentityPredicate(theResourceType, theParamName);
|
||||
Condition valuePredicate = BinaryCondition.lessThanOrEq(myColumnValueNormalized, generatePlaceholder(theNormalizedString));
|
||||
return ComboCondition.and(hashPredicate, valuePredicate);
|
||||
}
|
||||
|
||||
private Condition createPredicateNormalLessThan(String theResourceType, String theParamName, String theNormalizedString) {
|
||||
Condition hashPredicate = createHashIdentityPredicate(theResourceType, theParamName);
|
||||
Condition valuePredicate = BinaryCondition.lessThan(myColumnValueNormalized, generatePlaceholder(theNormalizedString));
|
||||
return ComboCondition.and(hashPredicate, valuePredicate);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Condition createPredicateLikeExpressionOnly(String theResourceType, String theParamName, String theLikeExpression, boolean theInverse) {
|
||||
long hashIdentity = ResourceIndexedSearchParamString.calculateHashIdentity(getPartitionSettings(), getRequestPartitionId(), theResourceType, theParamName);
|
||||
BinaryCondition identityPredicate = BinaryCondition.equalTo(myColumnHashIdentity, generatePlaceholder(hashIdentity));
|
||||
BinaryCondition likePredicate;
|
||||
if (theInverse) {
|
||||
likePredicate = BinaryCondition.notLike(myColumnValueNormalized, generatePlaceholder(theLikeExpression));
|
||||
} else {
|
||||
likePredicate = BinaryCondition.like(myColumnValueNormalized, generatePlaceholder(theLikeExpression));
|
||||
}
|
||||
return ComboCondition.and(identityPredicate, likePredicate);
|
||||
}
|
||||
|
||||
public static String createLeftAndRightMatchLikeExpression(String likeExpression) {
|
||||
return "%" + likeExpression.replace("%", "[%]") + "%";
|
||||
}
|
||||
|
||||
public static String createLeftMatchLikeExpression(String likeExpression) {
|
||||
return likeExpression.replace("%", "[%]") + "%";
|
||||
}
|
||||
|
||||
public static String createRightMatchLikeExpression(String likeExpression) {
|
||||
return "%" + likeExpression.replace("%", "[%]");
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.healthmarketscience.sqlbuilder.BinaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.ComboCondition;
|
||||
import com.healthmarketscience.sqlbuilder.Condition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class TagPredicateBuilder extends BaseJoiningPredicateBuilder {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(TagPredicateBuilder.class);
|
||||
|
||||
private final DbColumn myColumnResId;
|
||||
private final DbTable myTagDefinitionTable;
|
||||
private final DbColumn myTagDefinitionColumnTagId;
|
||||
private final DbColumn myTagDefinitionColumnTagSystem;
|
||||
private final DbColumn myTagDefinitionColumnTagCode;
|
||||
private final DbColumn myColumnTagId;
|
||||
private final DbColumn myTagDefinitionColumnTagType;
|
||||
|
||||
public TagPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_RES_TAG"));
|
||||
|
||||
myColumnResId = getTable().addColumn("RES_ID");
|
||||
myColumnTagId = getTable().addColumn("TAG_ID");
|
||||
|
||||
myTagDefinitionTable = theSearchSqlBuilder.addTable("HFJ_TAG_DEF");
|
||||
myTagDefinitionColumnTagId = myTagDefinitionTable.addColumn("TAG_ID");
|
||||
myTagDefinitionColumnTagSystem = myTagDefinitionTable.addColumn("TAG_SYSTEM");
|
||||
myTagDefinitionColumnTagCode = myTagDefinitionTable.addColumn("TAG_CODE");
|
||||
myTagDefinitionColumnTagType = myTagDefinitionTable.addColumn("TAG_TYPE");
|
||||
}
|
||||
|
||||
|
||||
public Condition createPredicateTag(TagTypeEnum theTagType, List<Pair<String, String>> theTokens, String theParamName, RequestPartitionId theRequestPartitionId) {
|
||||
addJoin(getTable(), myTagDefinitionTable, myColumnTagId, myTagDefinitionColumnTagId);
|
||||
return createPredicateTagList(theTagType, theTokens);
|
||||
}
|
||||
|
||||
private Condition createPredicateTagList(TagTypeEnum theTagType, List<Pair<String, String>> theTokens) {
|
||||
Condition typePredicate = BinaryCondition.equalTo(myTagDefinitionColumnTagType, generatePlaceholder(theTagType.ordinal()));
|
||||
|
||||
List<Condition> orPredicates = Lists.newArrayList();
|
||||
for (Pair<String, String> next : theTokens) {
|
||||
Condition codePredicate = BinaryCondition.equalTo(myTagDefinitionColumnTagCode, generatePlaceholder(next.getRight()));
|
||||
if (isNotBlank(next.getLeft())) {
|
||||
Condition systemPredicate = BinaryCondition.equalTo(myTagDefinitionColumnTagSystem, generatePlaceholder(next.getLeft()));
|
||||
orPredicates.add(ComboCondition.and(typePredicate, systemPredicate, codePredicate));
|
||||
} else {
|
||||
orPredicates.add(ComboCondition.and(typePredicate, codePredicate));
|
||||
}
|
||||
}
|
||||
|
||||
return ComboCondition.or(orPredicates.toArray(new Condition[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DbColumn getResourceIdColumn() {
|
||||
return myColumnResId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,321 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeDeclaredChildDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||
import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
|
||||
import ca.uhn.fhir.rest.param.NumberParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.FhirVersionIndependentConcept;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.healthmarketscience.sqlbuilder.BinaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.Condition;
|
||||
import com.healthmarketscience.sqlbuilder.InCondition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toAndPredicate;
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toOrPredicate;
|
||||
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class TokenPredicateBuilder extends BaseSearchParamPredicateBuilder {
|
||||
|
||||
private final DbColumn myColumnResId;
|
||||
private final DbColumn myColumnHashSystemAndValue;
|
||||
private final DbColumn myColumnHashSystem;
|
||||
private final DbColumn myColumnHashValue;
|
||||
private final DbColumn myColumnSystem;
|
||||
private final DbColumn myColumnValue;
|
||||
|
||||
@Autowired
|
||||
private ITermReadSvc myTerminologySvc;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public TokenPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_SPIDX_TOKEN"));
|
||||
myColumnResId = getTable().addColumn("RES_ID");
|
||||
myColumnHashSystem = getTable().addColumn("HASH_SYS");
|
||||
myColumnHashSystemAndValue = getTable().addColumn("HASH_SYS_AND_VALUE");
|
||||
myColumnHashValue = getTable().addColumn("HASH_VALUE");
|
||||
myColumnSystem = getTable().addColumn("SP_SYSTEM");
|
||||
myColumnValue = getTable().addColumn("SP_VALUE");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DbColumn getResourceIdColumn() {
|
||||
return myColumnResId;
|
||||
}
|
||||
|
||||
public Condition createPredicateToken(Collection<IQueryParameterType> theParameters,
|
||||
String theResourceName,
|
||||
RuntimeSearchParam theSearchParam,
|
||||
RequestPartitionId theRequestPartitionId) {
|
||||
return createPredicateToken(
|
||||
theParameters,
|
||||
theResourceName,
|
||||
theSearchParam,
|
||||
null,
|
||||
theRequestPartitionId);
|
||||
}
|
||||
|
||||
public Condition createPredicateToken(Collection<IQueryParameterType> theParameters,
|
||||
String theResourceName,
|
||||
RuntimeSearchParam theSearchParam,
|
||||
SearchFilterParser.CompareOperation theOperation,
|
||||
RequestPartitionId theRequestPartitionId) {
|
||||
final List<FhirVersionIndependentConcept> codes = new ArrayList<>();
|
||||
String paramName = theSearchParam.getName();
|
||||
|
||||
SearchFilterParser.CompareOperation operation = theOperation;
|
||||
|
||||
TokenParamModifier modifier = null;
|
||||
for (IQueryParameterType nextParameter : theParameters) {
|
||||
|
||||
String code;
|
||||
String system;
|
||||
if (nextParameter instanceof TokenParam) {
|
||||
TokenParam id = (TokenParam) nextParameter;
|
||||
system = id.getSystem();
|
||||
code = (id.getValue());
|
||||
modifier = id.getModifier();
|
||||
} else if (nextParameter instanceof BaseIdentifierDt) {
|
||||
BaseIdentifierDt id = (BaseIdentifierDt) nextParameter;
|
||||
system = id.getSystemElement().getValueAsString();
|
||||
code = (id.getValueElement().getValue());
|
||||
} else if (nextParameter instanceof BaseCodingDt) {
|
||||
BaseCodingDt id = (BaseCodingDt) nextParameter;
|
||||
system = id.getSystemElement().getValueAsString();
|
||||
code = (id.getCodeElement().getValue());
|
||||
} else if (nextParameter instanceof NumberParam) {
|
||||
NumberParam number = (NumberParam) nextParameter;
|
||||
system = null;
|
||||
code = number.getValueAsQueryToken(getFhirContext());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid token type: " + nextParameter.getClass());
|
||||
}
|
||||
|
||||
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
throw new InvalidRequestException(
|
||||
"Parameter[" + paramName + "] has system (" + system.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + system);
|
||||
}
|
||||
|
||||
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
throw new InvalidRequestException(
|
||||
"Parameter[" + paramName + "] has code (" + code.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + code);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process token modifiers (:in, :below, :above)
|
||||
*/
|
||||
|
||||
if (modifier == TokenParamModifier.IN) {
|
||||
codes.addAll(myTerminologySvc.expandValueSet(null, code));
|
||||
} else if (modifier == TokenParamModifier.ABOVE) {
|
||||
system = determineSystemIfMissing(theSearchParam, code, system);
|
||||
validateHaveSystemAndCodeForToken(paramName, code, system);
|
||||
codes.addAll(myTerminologySvc.findCodesAbove(system, code));
|
||||
} else if (modifier == TokenParamModifier.BELOW) {
|
||||
system = determineSystemIfMissing(theSearchParam, code, system);
|
||||
validateHaveSystemAndCodeForToken(paramName, code, system);
|
||||
codes.addAll(myTerminologySvc.findCodesBelow(system, code));
|
||||
} else {
|
||||
if (modifier == TokenParamModifier.NOT && operation == null) {
|
||||
operation = SearchFilterParser.CompareOperation.ne;
|
||||
}
|
||||
codes.add(new FhirVersionIndependentConcept(system, code));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
List<FhirVersionIndependentConcept> sortedCodesList = codes
|
||||
.stream()
|
||||
.filter(t -> t.getCode() != null || t.getSystem() != null)
|
||||
.sorted()
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (codes.isEmpty()) {
|
||||
// This will never match anything
|
||||
setMatchNothing();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
Condition predicate;
|
||||
if (operation == SearchFilterParser.CompareOperation.ne) {
|
||||
|
||||
/*
|
||||
* For a token :not search, we look for index rows that have the right identity (i.e. it's the right resource and
|
||||
* param name) but not the actual provided token value.
|
||||
*/
|
||||
|
||||
long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(getPartitionSettings(), theRequestPartitionId, theResourceName, paramName);
|
||||
Condition hashIdentityPredicate = BinaryCondition.equalTo(getColumnHashIdentity(), generatePlaceholder(hashIdentity));
|
||||
|
||||
Condition hashValuePredicate = createPredicateOrList(theResourceName, theSearchParam.getName(), sortedCodesList, false);
|
||||
predicate = toAndPredicate(hashIdentityPredicate, hashValuePredicate);
|
||||
|
||||
} else {
|
||||
|
||||
predicate = createPredicateOrList(theResourceName, theSearchParam.getName(), sortedCodesList, true);
|
||||
|
||||
}
|
||||
|
||||
return predicate;
|
||||
}
|
||||
|
||||
private String determineSystemIfMissing(RuntimeSearchParam theSearchParam, String code, String theSystem) {
|
||||
String retVal = theSystem;
|
||||
if (retVal == null) {
|
||||
if (theSearchParam != null) {
|
||||
Set<String> valueSetUris = Sets.newHashSet();
|
||||
for (String nextPath : theSearchParam.getPathsSplit()) {
|
||||
Class<? extends IBaseResource> type = getFhirContext().getResourceDefinition(getResourceType()).getImplementingClass();
|
||||
BaseRuntimeChildDefinition def = getFhirContext().newTerser().getDefinition(type, nextPath);
|
||||
if (def instanceof BaseRuntimeDeclaredChildDefinition) {
|
||||
String valueSet = ((BaseRuntimeDeclaredChildDefinition) def).getBindingValueSet();
|
||||
if (isNotBlank(valueSet)) {
|
||||
valueSetUris.add(valueSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (valueSetUris.size() == 1) {
|
||||
String valueSet = valueSetUris.iterator().next();
|
||||
ValueSetExpansionOptions options = new ValueSetExpansionOptions()
|
||||
.setFailOnMissingCodeSystem(false);
|
||||
List<FhirVersionIndependentConcept> candidateCodes = myTerminologySvc.expandValueSet(options, valueSet);
|
||||
for (FhirVersionIndependentConcept nextCandidate : candidateCodes) {
|
||||
if (nextCandidate.getCode().equals(code)) {
|
||||
retVal = nextCandidate.getSystem();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public DbColumn getColumnSystem() {
|
||||
return myColumnSystem;
|
||||
}
|
||||
|
||||
public DbColumn getColumnValue() {
|
||||
return myColumnValue;
|
||||
}
|
||||
|
||||
private void validateHaveSystemAndCodeForToken(String theParamName, String theCode, String theSystem) {
|
||||
String systemDesc = defaultIfBlank(theSystem, "(missing)");
|
||||
String codeDesc = defaultIfBlank(theCode, "(missing)");
|
||||
if (isBlank(theCode)) {
|
||||
String msg = getFhirContext().getLocalizer().getMessage(LegacySearchBuilder.class, "invalidCodeMissingSystem", theParamName, systemDesc, codeDesc);
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
if (isBlank(theSystem)) {
|
||||
String msg = getFhirContext().getLocalizer().getMessage(LegacySearchBuilder.class, "invalidCodeMissingCode", theParamName, systemDesc, codeDesc);
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Condition createPredicateOrList(String theResourceType, String theSearchParamName, List<FhirVersionIndependentConcept> theCodes, boolean theWantEquals) {
|
||||
Condition[] conditions = new Condition[theCodes.size()];
|
||||
|
||||
Long[] hashes = new Long[theCodes.size()];
|
||||
DbColumn[] columns = new DbColumn[theCodes.size()];
|
||||
boolean haveMultipleColumns = false;
|
||||
for (int i = 0; i < conditions.length; i++) {
|
||||
|
||||
FhirVersionIndependentConcept nextToken = theCodes.get(i);
|
||||
long hash;
|
||||
DbColumn column;
|
||||
if (nextToken.getSystem() == null) {
|
||||
hash = ResourceIndexedSearchParamToken.calculateHashValue(getPartitionSettings(), getRequestPartitionId(), theResourceType, theSearchParamName, nextToken.getCode());
|
||||
column = myColumnHashValue;
|
||||
} else if (isBlank(nextToken.getCode())) {
|
||||
hash = ResourceIndexedSearchParamToken.calculateHashSystem(getPartitionSettings(), getRequestPartitionId(), theResourceType, theSearchParamName, nextToken.getSystem());
|
||||
column = myColumnHashSystem;
|
||||
} else {
|
||||
hash = ResourceIndexedSearchParamToken.calculateHashSystemAndValue(getPartitionSettings(), getRequestPartitionId(), theResourceType, theSearchParamName, nextToken.getSystem(), nextToken.getCode());
|
||||
column = myColumnHashSystemAndValue;
|
||||
}
|
||||
hashes[i] = hash;
|
||||
columns[i] = column;
|
||||
if (i > 0 && columns[0] != columns[i]) {
|
||||
haveMultipleColumns = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!haveMultipleColumns && conditions.length > 1) {
|
||||
List<Long> values = Arrays.asList(hashes);
|
||||
InCondition predicate = new InCondition(columns[0], generatePlaceholders(values));
|
||||
if (!theWantEquals) {
|
||||
predicate.setNegate(true);
|
||||
}
|
||||
return predicate;
|
||||
}
|
||||
|
||||
for (int i = 0; i < conditions.length; i++) {
|
||||
String valuePlaceholder = generatePlaceholder(hashes[i]);
|
||||
if (theWantEquals) {
|
||||
conditions[i] = BinaryCondition.equalTo(columns[i], valuePlaceholder);
|
||||
} else {
|
||||
conditions[i] = BinaryCondition.notEqualTo(columns[i], valuePlaceholder);
|
||||
}
|
||||
}
|
||||
if (conditions.length > 1) {
|
||||
if (theWantEquals) {
|
||||
return toOrPredicate(conditions);
|
||||
} else {
|
||||
return toAndPredicate(conditions);
|
||||
}
|
||||
} else {
|
||||
return conditions[0];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.predicate;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import 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.dao.data.IResourceIndexedSearchParamUriDao;
|
||||
import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
|
||||
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
|
||||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.param.UriParamQualifierEnum;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import com.healthmarketscience.sqlbuilder.BinaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.ComboCondition;
|
||||
import com.healthmarketscience.sqlbuilder.Condition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toEqualToOrInPredicate;
|
||||
import static ca.uhn.fhir.jpa.search.builder.predicate.StringPredicateBuilder.createLeftAndRightMatchLikeExpression;
|
||||
import static ca.uhn.fhir.jpa.search.builder.predicate.StringPredicateBuilder.createLeftMatchLikeExpression;
|
||||
import static ca.uhn.fhir.jpa.search.builder.predicate.StringPredicateBuilder.createRightMatchLikeExpression;
|
||||
|
||||
public class UriPredicateBuilder extends BaseSearchParamPredicateBuilder {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(UriPredicateBuilder.class);
|
||||
private final DbColumn myColumnUri;
|
||||
private final DbColumn myColumnHashUri;
|
||||
|
||||
@Autowired
|
||||
private IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
|
||||
@Autowired
|
||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public UriPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_SPIDX_URI"));
|
||||
|
||||
myColumnUri = getTable().addColumn("SP_URI");
|
||||
myColumnHashUri = getTable().addColumn("HASH_URI");
|
||||
}
|
||||
|
||||
|
||||
public Condition addPredicate(List<? extends IQueryParameterType> theUriOrParameterList, String theParamName, SearchFilterParser.CompareOperation theOperation, RequestDetails theRequestDetails) {
|
||||
|
||||
List<Condition> codePredicates = new ArrayList<>();
|
||||
for (IQueryParameterType nextOr : theUriOrParameterList) {
|
||||
|
||||
if (nextOr instanceof UriParam) {
|
||||
UriParam param = (UriParam) nextOr;
|
||||
|
||||
String value = param.getValue();
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (param.getQualifier() == UriParamQualifierEnum.ABOVE) {
|
||||
|
||||
/*
|
||||
* :above is an inefficient query- It means that the user is supplying a more specific URL (say
|
||||
* http://example.com/foo/bar/baz) and that we should match on any URLs that are less
|
||||
* specific but otherwise the same. For example http://example.com and http://example.com/foo would both
|
||||
* match.
|
||||
*
|
||||
* We do this by querying the DB for all candidate URIs and then manually checking each one. This isn't
|
||||
* very efficient, but this is also probably not a very common type of query to do.
|
||||
*
|
||||
* If we ever need to make this more efficient, lucene could certainly be used as an optimization.
|
||||
*/
|
||||
String msg = "Searching for candidate URI:above parameters for Resource["+getResourceType()+"] param["+theParamName+"]";
|
||||
ourLog.info(msg);
|
||||
|
||||
StorageProcessingMessage message = new StorageProcessingMessage();
|
||||
ourLog.warn(msg);
|
||||
message.setMessage(msg);
|
||||
HookParams params = new HookParams()
|
||||
.add(RequestDetails.class, theRequestDetails)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
|
||||
.add(StorageProcessingMessage.class, message);
|
||||
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.JPA_PERFTRACE_WARNING, params);
|
||||
|
||||
Collection<String> candidates = myResourceIndexedSearchParamUriDao.findAllByResourceTypeAndParamName(getResourceType(), theParamName);
|
||||
List<String> toFind = new ArrayList<>();
|
||||
for (String next : candidates) {
|
||||
if (value.length() >= next.length()) {
|
||||
if (value.startsWith(next)) {
|
||||
toFind.add(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toFind.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Condition uriPredicate = toEqualToOrInPredicate(myColumnUri, generatePlaceholders(toFind));
|
||||
Condition hashAndUriPredicate = combineWithHashIdentityPredicate(getResourceType(), theParamName, uriPredicate);
|
||||
codePredicates.add(hashAndUriPredicate);
|
||||
|
||||
} else if (param.getQualifier() == UriParamQualifierEnum.BELOW) {
|
||||
|
||||
Condition uriPredicate = BinaryCondition.like(myColumnUri, generatePlaceholder(createLeftMatchLikeExpression(value)));
|
||||
Condition hashAndUriPredicate = combineWithHashIdentityPredicate(getResourceType(), theParamName, uriPredicate);
|
||||
codePredicates.add(hashAndUriPredicate);
|
||||
|
||||
} else {
|
||||
|
||||
Condition uriPredicate = null;
|
||||
if (theOperation == null || theOperation == SearchFilterParser.CompareOperation.eq) {
|
||||
long hashUri = ResourceIndexedSearchParamUri.calculateHashUri(getPartitionSettings(), getRequestPartitionId(), getResourceType(), theParamName, value);
|
||||
Condition hashPredicate = BinaryCondition.equalTo(myColumnHashUri, generatePlaceholder(hashUri));
|
||||
codePredicates.add(hashPredicate);
|
||||
} else if (theOperation == SearchFilterParser.CompareOperation.ne) {
|
||||
uriPredicate = BinaryCondition.notEqualTo(myColumnUri, generatePlaceholder(value));
|
||||
} else if (theOperation == SearchFilterParser.CompareOperation.co) {
|
||||
uriPredicate = BinaryCondition.like(myColumnUri, generatePlaceholder(createLeftAndRightMatchLikeExpression(value)));
|
||||
} else if (theOperation == SearchFilterParser.CompareOperation.gt) {
|
||||
uriPredicate = BinaryCondition.greaterThan(myColumnUri, generatePlaceholder(value));
|
||||
} else if (theOperation == SearchFilterParser.CompareOperation.lt) {
|
||||
uriPredicate = BinaryCondition.lessThan(myColumnUri, generatePlaceholder(value));
|
||||
} else if (theOperation == SearchFilterParser.CompareOperation.ge) {
|
||||
uriPredicate = BinaryCondition.greaterThanOrEq(myColumnUri, generatePlaceholder(value));
|
||||
} else if (theOperation == SearchFilterParser.CompareOperation.le) {
|
||||
uriPredicate = BinaryCondition.lessThanOrEq(myColumnUri, generatePlaceholder(value));
|
||||
} else if (theOperation == SearchFilterParser.CompareOperation.sw) {
|
||||
uriPredicate = BinaryCondition.like(myColumnUri, generatePlaceholder(createLeftMatchLikeExpression(value)));
|
||||
} else if (theOperation == SearchFilterParser.CompareOperation.ew) {
|
||||
uriPredicate = BinaryCondition.like(myColumnUri, generatePlaceholder(createRightMatchLikeExpression(value)));
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Unsupported operator specified in _filter clause, %s",
|
||||
theOperation.toString()));
|
||||
}
|
||||
|
||||
if (uriPredicate != null) {
|
||||
long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(getPartitionSettings(), getRequestPartitionId(), getResourceType(), theParamName);
|
||||
BinaryCondition hashIdentityPredicate = BinaryCondition.equalTo(getColumnHashIdentity(), generatePlaceholder(hashIdentity));
|
||||
codePredicates.add(ComboCondition.and(hashIdentityPredicate, uriPredicate));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid URI type: " + nextOr.getClass());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* If we haven't found any of the requested URIs in the candidates, then the whole query should match nothing
|
||||
*/
|
||||
if (codePredicates.isEmpty()) {
|
||||
setMatchNothing();
|
||||
return null;
|
||||
}
|
||||
|
||||
ComboCondition orPredicate = ComboCondition.or(codePredicates.toArray(new Condition[0]));
|
||||
Condition outerPredicate = combineWithHashIdentityPredicate(getResourceType(), theParamName, orPredicate);
|
||||
return outerPredicate;
|
||||
|
||||
}
|
||||
|
||||
public DbColumn getColumnValue() {
|
||||
return myColumnUri;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.sql;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Represents the SQL generated by this query
|
||||
*/
|
||||
public class GeneratedSql {
|
||||
public static final Pattern INLINE_EQ_PATTERN = Pattern.compile("=\\s*['0-9]");
|
||||
public static final Pattern INLINE_IN_PATTERN = Pattern.compile("(in|IN)\\s*\\(\\s*['0-9]");
|
||||
private final String mySql;
|
||||
private final List<Object> myBindVariables;
|
||||
private final boolean myMatchNothing;
|
||||
|
||||
public GeneratedSql(boolean theMatchNothing, String theSql, List<Object> theBindVariables) {
|
||||
assert INLINE_EQ_PATTERN.matcher(theSql).find() == false : "Non-bound SQL parameter found: " + theSql;
|
||||
assert INLINE_IN_PATTERN.matcher(theSql).find() == false : "Non-bound SQL parameter found: " + theSql;
|
||||
|
||||
myMatchNothing = theMatchNothing;
|
||||
mySql = theSql;
|
||||
myBindVariables = theBindVariables;
|
||||
}
|
||||
|
||||
public boolean isMatchNothing() {
|
||||
return myMatchNothing;
|
||||
}
|
||||
|
||||
public List<Object> getBindVariables() {
|
||||
return myBindVariables;
|
||||
}
|
||||
|
||||
public String getSql() {
|
||||
return mySql;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,515 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.sql;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.config.HibernateDialectProvider;
|
||||
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.CoordsPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.ForcedIdPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.NumberPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.QuantityPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.ResourceIdPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.ResourceLinkPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.ResourceTablePredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.SearchParamPresentPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.SourcePredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.StringPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.TagPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.TokenPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.UriPredicateBuilder;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
import com.healthmarketscience.sqlbuilder.BinaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.ComboCondition;
|
||||
import com.healthmarketscience.sqlbuilder.Condition;
|
||||
import com.healthmarketscience.sqlbuilder.FunctionCall;
|
||||
import com.healthmarketscience.sqlbuilder.InCondition;
|
||||
import com.healthmarketscience.sqlbuilder.OrderObject;
|
||||
import com.healthmarketscience.sqlbuilder.SelectQuery;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.Join;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbJoin;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbSchema;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbSpec;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
import org.hibernate.engine.spi.RowSelection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
|
||||
public class SearchQueryBuilder {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(SearchQueryBuilder.class);
|
||||
private final String myBindVariableSubstitutionBase;
|
||||
private final ArrayList<Object> myBindVariableValues;
|
||||
private final DbSpec mySpec;
|
||||
private final DbSchema mySchema;
|
||||
private final SelectQuery mySelect;
|
||||
private final PartitionSettings myPartitionSettings;
|
||||
private final RequestPartitionId myRequestPartitionId;
|
||||
private final String myResourceType;
|
||||
private final ModelConfig myModelConfig;
|
||||
private final FhirContext myFhirContext;
|
||||
private final SqlObjectFactory mySqlBuilderFactory;
|
||||
private final boolean myCountQuery;
|
||||
private final Dialect myDialect;
|
||||
private boolean myMatchNothing;
|
||||
private ResourceTablePredicateBuilder myResourceTableRoot;
|
||||
private boolean myHaveAtLeastOnePredicate;
|
||||
private BaseJoiningPredicateBuilder myFirstPredicateBuilder;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public SearchQueryBuilder(FhirContext theFhirContext, ModelConfig theModelConfig, PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, SqlObjectFactory theSqlBuilderFactory, HibernateDialectProvider theDialectProvider, boolean theCountQuery) {
|
||||
this(theFhirContext, theModelConfig, thePartitionSettings, theRequestPartitionId, theResourceType, theSqlBuilderFactory, UUID.randomUUID().toString() + "-", theDialectProvider.getDialect(), theCountQuery, new ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for child SQL Builders
|
||||
*/
|
||||
private SearchQueryBuilder(FhirContext theFhirContext, ModelConfig theModelConfig, PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, SqlObjectFactory theSqlBuilderFactory, String theBindVariableSubstitutionBase, Dialect theDialect, boolean theCountQuery, ArrayList<Object> theBindVariableValues) {
|
||||
myFhirContext = theFhirContext;
|
||||
myModelConfig = theModelConfig;
|
||||
myPartitionSettings = thePartitionSettings;
|
||||
myRequestPartitionId = theRequestPartitionId;
|
||||
myResourceType = theResourceType;
|
||||
mySqlBuilderFactory = theSqlBuilderFactory;
|
||||
myCountQuery = theCountQuery;
|
||||
myDialect = theDialect;
|
||||
|
||||
mySpec = new DbSpec();
|
||||
mySchema = mySpec.addDefaultSchema();
|
||||
mySelect = new SelectQuery();
|
||||
|
||||
myBindVariableSubstitutionBase = theBindVariableSubstitutionBase;
|
||||
myBindVariableValues = theBindVariableValues;
|
||||
}
|
||||
|
||||
public FhirContext getFhirContext() {
|
||||
return myFhirContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
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
|
||||
*/
|
||||
public CoordsPredicateBuilder addCoordsPredicateBuilder(@Nullable DbColumn theSourceJoinColumn) {
|
||||
CoordsPredicateBuilder retVal = mySqlBuilderFactory.coordsPredicateBuilder(this);
|
||||
addTable(retVal, theSourceJoinColumn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a DATE search parameter
|
||||
*/
|
||||
public DatePredicateBuilder addDatePredicateBuilder(@Nullable DbColumn theSourceJoinColumn) {
|
||||
DatePredicateBuilder retVal = mySqlBuilderFactory.dateIndexTable(this);
|
||||
addTable(retVal, theSourceJoinColumn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and return a predicate builder for selecting a forced ID. This is only intended for use with sorts so it can not
|
||||
* be the root query.
|
||||
*/
|
||||
public ForcedIdPredicateBuilder addForcedIdPredicateBuilder(@Nonnull DbColumn theSourceJoinColumn) {
|
||||
Validate.isTrue(theSourceJoinColumn != null);
|
||||
|
||||
ForcedIdPredicateBuilder retVal = mySqlBuilderFactory.newForcedIdPredicateBuilder(this);
|
||||
addTable(retVal, theSourceJoinColumn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a NUMBER search parameter
|
||||
*/
|
||||
public NumberPredicateBuilder addNumberPredicateBuilder(@Nullable DbColumn theSourceJoinColumn) {
|
||||
NumberPredicateBuilder retVal = mySqlBuilderFactory.numberIndexTable(this);
|
||||
addTable(retVal, theSourceJoinColumn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a QUANTITY search parameter
|
||||
*/
|
||||
public ResourceTablePredicateBuilder addResourceTablePredicateBuilder(@Nullable DbColumn theSourceJoinColumn) {
|
||||
ResourceTablePredicateBuilder retVal = mySqlBuilderFactory.resourceTable(this);
|
||||
addTable(retVal, theSourceJoinColumn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a QUANTITY search parameter
|
||||
*/
|
||||
public QuantityPredicateBuilder addQuantityPredicateBuilder(@Nullable DbColumn theSourceJoinColumn) {
|
||||
QuantityPredicateBuilder retVal = mySqlBuilderFactory.quantityIndexTable(this);
|
||||
addTable(retVal, theSourceJoinColumn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a <code>_source</code> search parameter
|
||||
*/
|
||||
public SourcePredicateBuilder addSourcePredicateBuilder(@Nullable DbColumn theSourceJoinColumn) {
|
||||
SourcePredicateBuilder retVal = mySqlBuilderFactory.newSourcePredicateBuilder(this);
|
||||
addTable(retVal, theSourceJoinColumn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a REFERENCE search parameter
|
||||
*/
|
||||
public ResourceLinkPredicateBuilder addReferencePredicateBuilder(QueryStack theQueryStack, @Nullable DbColumn theSourceJoinColumn) {
|
||||
ResourceLinkPredicateBuilder retVal = mySqlBuilderFactory.referenceIndexTable(theQueryStack, this, false);
|
||||
addTable(retVal, theSourceJoinColumn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a reosource link where the
|
||||
* source and target are reversed. This is used for _has queries.
|
||||
*/
|
||||
public ResourceLinkPredicateBuilder addReferencePredicateBuilderReversed(QueryStack theQueryStack, DbColumn theSourceJoinColumn) {
|
||||
ResourceLinkPredicateBuilder retVal = mySqlBuilderFactory.referenceIndexTable(theQueryStack, this, true);
|
||||
addTable(retVal, theSourceJoinColumn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a STRING search parameter
|
||||
*/
|
||||
public StringPredicateBuilder addStringPredicateBuilder(@Nullable DbColumn theSourceJoinColumn) {
|
||||
StringPredicateBuilder retVal = mySqlBuilderFactory.stringIndexTable(this);
|
||||
addTable(retVal, theSourceJoinColumn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a <code>_tag</code> search parameter
|
||||
*/
|
||||
public TagPredicateBuilder addTagPredicateBuilder(@Nullable DbColumn theSourceJoinColumn) {
|
||||
TagPredicateBuilder retVal = mySqlBuilderFactory.newTagPredicateBuilder(this);
|
||||
addTable(retVal, theSourceJoinColumn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a TOKEN search parameter
|
||||
*/
|
||||
public TokenPredicateBuilder addTokenPredicateBuilder(@Nullable DbColumn theSourceJoinColumn) {
|
||||
TokenPredicateBuilder retVal = mySqlBuilderFactory.tokenIndexTable(this);
|
||||
addTable(retVal, theSourceJoinColumn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a <code>:missing</code> search parameter
|
||||
*/
|
||||
public SearchParamPresentPredicateBuilder addSearchParamPresentPredicateBuilder(@Nullable DbColumn theSourceJoinColumn) {
|
||||
SearchParamPresentPredicateBuilder retVal = mySqlBuilderFactory.searchParamPresentPredicateBuilder(this);
|
||||
addTable(retVal, theSourceJoinColumn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and return a predicate builder (or a root query if no root query exists yet) for selecting on a URI search parameter
|
||||
*/
|
||||
public UriPredicateBuilder addUriPredicateBuilder(@Nullable DbColumn theSourceJoinColumn) {
|
||||
UriPredicateBuilder retVal = mySqlBuilderFactory.uriIndexTable(this);
|
||||
addTable(retVal, theSourceJoinColumn);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
public ResourceIdPredicateBuilder newResourceIdBuilder() {
|
||||
return mySqlBuilderFactory.resourceId(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add and return a predicate builder (or a root query if no root query exists yet) for an arbitrary table
|
||||
*/
|
||||
private void addTable(BaseJoiningPredicateBuilder thePredicateBuilder, @Nullable DbColumn theSourceJoinColumn) {
|
||||
if (theSourceJoinColumn != null) {
|
||||
DbTable fromTable = theSourceJoinColumn.getTable();
|
||||
DbTable toTable = thePredicateBuilder.getTable();
|
||||
DbColumn toColumn = thePredicateBuilder.getResourceIdColumn();
|
||||
addJoin(fromTable, toTable, theSourceJoinColumn, toColumn);
|
||||
} else {
|
||||
if (myFirstPredicateBuilder == null) {
|
||||
ResourceTablePredicateBuilder root;
|
||||
if (thePredicateBuilder instanceof ResourceTablePredicateBuilder) {
|
||||
root = (ResourceTablePredicateBuilder) thePredicateBuilder;
|
||||
} else {
|
||||
root = mySqlBuilderFactory.resourceTable(this);
|
||||
}
|
||||
|
||||
if (myCountQuery) {
|
||||
mySelect.addCustomColumns(FunctionCall.count().setIsDistinct(true).addColumnParams(root.getResourceIdColumn()));
|
||||
} else {
|
||||
mySelect.addColumns(root.getResourceIdColumn());
|
||||
}
|
||||
mySelect.addFromTable(root.getTable());
|
||||
myFirstPredicateBuilder = root;
|
||||
|
||||
if (thePredicateBuilder instanceof ResourceTablePredicateBuilder) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DbTable fromTable = myFirstPredicateBuilder.getTable();
|
||||
DbTable toTable = thePredicateBuilder.getTable();
|
||||
DbColumn fromColumn = myFirstPredicateBuilder.getResourceIdColumn();
|
||||
DbColumn toColumn = thePredicateBuilder.getResourceIdColumn();
|
||||
addJoin(fromTable, toTable, fromColumn, toColumn);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void addJoin(DbTable theFromTable, DbTable theToTable, DbColumn theFromColumn, DbColumn theToColumn) {
|
||||
Join join = new DbJoin(mySpec, theFromTable, theToTable, new DbColumn[]{theFromColumn}, new DbColumn[]{theToColumn});
|
||||
mySelect.addJoins(SelectQuery.JoinType.LEFT_OUTER, join);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and return the SQL generated by this builder
|
||||
*/
|
||||
public GeneratedSql generate(@Nullable Integer theOffset, @Nullable Integer theMaxResultsToFetch) {
|
||||
|
||||
getOrCreateFirstPredicateBuilder();
|
||||
|
||||
mySelect.validate();
|
||||
String sql = mySelect.toString();
|
||||
|
||||
List<Object> bindVariables = new ArrayList<>();
|
||||
while (true) {
|
||||
|
||||
int idx = sql.indexOf(myBindVariableSubstitutionBase);
|
||||
if (idx == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
int endIdx = sql.indexOf("'", idx + myBindVariableSubstitutionBase.length());
|
||||
String substitutionIndexString = sql.substring(idx + myBindVariableSubstitutionBase.length(), endIdx);
|
||||
int substitutionIndex = Integer.parseInt(substitutionIndexString);
|
||||
bindVariables.add(myBindVariableValues.get(substitutionIndex));
|
||||
|
||||
sql = sql.substring(0, idx - 1) + "?" + sql.substring(endIdx + 1);
|
||||
}
|
||||
|
||||
Integer maxResultsToFetch = theMaxResultsToFetch;
|
||||
Integer offset = theOffset;
|
||||
if (offset != null && offset == 0) {
|
||||
offset = null;
|
||||
}
|
||||
if (maxResultsToFetch != null || offset != null) {
|
||||
|
||||
maxResultsToFetch = defaultIfNull(maxResultsToFetch, 10000);
|
||||
|
||||
sql = "SELECT " +
|
||||
myFirstPredicateBuilder.getResourceIdColumn().getColumnNameSQL() +
|
||||
" FROM ( " + sql + " ) " +
|
||||
" AS " + myFirstPredicateBuilder.getResourceIdColumn().getColumnNameSQL();
|
||||
|
||||
LimitHandler limitHandler = myDialect.getLimitHandler();
|
||||
RowSelection selection = new RowSelection();
|
||||
selection.setFirstRow(offset);
|
||||
selection.setMaxRows(maxResultsToFetch);
|
||||
sql = limitHandler.processSql(sql, selection);
|
||||
|
||||
if (limitHandler.supportsLimit()) {
|
||||
bindVariables.add(maxResultsToFetch);
|
||||
}
|
||||
if (limitHandler.supportsLimitOffset() && offset != null) {
|
||||
bindVariables.add(offset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new GeneratedSql(myMatchNothing, sql, bindVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* If at least one predicate builder already exists, return the last one added to the chain. If none has been selected, create a builder on HFJ_RESOURCE, add it and return it.
|
||||
*/
|
||||
public BaseJoiningPredicateBuilder getOrCreateFirstPredicateBuilder() {
|
||||
if (myFirstPredicateBuilder == null) {
|
||||
getOrCreateResourceTablePredicateBuilder();
|
||||
}
|
||||
return myFirstPredicateBuilder;
|
||||
}
|
||||
|
||||
public ResourceTablePredicateBuilder getOrCreateResourceTablePredicateBuilder() {
|
||||
if (myResourceTableRoot == null) {
|
||||
ResourceTablePredicateBuilder resourceTable = mySqlBuilderFactory.resourceTable(this);
|
||||
addTable(resourceTable, null);
|
||||
Condition typeAndDeletionPredicate = resourceTable.createResourceTypeAndNonDeletedPredicates();
|
||||
addPredicate(typeAndDeletionPredicate);
|
||||
myResourceTableRoot = resourceTable;
|
||||
}
|
||||
return myResourceTableRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* The SQL Builder library has one annoying limitation, which is that it does not use/understand bind variables
|
||||
* for its generated SQL. So we work around this by replacing our contents with a string in the SQL consisting
|
||||
* of <code>[random UUID]-[value index]</code> and then
|
||||
*/
|
||||
public String generatePlaceholder(Object theValue) {
|
||||
String placeholder = myBindVariableSubstitutionBase + myBindVariableValues.size();
|
||||
myBindVariableValues.add(theValue);
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
public List<String> generatePlaceholders(Collection<?> theValues) {
|
||||
return theValues
|
||||
.stream()
|
||||
.map(t -> generatePlaceholder(t))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
public void setMatchNothing() {
|
||||
myMatchNothing = true;
|
||||
}
|
||||
|
||||
public DbTable addTable(String theTableName) {
|
||||
return mySchema.addTable(theTableName);
|
||||
}
|
||||
|
||||
public PartitionSettings getPartitionSettings() {
|
||||
return myPartitionSettings;
|
||||
}
|
||||
|
||||
public RequestPartitionId getRequestPartitionId() {
|
||||
return myRequestPartitionId;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return myResourceType;
|
||||
}
|
||||
|
||||
public ModelConfig getModelConfig() {
|
||||
return myModelConfig;
|
||||
}
|
||||
|
||||
public void addPredicate(@Nonnull Condition theCondition) {
|
||||
assert theCondition != null;
|
||||
mySelect.addCondition(theCondition);
|
||||
myHaveAtLeastOnePredicate = true;
|
||||
}
|
||||
|
||||
public ComboCondition addPredicateLastUpdated(DateRangeParam theDateRange) {
|
||||
ResourceTablePredicateBuilder resourceTableRoot = getOrCreateResourceTablePredicateBuilder();
|
||||
|
||||
List<Condition> conditions = new ArrayList<>(2);
|
||||
if (theDateRange.getLowerBoundAsInstant() != null) {
|
||||
BinaryCondition condition = createConditionForValueWithComparator(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, resourceTableRoot.getLastUpdatedColumn(), theDateRange.getLowerBoundAsInstant());
|
||||
conditions.add(condition);
|
||||
}
|
||||
|
||||
if (theDateRange.getUpperBoundAsInstant() != null) {
|
||||
BinaryCondition condition = createConditionForValueWithComparator(ParamPrefixEnum.LESSTHAN_OR_EQUALS, resourceTableRoot.getLastUpdatedColumn(), theDateRange.getUpperBoundAsInstant());
|
||||
conditions.add(condition);
|
||||
}
|
||||
|
||||
return ComboCondition.and(conditions.toArray(new Condition[0]));
|
||||
}
|
||||
|
||||
|
||||
public void addResourceIdsPredicate(List<Long> thePidList) {
|
||||
DbColumn resourceIdColumn = getOrCreateFirstPredicateBuilder().getResourceIdColumn();
|
||||
InCondition predicate = new InCondition(resourceIdColumn, generatePlaceholders(thePidList));
|
||||
addPredicate(predicate);
|
||||
}
|
||||
|
||||
|
||||
public BinaryCondition createConditionForValueWithComparator(ParamPrefixEnum theComparator, DbColumn theColumn, Object theValue) {
|
||||
switch (theComparator) {
|
||||
case LESSTHAN:
|
||||
return BinaryCondition.lessThan(theColumn, generatePlaceholder(theValue));
|
||||
case LESSTHAN_OR_EQUALS:
|
||||
return BinaryCondition.lessThanOrEq(theColumn, generatePlaceholder(theValue));
|
||||
case GREATERTHAN:
|
||||
return BinaryCondition.greaterThan(theColumn, generatePlaceholder(theValue));
|
||||
case GREATERTHAN_OR_EQUALS:
|
||||
return BinaryCondition.greaterThanOrEq(theColumn, generatePlaceholder(theValue));
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
public SearchQueryBuilder newChildSqlBuilder() {
|
||||
return new SearchQueryBuilder(myFhirContext, myModelConfig, myPartitionSettings, myRequestPartitionId, myResourceType, mySqlBuilderFactory, myBindVariableSubstitutionBase, myDialect, false, myBindVariableValues);
|
||||
}
|
||||
|
||||
public SelectQuery getSelect() {
|
||||
return mySelect;
|
||||
}
|
||||
|
||||
public boolean haveAtLeastOnePredicate() {
|
||||
return myHaveAtLeastOnePredicate;
|
||||
}
|
||||
|
||||
public void addSort(DbColumn theColumnValueNormalized, boolean theAscending) {
|
||||
OrderObject.NullOrder nullOrder = OrderObject.NullOrder.LAST;
|
||||
addSort(theColumnValueNormalized, theAscending, nullOrder);
|
||||
}
|
||||
|
||||
public void addSort(DbColumn theTheColumnValueNormalized, boolean theTheAscending, OrderObject.NullOrder theNullOrder) {
|
||||
OrderObject.Dir direction = theTheAscending ? OrderObject.Dir.ASCENDING : OrderObject.Dir.DESCENDING;
|
||||
OrderObject orderObject = new OrderObject(direction, theTheColumnValueNormalized);
|
||||
orderObject.setNullOrder(theNullOrder);
|
||||
mySelect.addCustomOrderings(orderObject);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.sql;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.util.ScrollableResultsIterator;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.IoUtil;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.ScrollableResults;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.PersistenceContextType;
|
||||
import javax.persistence.Query;
|
||||
import java.io.Closeable;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class SearchQueryExecutor implements Iterator<Long>, Closeable {
|
||||
|
||||
private static final Long NO_MORE = -1L;
|
||||
private static final SearchQueryExecutor NO_VALUE_EXECUTOR = new SearchQueryExecutor();
|
||||
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(SearchQueryExecutor.class);
|
||||
private final GeneratedSql myGeneratedSql;
|
||||
private final Integer myMaxResultsToFetch;
|
||||
|
||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||
private EntityManager myEntityManager;
|
||||
private boolean myQueryInitialized;
|
||||
private Connection myConnection;
|
||||
private PreparedStatement myStatement;
|
||||
private ScrollableResultsIterator<Number> myResultSet;
|
||||
private Long myNext;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public SearchQueryExecutor(GeneratedSql theGeneratedSql, Integer theMaxResultsToFetch) {
|
||||
Validate.notNull(theGeneratedSql, "theGeneratedSql must not be null");
|
||||
myGeneratedSql = theGeneratedSql;
|
||||
myQueryInitialized = false;
|
||||
myMaxResultsToFetch = theMaxResultsToFetch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal constructor for empty executor
|
||||
*/
|
||||
private SearchQueryExecutor() {
|
||||
assert NO_MORE != null;
|
||||
|
||||
myGeneratedSql = null;
|
||||
myMaxResultsToFetch = null;
|
||||
myNext = NO_MORE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
IoUtil.closeQuietly(myResultSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
fetchNext();
|
||||
return !NO_MORE.equals(myNext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long next() {
|
||||
fetchNext();
|
||||
Validate.isTrue(hasNext(), "Can not call next() right now, no data remains");
|
||||
Long next = myNext;
|
||||
myNext = null;
|
||||
return next;
|
||||
}
|
||||
|
||||
private void fetchNext() {
|
||||
if (myNext == null) {
|
||||
String sql = myGeneratedSql.getSql();
|
||||
Object[] args = myGeneratedSql.getBindVariables().toArray(EMPTY_OBJECT_ARRAY);
|
||||
|
||||
try {
|
||||
if (!myQueryInitialized) {
|
||||
|
||||
/*
|
||||
* Note that we use the spring managed connection, and the expectation is that a transaction that
|
||||
* is managed by Spring has been started before this method is called.
|
||||
*/
|
||||
assert TransactionSynchronizationManager.isSynchronizationActive();
|
||||
|
||||
Query nativeQuery = myEntityManager.createNativeQuery(sql);
|
||||
org.hibernate.query.Query<?> hibernateQuery = (org.hibernate.query.Query<?>) nativeQuery;
|
||||
for (int i = 1; i <= args.length; i++) {
|
||||
hibernateQuery.setParameter(i, args[i - 1]);
|
||||
}
|
||||
|
||||
ourLog.trace("About to execute SQL: {}", sql);
|
||||
|
||||
ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY);
|
||||
myResultSet = new ScrollableResultsIterator<>(scrollableResults);
|
||||
myQueryInitialized = true;
|
||||
|
||||
}
|
||||
|
||||
if (myResultSet == null || !myResultSet.hasNext()) {
|
||||
myNext = NO_MORE;
|
||||
} else {
|
||||
Number next = myResultSet.next();
|
||||
myNext = next.longValue();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failed to create or execute SQL query", e);
|
||||
close();
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static SearchQueryExecutor emptyExecutor() {
|
||||
return NO_VALUE_EXECUTOR;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.sql;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.search.builder.QueryStack;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.CompositeUniqueSearchParameterPredicateBuilder;
|
||||
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;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.NumberPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.QuantityPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.ResourceIdPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.ResourceLinkPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.ResourceTablePredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.SearchParamPresentPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.SourcePredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.StringPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.TagPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.TokenPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.UriPredicateBuilder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
public class SqlObjectFactory {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext myApplicationContext;
|
||||
|
||||
public CompositeUniqueSearchParameterPredicateBuilder newCompositeUniqueSearchParameterPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return myApplicationContext.getBean(CompositeUniqueSearchParameterPredicateBuilder.class, theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
public CoordsPredicateBuilder coordsPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return myApplicationContext.getBean(CoordsPredicateBuilder.class, theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
public DatePredicateBuilder dateIndexTable(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return myApplicationContext.getBean(DatePredicateBuilder.class, theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
public ForcedIdPredicateBuilder newForcedIdPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return myApplicationContext.getBean(ForcedIdPredicateBuilder.class, theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
public NumberPredicateBuilder numberIndexTable(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return myApplicationContext.getBean(NumberPredicateBuilder.class, theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
public QuantityPredicateBuilder quantityIndexTable(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return myApplicationContext.getBean(QuantityPredicateBuilder.class, theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
public ResourceLinkPredicateBuilder referenceIndexTable(QueryStack theQueryStack, SearchQueryBuilder theSearchSqlBuilder, boolean theReversed) {
|
||||
return myApplicationContext.getBean(ResourceLinkPredicateBuilder.class, theQueryStack, theSearchSqlBuilder, theReversed);
|
||||
}
|
||||
|
||||
public ResourceTablePredicateBuilder resourceTable(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return myApplicationContext.getBean(ResourceTablePredicateBuilder.class, theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
public ResourceIdPredicateBuilder resourceId(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return myApplicationContext.getBean(ResourceIdPredicateBuilder.class, theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
public SearchParamPresentPredicateBuilder searchParamPresentPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return myApplicationContext.getBean(SearchParamPresentPredicateBuilder.class, theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
public StringPredicateBuilder stringIndexTable(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return myApplicationContext.getBean(StringPredicateBuilder.class, theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
public TokenPredicateBuilder tokenIndexTable(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return myApplicationContext.getBean(TokenPredicateBuilder.class, theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
public UriPredicateBuilder uriIndexTable(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return myApplicationContext.getBean(UriPredicateBuilder.class, theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
public TagPredicateBuilder newTagPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return myApplicationContext.getBean(TagPredicateBuilder.class, theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
public SourcePredicateBuilder newSourcePredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
|
||||
return myApplicationContext.getBean(SourcePredicateBuilder.class, theSearchSqlBuilder);
|
||||
}
|
||||
|
||||
public SearchQueryExecutor newSearchQueryExecutor(GeneratedSql theGeneratedSql, Integer theMaxResultsToFetch) {
|
||||
return myApplicationContext.getBean(SearchQueryExecutor.class, theGeneratedSql, theMaxResultsToFetch);
|
||||
}
|
||||
}
|
|
@ -20,7 +20,8 @@ package ca.uhn.fhir.jpa.util;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.hibernate.engine.jdbc.internal.BasicFormatterImpl;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.trim;
|
||||
|
||||
|
|
|
@ -21,13 +21,13 @@ import static org.mockito.Mockito.mock;
|
|||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class SearchBuilderTest {
|
||||
public class LegacySearchBuilderTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void testIncludeIterator() {
|
||||
BaseHapiFhirDao<?> mockDao = mock(BaseHapiFhirDao.class);
|
||||
SearchBuilder searchBuilder = new SearchBuilder(mockDao, null, null);
|
||||
LegacySearchBuilder searchBuilder = new LegacySearchBuilder(mockDao, null, null);
|
||||
searchBuilder.setDaoConfigForUnitTest(new DaoConfig());
|
||||
searchBuilder.setParamsForUnitTest(new SearchParameterMap());
|
||||
EntityManager mockEntityManager = mock(EntityManager.class);
|
||||
|
@ -46,7 +46,7 @@ public class SearchBuilderTest {
|
|||
resultList.add(link);
|
||||
when(mockQuery.getResultList()).thenReturn(resultList);
|
||||
|
||||
SearchBuilder.IncludesIterator includesIterator = searchBuilder.new IncludesIterator(pidSet, null);
|
||||
LegacySearchBuilder.IncludesIterator includesIterator = searchBuilder.new IncludesIterator(pidSet, null);
|
||||
// hasNext() should return false if the pid added was already on our list going in.
|
||||
assertFalse(includesIterator.hasNext());
|
||||
}
|
|
@ -493,14 +493,16 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
|
|||
assertThat(foundResources, contains(p2id.getValue()));
|
||||
|
||||
// Search by chain
|
||||
map = new SearchParameterMap();
|
||||
myCaptureQueriesListener.clear();
|
||||
map = new SearchParameterMap().setLoadSynchronous(true);
|
||||
map.add("sibling", new ReferenceParam("name", "P1"));
|
||||
results = myPatientDao.search(map);
|
||||
foundResources = toUnqualifiedVersionlessIdValues(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(foundResources, contains(p2id.getValue()));
|
||||
|
||||
// Search by two level chain
|
||||
map = new SearchParameterMap();
|
||||
map = new SearchParameterMap().setLoadSynchronous(true);
|
||||
map.add("patient", new ReferenceParam("sibling.name", "P1"));
|
||||
results = myAppointmentDao.search(map);
|
||||
foundResources = toUnqualifiedVersionlessIdValues(results);
|
||||
|
@ -1031,7 +1033,7 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
|
|||
myPatientDao.search(map).size();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, active, address, address-city, address-country, address-postalcode, address-state, address-use, animal-breed, animal-species, birthdate, careprovider, deathdate, deceased, email, family, gender, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
assertEquals("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, _lastUpdated, active, address, address-city, address-country, address-postalcode, address-state, address-use, animal-breed, animal-species, birthdate, careprovider, deathdate, deceased, email, family, gender, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1068,7 +1070,7 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
|
|||
myPatientDao.search(map).size();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, active, address, address-city, address-country, address-postalcode, address-state, address-use, animal-breed, animal-species, birthdate, careprovider, deathdate, deceased, email, family, gender, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
assertEquals("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, _lastUpdated, active, address, address-city, address-country, address-postalcode, address-state, address-use, animal-breed, animal-species, birthdate, careprovider, deathdate, deceased, email, family, gender, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
}
|
||||
|
||||
// Try with normal gender SP
|
||||
|
|
|
@ -11,7 +11,6 @@ import javax.servlet.http.HttpServletRequest;
|
|||
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
|
@ -24,7 +23,6 @@ import ca.uhn.fhir.model.primitive.Base64BinaryDt;
|
|||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.param.*;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
|
||||
|
||||
|
@ -360,7 +358,7 @@ public class FhirResourceDaoDstu2SearchFtTest extends BaseJpaDstu2Test {
|
|||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.patientTypeEverything(request, null, null, null, null, param, null, null, mySrd));
|
||||
assertThat(actual, containsInAnyOrder(ptId1, obsId1, obsId4, devId1));
|
||||
assertThat(actual, containsInAnyOrder(ptId2, ptId1, obsId1, obsId4, devId1));
|
||||
|
||||
/*
|
||||
* Make one previous match no longer match
|
||||
|
|
|
@ -37,7 +37,6 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
|
|
@ -69,14 +69,12 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hamcrest.core.StringContains;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
@ -1600,7 +1598,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
found = toList(myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_BIRTHDATE + "AAAA", new DateParam(ParamPrefixEnum.GREATERTHAN, "2000-01-01"))));
|
||||
assertEquals(0, found.size());
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unknown search parameter \"birthdateAAAA\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, active, address, address-city, address-country, address-postalcode, address-state, address-use, animal-breed, animal-species, birthdate, careprovider, deathdate, deceased, email, family, gender, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
assertEquals("Unknown search parameter \"birthdateAAAA\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, _lastUpdated, active, address, address-city, address-country, address-postalcode, address-state, address-use, animal-breed, animal-species, birthdate, careprovider, deathdate, deceased, email, family, gender, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1629,13 +1627,13 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
|
||||
myObservationDao.create(obs, mySrd);
|
||||
|
||||
List<Observation> found = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("value-quantity", new QuantityDt(111))));
|
||||
List<Observation> found = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("value-quantity", new QuantityParam(111))));
|
||||
assertEquals(1, found.size());
|
||||
|
||||
found = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("value-quantity", new QuantityDt(112))));
|
||||
found = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("value-quantity", new QuantityParam(112))));
|
||||
assertEquals(0, found.size());
|
||||
|
||||
found = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("value-quantity", new QuantityDt(212))));
|
||||
found = toList(myObservationDao.search(new SearchParameterMap().setLoadSynchronous(true).add("value-quantity", new QuantityParam(212))));
|
||||
assertEquals(0, found.size());
|
||||
|
||||
}
|
||||
|
@ -1665,7 +1663,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
// found = ourPatientDao.search(Patient.SP_GENDER, new IdentifierDt(null, "F"));
|
||||
// assertEquals(0, found.size());
|
||||
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||
map.add(Patient.SP_IDENTIFIER, new IdentifierDt("urn:system", "001testPersistSearchParams"));
|
||||
map.add(Patient.SP_GENDER, new IdentifierDt("urn:some:wrong:system", AdministrativeGenderEnum.MALE.getCode()));
|
||||
found = toList(myPatientDao.search(map));
|
||||
|
@ -2112,14 +2110,14 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
pm.setSort(new SortSpec(Patient.SP_BIRTHDATE));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id4, id1, id2, id3));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testtestSortByDate"));
|
||||
pm.setSort(new SortSpec(Patient.SP_BIRTHDATE).setOrder(SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id4, id1, id2, id3));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testtestSortByDate"));
|
||||
|
@ -2164,21 +2162,21 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
pm.setSort(new SortSpec(BaseResource.SP_RES_ID));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(5, actual.size());
|
||||
assertThat(actual, contains(id1, id2, id3, id4, idMethodName));
|
||||
assertThat(actual, contains(idMethodName, id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
|
||||
pm.setSort(new SortSpec(BaseResource.SP_RES_ID).setOrder(SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(5, actual.size());
|
||||
assertThat(actual, contains(id1, id2, id3, id4, idMethodName));
|
||||
assertThat(actual, contains(idMethodName, id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
|
||||
pm.setSort(new SortSpec(BaseResource.SP_RES_ID).setOrder(SortOrderEnum.DESC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(5, actual.size());
|
||||
assertThat(actual, contains(idMethodName, id4, id3, id2, id1));
|
||||
assertThat(actual, contains(id4, id3, id2, id1, idMethodName));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -2404,14 +2402,14 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
pm.setSort(new SortSpec(Patient.SP_FAMILY));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id4, id1, id2, id3));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", string));
|
||||
pm.setSort(new SortSpec(Patient.SP_FAMILY).setOrder(SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id4, id1, id2, id3));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", string));
|
||||
|
|
|
@ -49,6 +49,7 @@ import java.io.InputStream;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
@ -429,7 +430,14 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
|
|||
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.POST).setIfNoneExist("Patient?identifier=urn%3Asystem%7C" + methodName);
|
||||
|
||||
try {
|
||||
myCaptureQueriesListener.clear();
|
||||
mySystemDao.transaction(mySrd, request);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
|
||||
runInTransaction(()->{
|
||||
ourLog.info("Tokens:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||
});
|
||||
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals(e.getMessage(), "Unable to process Transaction - Request would cause multiple resources to match URL: \"Patient?identifier=urn%3Asystem%7CtestTransactionCreateWithDuplicateMatchUrl01\". Does transaction request contain duplicates?");
|
||||
|
|
|
@ -1015,7 +1015,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
|||
myPatientDao.search(map).size();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, active, address, address-city, address-country, address-postalcode, address-state, address-use, animal-breed, animal-species, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
assertEquals("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, _lastUpdated, active, address, address-city, address-country, address-postalcode, address-state, address-use, animal-breed, animal-species, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1053,7 +1053,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
|||
myPatientDao.search(map).size();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, active, address, address-city, address-country, address-postalcode, address-state, address-use, animal-breed, animal-species, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
assertEquals("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, _lastUpdated, active, address, address-city, address-country, address-postalcode, address-state, address-use, animal-breed, animal-species, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
}
|
||||
|
||||
// Try with normal gender SP
|
||||
|
|
|
@ -457,7 +457,7 @@ public class FhirResourceDaoDstu3SearchFtTest extends BaseJpaDstu3Test {
|
|||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, param, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId4, devId1)));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, ptId2, obsId1, obsId4, devId1)));
|
||||
|
||||
/*
|
||||
* Make one previous match no longer match
|
||||
|
|
|
@ -92,7 +92,6 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -109,6 +108,7 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
|
@ -1424,9 +1424,11 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
|||
assertThat(patients, hasItems(id1a, id1b, id2));
|
||||
}
|
||||
{
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
SearchParameterMap params = SearchParameterMap.newSynchronous();
|
||||
params.setLastUpdated(new DateRangeParam(beforeR2, null));
|
||||
myCaptureQueriesListener.clear();
|
||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(patients, hasItems(id2));
|
||||
assertThat(patients, not(hasItems(id1a, id1b)));
|
||||
}
|
||||
|
@ -3287,7 +3289,9 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
|||
TokenParam param = new TokenParam();
|
||||
param.setMissing(false);
|
||||
params.add(Observation.SP_CODE, param);
|
||||
myCaptureQueriesListener.clear();
|
||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(patients, not(containsInRelativeOrder(missing)));
|
||||
assertThat(patients, containsInRelativeOrder(notMissing));
|
||||
}
|
||||
|
@ -3458,7 +3462,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
|||
map = new SearchParameterMap();
|
||||
map.setSort(new SortSpec("_id", SortOrderEnum.ASC));
|
||||
ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(ids, contains(id1, id2, "Patient/AA", "Patient/AB"));
|
||||
assertThat(ids, contains("Patient/AA", "Patient/AB", id1, id2));
|
||||
|
||||
}
|
||||
|
||||
|
@ -3614,7 +3618,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
|||
map = new SearchParameterMap();
|
||||
map.setSort(new SortSpec(Patient.SP_FAMILY, SortOrderEnum.ASC).setChain(new SortSpec(Patient.SP_GIVEN, SortOrderEnum.ASC)));
|
||||
ids = toUnqualifiedVersionlessIds(myPatientDao.search(map));
|
||||
assertThat(ids.toString(), ids, contains(pid1, pid2, pid3, pid4, pid5));
|
||||
assertThat(ids.toString(), ids, contains(pid2, pid4, pid5, pid3, pid1));
|
||||
assertEquals(5, ids.size());
|
||||
|
||||
}
|
||||
|
@ -3670,7 +3674,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
|||
map.setSort(new SortSpec("gender").setChain(new SortSpec("family", SortOrderEnum.ASC).setChain(new SortSpec("given", SortOrderEnum.ASC))));
|
||||
ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
ourLog.info("IDS: {}", ids);
|
||||
assertThat(ids.toString(), ids, contains("Patient/CA", "Patient/AA", "Patient/AB", "Patient/BA", "Patient/BB"));
|
||||
assertThat(ids.toString(), ids, contains("Patient/AA", "Patient/AB", "Patient/BA", "Patient/BB", "Patient/CA"));
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.add(Patient.SP_ACTIVE, new TokenParam(null, "true"));
|
||||
|
|
|
@ -30,7 +30,6 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
|
@ -81,7 +80,6 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -2025,7 +2023,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
found = toList(myPatientDao.search(new SearchParameterMap(Patient.SP_BIRTHDATE + "AAAA", new DateParam(ParamPrefixEnum.GREATERTHAN, "2000-01-01")).setLoadSynchronous(true)));
|
||||
assertEquals(0, found.size());
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unknown search parameter \"birthdateAAAA\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, active, address, address-city, address-country, address-postalcode, address-state, address-use, animal-breed, animal-species, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
assertEquals("Unknown search parameter \"birthdateAAAA\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, _lastUpdated, active, address, address-city, address-country, address-postalcode, address-state, address-use, animal-breed, animal-species, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2600,14 +2598,14 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
pm.setSort(new SortSpec(Patient.SP_BIRTHDATE));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id4, id1, id2, id3));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testtestSortByDate"));
|
||||
pm.setSort(new SortSpec(Patient.SP_BIRTHDATE).setOrder(SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id4, id1, id2, id3));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testtestSortByDate"));
|
||||
|
@ -2652,21 +2650,21 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
pm.setSort(new SortSpec(IAnyResource.SP_RES_ID));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(5, actual.size());
|
||||
assertThat(actual.toString(), actual, contains(id1, id2, id3, id4, idMethodName));
|
||||
assertThat(actual.toString(), actual, contains(idMethodName, id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
|
||||
pm.setSort(new SortSpec(IAnyResource.SP_RES_ID).setOrder(SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(5, actual.size());
|
||||
assertThat(actual.toString(), actual, contains(id1, id2, id3, id4, idMethodName));
|
||||
assertThat(actual.toString(), actual, contains(idMethodName, id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
|
||||
pm.setSort(new SortSpec(IAnyResource.SP_RES_ID).setOrder(SortOrderEnum.DESC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(5, actual.size());
|
||||
assertThat(actual.toString(), actual, contains(idMethodName, id4, id3, id2, id1));
|
||||
assertThat(actual.toString(), actual, contains(id4, id3, id2, id1, idMethodName));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -2891,14 +2889,14 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
pm.setSort(new SortSpec(Patient.SP_FAMILY));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id4, id1, id2, id3));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", string));
|
||||
pm.setSort(new SortSpec(Patient.SP_FAMILY).setOrder(SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id4, id1, id2, id3));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", string));
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.dao.predicate;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.util.CoordCalculator;
|
||||
import ca.uhn.fhir.jpa.util.CoordCalculatorTest;
|
||||
import ca.uhn.fhir.jpa.util.SearchBox;
|
||||
|
@ -27,13 +27,13 @@ import static org.mockito.Mockito.when;
|
|||
@ExtendWith(MockitoExtension.class)
|
||||
public class PredicateBuilderCoordsTest {
|
||||
PredicateBuilderCoords myPredicateBuilderCoords;
|
||||
private SearchBuilder mySearchBuilder;
|
||||
private LegacySearchBuilder mySearchBuilder;
|
||||
private CriteriaBuilder myBuilder;
|
||||
private From myFrom;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
mySearchBuilder = mock(SearchBuilder.class);
|
||||
mySearchBuilder = mock(LegacySearchBuilder.class);
|
||||
myBuilder = mock(CriteriaBuilder.class);
|
||||
myFrom = mock(From.class);
|
||||
myPredicateBuilderCoords = new PredicateBuilderCoords(mySearchBuilder);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.dao.predicate;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilderTest;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilderTest;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -14,7 +14,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
public class SearchFuzzUtilTest {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(SearchBuilderTest.class);
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(LegacySearchBuilderTest.class);
|
||||
|
||||
@Test
|
||||
public void testCalculateMultiplierEqualNoDecimal() {
|
||||
|
|
|
@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
|||
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.IResourceIndexedSearchParamCoordsDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamDateDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamQuantityDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamStringDao;
|
||||
|
@ -72,6 +73,7 @@ import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
|
|||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4;
|
||||
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
||||
import ca.uhn.fhir.jpa.validation.ValidationSettings;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
|
@ -204,6 +206,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
|
|||
@Autowired
|
||||
protected IResourceIndexedSearchParamTokenDao myResourceIndexedSearchParamTokenDao;
|
||||
@Autowired
|
||||
protected IResourceIndexedSearchParamCoordsDao myResourceIndexedSearchParamCoordsDao;
|
||||
@Autowired
|
||||
protected IResourceIndexedSearchParamQuantityDao myResourceIndexedSearchParamQuantityDao;
|
||||
@Autowired
|
||||
protected IResourceIndexedSearchParamDateDao myResourceIndexedSearchParamDateDao;
|
||||
|
@ -458,6 +462,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
|
|||
private IdHelperService myIdHelperService;
|
||||
@Autowired
|
||||
protected IBatchJobSubmitter myBatchJobSubmitter;
|
||||
@Autowired
|
||||
protected ValidationSettings myValidationSettings;
|
||||
|
||||
@AfterEach()
|
||||
public void afterCleanupDao() {
|
||||
|
@ -532,6 +538,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
|
|||
myDaoConfig.setHardTagListLimit(1000);
|
||||
myDaoConfig.setIncludeLimit(2000);
|
||||
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
|
||||
myValidationSettings.setLocalReferenceValidationDefaultPolicy(new ValidationSettings().getLocalReferenceValidationDefaultPolicy());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceProvenanceDao;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.util.TestUtil;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
|
@ -15,21 +15,29 @@ import org.hl7.fhir.r4.model.DecimalType;
|
|||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.RiskAssessment;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
@SuppressWarnings({"Duplicates"})
|
||||
public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4FilterTest.class);
|
||||
@Autowired
|
||||
private IResourceProvenanceDao myResourceProvenanceDao;
|
||||
|
||||
@AfterEach
|
||||
public void after() {
|
||||
myDaoConfig.setFilterParameterEnabled(new DaoConfig().isFilterParameterEnabled());
|
||||
|
@ -103,7 +111,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("name eq smi"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
|
@ -162,6 +170,38 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSourceComparatorEq() {
|
||||
myDaoConfig.setStoreMetaSourceInformation(DaoConfig.StoreMetaSourceInformationEnum.SOURCE_URI_AND_REQUEST_ID);
|
||||
|
||||
Patient p = new Patient();
|
||||
p.getMeta().setSource("http://source");
|
||||
p.addName().setFamily("Smith").addGiven("John");
|
||||
p.setActive(true);
|
||||
String ptId = myPatientDao.create(p).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
p = new Patient();
|
||||
p.addName().setFamily("Smith").addGiven("John2");
|
||||
p.setActive(true);
|
||||
myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameterMap map;
|
||||
List<String> found;
|
||||
|
||||
runInTransaction(() -> {
|
||||
ourLog.info("Provenance:\n * {}", myResourceProvenanceDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
|
||||
});
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("_source eq http://source"));
|
||||
myCaptureQueriesListener.clear();
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(found, containsInAnyOrder(ptId));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterDisabled() {
|
||||
myDaoConfig.setFilterParameterEnabled(false);
|
||||
|
@ -176,6 +216,9 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the Encounter DAO to find things that would match a Patient
|
||||
*/
|
||||
@Test
|
||||
public void testRetrieveDifferentTypeEq() {
|
||||
|
||||
|
@ -190,24 +233,29 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam(String.format("status eq active or _id eq %s",
|
||||
idVal)));
|
||||
map.add(Constants.PARAM_FILTER, new StringParam(String.format("status eq active or _id eq %s", idVal)));
|
||||
myCaptureQueriesListener.clear();
|
||||
found = toUnqualifiedVersionlessIdValues(myEncounterDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(found, empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam(String.format("_id eq %s",
|
||||
idVal)));
|
||||
map.add(Constants.PARAM_FILTER, new StringParam(String.format("status eq inactive or _id eq %s", idVal)));
|
||||
found = toUnqualifiedVersionlessIdValues(myEncounterDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam(String.format("_id eq %s",
|
||||
idVal)));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, containsInAnyOrder(id1));
|
||||
map.add(Constants.PARAM_FILTER, new StringParam(String.format("status eq inactive or _id eq Patient/FOO")));
|
||||
found = toUnqualifiedVersionlessIdValues(myEncounterDao.search(map));
|
||||
assertThat(found, empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam(String.format("_id eq %s", idVal)));
|
||||
found = toUnqualifiedVersionlessIdValues(myEncounterDao.search(map));
|
||||
assertThat(found, empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -272,9 +320,10 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
|
||||
CarePlan cp = new CarePlan();
|
||||
cp.getSubject().setReference(ptId.getValue());
|
||||
String cpId = myCarePlanDao.create(cp).getId().toUnqualifiedVersionless().getValue();
|
||||
myCarePlanDao.create(cp).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
cp = new CarePlan();
|
||||
cp.getSubject().setReference(ptId2.getValue());
|
||||
cp.addActivity().getDetail().addPerformer().setReference(ptId2.getValue());
|
||||
String cpId2 = myCarePlanDao.create(cp).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
|
@ -284,23 +333,43 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("subject ne " + ptId.getValue()));
|
||||
myCaptureQueriesListener.clear();
|
||||
found = toUnqualifiedVersionlessIdValues(myCarePlanDao.search(map));
|
||||
assertThat(found, containsInAnyOrder(cpId2));
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("subject ne " + ptId.getIdPart()));
|
||||
found = toUnqualifiedVersionlessIdValues(myCarePlanDao.search(map));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(found, containsInAnyOrder(cpId2));
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("(subject ne " + ptId.getIdPart() + ") and (performer ne " + ptId2.getValue() + ")"));
|
||||
found = toUnqualifiedVersionlessIdValues(myCarePlanDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testLanguageComparatorEq() {
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setLanguage("en");
|
||||
p.addName().setFamily("Smith").addGiven("John");
|
||||
p.setBirthDateElement(new DateType("1955-01-01"));
|
||||
p.setActive(true);
|
||||
String id1 = myPatientDao.create(p).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
SearchParameterMap map;
|
||||
List<String> found;
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("_language eq en"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, containsInAnyOrder(id1));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testStringComparatorCo() {
|
||||
|
||||
|
@ -369,7 +438,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("name sw mi"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
|
@ -405,7 +474,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("name ew it"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
|
@ -447,7 +516,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("given gt john"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -483,7 +552,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("given lt frank"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -506,7 +575,9 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("family ge jones"));
|
||||
myCaptureQueriesListener.clear();
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(found, containsInAnyOrder(id1, id2));
|
||||
|
||||
map = new SearchParameterMap();
|
||||
|
@ -525,7 +596,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("given ge jon"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -561,7 +632,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("family le jackson"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -601,7 +672,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("birthdate ne 1955-01-01"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
|
@ -633,7 +704,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("birthdate gt 1955-01-01"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -659,7 +730,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("birthdate lt 1955-01-01"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -691,7 +762,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("birthdate ge 1955-01-02"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -717,7 +788,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("birthdate le 1954-12-31"));
|
||||
found = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
|
@ -764,7 +835,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("probability eq 0.1"));
|
||||
found = toUnqualifiedVersionlessIdValues(myRiskAssessmentDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -840,7 +911,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("probability gt 0.3"));
|
||||
found = toUnqualifiedVersionlessIdValues(myRiskAssessmentDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -875,7 +946,7 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("probability lt 0.25"));
|
||||
found = toUnqualifiedVersionlessIdValues(myRiskAssessmentDao.search(map));
|
||||
assertThat(found, Matchers.empty());
|
||||
assertThat(found, empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -961,17 +1032,17 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
IIdType vsId2 = myValueSetDao.create(vs2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
IBundleProvider result;
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url eq http://hl7.org/foo/baz")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId1));
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url eq http://hl7.org/foo/bar")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId2));
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url eq http://hl7.org/foo/bar/bar")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), Matchers.empty());
|
||||
assertThat(toUnqualifiedVersionlessIds(result), empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -987,17 +1058,17 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
IIdType vsId2 = myValueSetDao.create(vs2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
IBundleProvider result;
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url ne http://hl7.org/foo/baz")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId2));
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url ne http://hl7.org/foo/bar")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId1));
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url ne http://hl7.org/foo/baz and url ne http://hl7.org/foo/bar")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), Matchers.empty());
|
||||
assertThat(toUnqualifiedVersionlessIds(result), empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -1013,17 +1084,17 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
IIdType vsId2 = myValueSetDao.create(vs2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
IBundleProvider result;
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url co http://hl7.org/foo")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId1, vsId2));
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url co baz")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId1));
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url co http://hl7.org/foo/bat")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), Matchers.empty());
|
||||
assertThat(toUnqualifiedVersionlessIds(result), empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -1039,17 +1110,17 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
IIdType vsId2 = myValueSetDao.create(vs2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
IBundleProvider result;
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url gt http://hl7.org/foo")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId1, vsId2));
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url gt http://hl7.org/foo/bar")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId1));
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url gt http://hl7.org/foo/baza")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), Matchers.empty());
|
||||
assertThat(toUnqualifiedVersionlessIds(result), empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -1065,15 +1136,15 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
IIdType vsId2 = myValueSetDao.create(vs2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
IBundleProvider result;
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url lt http://hl7.org/foo")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), Matchers.empty());
|
||||
assertThat(toUnqualifiedVersionlessIds(result), empty());
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url lt http://hl7.org/foo/baz")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId2));
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url lt http://hl7.org/foo/bara")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId2));
|
||||
|
||||
|
@ -1091,13 +1162,13 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
IIdType vsId2 = myValueSetDao.create(vs2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
IBundleProvider result;
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url ge http://hl7.org/foo/bar")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId1, vsId2));
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url ge http://hl7.org/foo/baza")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), Matchers.empty());
|
||||
assertThat(toUnqualifiedVersionlessIds(result), empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -1113,15 +1184,15 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
IIdType vsId2 = myValueSetDao.create(vs2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
IBundleProvider result;
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url le http://hl7.org/foo/baz")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId1, vsId2));
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url le http://hl7.org/foo/bar")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId2));
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url lt http://hl7.org/foo/baza")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId1, vsId2));
|
||||
|
||||
|
@ -1139,13 +1210,13 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
IIdType vsId2 = myValueSetDao.create(vs2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
IBundleProvider result;
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url sw http://hl7.org")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId1, vsId2));
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url sw hl7.org/foo/bar")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), Matchers.empty());
|
||||
assertThat(toUnqualifiedVersionlessIds(result), empty());
|
||||
|
||||
}
|
||||
|
||||
|
@ -1161,15 +1232,27 @@ public class FhirResourceDaoR4FilterTest extends BaseJpaR4Test {
|
|||
IIdType vsId2 = myValueSetDao.create(vs2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
IBundleProvider result;
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url ew baz")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), containsInAnyOrder(vsId1));
|
||||
|
||||
result = myValueSetDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Constants.PARAM_FILTER,
|
||||
result = myValueSetDao.search(SearchParameterMap.newSynchronous().add(Constants.PARAM_FILTER,
|
||||
new StringParam("url ew ba")));
|
||||
assertThat(toUnqualifiedVersionlessIds(result), Matchers.empty());
|
||||
assertThat(toUnqualifiedVersionlessIds(result), empty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnknownSearchParam() {
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Constants.PARAM_FILTER, new StringParam("foo eq smith"));
|
||||
try {
|
||||
myPatientDao.search(map);
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, _lastUpdated, active, address, address-city, address-country, address-postalcode, address-state, address-use, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -535,7 +535,8 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
|
|||
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
|
||||
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertEquals(1, StringUtils.countMatches(myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true).toLowerCase(), "join"));
|
||||
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true).toLowerCase();
|
||||
assertEquals(2, StringUtils.countMatches(sql, "join"), sql);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -70,11 +70,13 @@ import org.springframework.transaction.support.TransactionTemplate;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.countMatches;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
@ -807,27 +809,45 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
|
|||
SearchParameterMap map;
|
||||
IBundleProvider results;
|
||||
List<String> foundResources;
|
||||
String sql;
|
||||
|
||||
// Search by ref
|
||||
map = new SearchParameterMap();
|
||||
map = SearchParameterMap.newSynchronous();
|
||||
map.add("sibling", new ReferenceParam(p1id.getValue()));
|
||||
myCaptureQueriesListener.clear();
|
||||
results = myPatientDao.search(map);
|
||||
foundResources = toUnqualifiedVersionlessIdValues(results);
|
||||
assertThat(foundResources, contains(p2id.getValue()));
|
||||
sql = myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(sql, countMatches(sql, "JOIN"), equalTo(1));
|
||||
assertThat(sql, countMatches(sql, "t0.SRC_PATH = 'Patient.extension('http://acme.org/sibling')'"), equalTo(1));
|
||||
assertThat(sql, countMatches(sql, "t0.TARGET_RESOURCE_ID = '"), equalTo(1));
|
||||
|
||||
// Search by chain
|
||||
map = new SearchParameterMap();
|
||||
map = SearchParameterMap.newSynchronous();
|
||||
map.add("sibling", new ReferenceParam("name", "P1"));
|
||||
myCaptureQueriesListener.clear();
|
||||
results = myPatientDao.search(map);
|
||||
foundResources = toUnqualifiedVersionlessIdValues(results);
|
||||
assertThat(foundResources, contains(p2id.getValue()));
|
||||
sql = myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(sql, countMatches(sql, "JOIN"), equalTo(2));
|
||||
assertThat(sql, countMatches(sql, "SRC_PATH = 'Patient.extension('http://acme.org/sibling')'"), equalTo(1));
|
||||
assertThat(sql, countMatches(sql, "HASH_NORM_PREFIX = '"), equalTo(39));
|
||||
assertThat(sql, countMatches(sql, "SP_VALUE_NORMALIZED LIKE "), equalTo(39));
|
||||
|
||||
// Search by two level chain
|
||||
map = new SearchParameterMap();
|
||||
map = SearchParameterMap.newSynchronous();
|
||||
map.add("patient", new ReferenceParam("sibling.name", "P1"));
|
||||
myCaptureQueriesListener.clear();
|
||||
results = myAppointmentDao.search(map);
|
||||
foundResources = toUnqualifiedVersionlessIdValues(results);
|
||||
assertThat(foundResources, containsInAnyOrder(appid.getValue()));
|
||||
sql = myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(sql, countMatches(sql, "JOIN"), equalTo(3));
|
||||
assertThat(sql, countMatches(sql, "SRC_PATH = 'Appointment.participant.actor.where(resolve() is Patient)'"), equalTo(1));
|
||||
assertThat(sql, countMatches(sql, "SRC_PATH = 'Patient.extension('http://acme.org/sibling')'"), equalTo(1));
|
||||
assertThat(sql, countMatches(sql, "SP_VALUE_NORMALIZED LIKE 'P1%'"), equalTo(39));
|
||||
|
||||
}
|
||||
|
||||
|
@ -1426,7 +1446,7 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
|
|||
myPatientDao.search(map).size();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, active, address, address-city, address-country, address-postalcode, address-state, address-use, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
assertEquals("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, _lastUpdated, active, address, address-city, address-country, address-postalcode, address-state, address-use, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1464,7 +1484,7 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
|
|||
myPatientDao.search(map).size();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, active, address, address-city, address-country, address-postalcode, address-state, address-use, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
assertEquals("Unknown search parameter \"foo\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, _lastUpdated, active, address, address-city, address-country, address-postalcode, address-state, address-use, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
}
|
||||
|
||||
// Try with normal gender SP
|
||||
|
|
|
@ -451,7 +451,7 @@ public class FhirResourceDaoR4SearchFtTest extends BaseJpaR4Test {
|
|||
param = new StringAndListParam();
|
||||
param.addAnd(new StringOrListParam().addOr(new StringParam("obsvalue1")));
|
||||
actual = toUnqualifiedVersionlessIdValues(myPatientDao.patientTypeEverything(request, null, null, null, null, param, null, null, mockSrd()));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, obsId1, obsId4, devId1)));
|
||||
assertThat(actual, containsInAnyOrder(toValues(ptId1, ptId2, obsId1, obsId4, devId1)));
|
||||
|
||||
/*
|
||||
* Make one previous match no longer match
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
|
@ -66,7 +66,7 @@ public class FhirResourceDaoR4SearchLastNAsyncIT extends BaseR4SearchLastN {
|
|||
@Test
|
||||
public void testLastNChunking() {
|
||||
|
||||
runInTransaction(()->{
|
||||
runInTransaction(() -> {
|
||||
for (Search search : mySearchDao.findAll()) {
|
||||
mySearchDao.updateDeleted(search.getId(), true);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
|
@ -64,25 +65,25 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseR4SearchLastN {
|
|||
assertEquals(4, queries.size());
|
||||
|
||||
// The first and third chunked queries should have a full complement of PIDs
|
||||
StringBuilder firstQueryPattern = new StringBuilder(".*RES_ID in \\('[0-9]+'");
|
||||
StringBuilder firstQueryPattern = new StringBuilder(".*RES_ID IN \\('[0-9]+'");
|
||||
for (int pidIndex = 1; pidIndex < 50; pidIndex++) {
|
||||
firstQueryPattern.append(" , '[0-9]+'");
|
||||
firstQueryPattern.append(",'[0-9]+'");
|
||||
}
|
||||
firstQueryPattern.append("\\).*");
|
||||
assertThat(queries.get(0), matchesPattern(firstQueryPattern.toString()));
|
||||
assertThat(queries.get(2), matchesPattern(firstQueryPattern.toString()));
|
||||
assertThat(queries.get(0).toUpperCase().replaceAll(" , ", ","), matchesPattern(firstQueryPattern.toString()));
|
||||
assertThat(queries.get(2).toUpperCase().replaceAll(" , ", ","), matchesPattern(firstQueryPattern.toString()));
|
||||
|
||||
// the second and fourth chunked queries should be padded with "-1".
|
||||
StringBuilder secondQueryPattern = new StringBuilder(".*RES_ID in \\('[0-9]+'");
|
||||
StringBuilder secondQueryPattern = new StringBuilder(".*RES_ID IN \\('[0-9]+'");
|
||||
for (int pidIndex = 1; pidIndex < 25; pidIndex++) {
|
||||
secondQueryPattern.append(" , '[0-9]+'");
|
||||
secondQueryPattern.append(",'[0-9]+'");
|
||||
}
|
||||
for (int pidIndex = 0; pidIndex < 25; pidIndex++) {
|
||||
secondQueryPattern.append(" , '-1'");
|
||||
secondQueryPattern.append(",'-1'");
|
||||
}
|
||||
secondQueryPattern.append("\\).*");
|
||||
assertThat(queries.get(1), matchesPattern(secondQueryPattern.toString()));
|
||||
assertThat(queries.get(3), matchesPattern(secondQueryPattern.toString()));
|
||||
assertThat(queries.get(1).toUpperCase().replaceAll(" , ", ","), matchesPattern(secondQueryPattern.toString()));
|
||||
assertThat(queries.get(3).toUpperCase().replaceAll(" , ", ","), matchesPattern(secondQueryPattern.toString()));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
@ -31,11 +32,24 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testIndexMissingFieldsDisabledDontAllowInSearch() {
|
||||
public void testIndexMissingFieldsDisabledDontAllowInSearch_NonReference() {
|
||||
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED);
|
||||
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.add("foo", new StringParam().setMissing(true));
|
||||
params.add(Patient.SP_ACTIVE, new StringParam().setMissing(true));
|
||||
try {
|
||||
myPatientDao.search(params);
|
||||
} catch (MethodNotAllowedException e) {
|
||||
assertEquals(":missing modifier is disabled on this server", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexMissingFieldsDisabledDontAllowInSearch_Reference() {
|
||||
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED);
|
||||
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.add(Patient.SP_ORGANIZATION, new StringParam().setMissing(true));
|
||||
try {
|
||||
myPatientDao.search(params);
|
||||
} catch (MethodNotAllowedException e) {
|
||||
|
@ -162,6 +176,39 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithMissingCoords() {
|
||||
String locId = myLocationDao.create(new Location(), mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||
String locId2 = myLocationDao.create(new Location().setPosition(new Location.LocationPositionComponent(new DecimalType(10), new DecimalType(10))), mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
runInTransaction(()->{
|
||||
ourLog.info("Coords:\n * {}", myResourceIndexedSearchParamCoordsDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||
});
|
||||
|
||||
{
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLoadSynchronous(true);
|
||||
TokenParam param = new TokenParam();
|
||||
param.setMissing(true);
|
||||
params.add(Location.SP_NEAR, param);
|
||||
myCaptureQueriesListener.clear();
|
||||
List<String> patients = toUnqualifiedVersionlessIdValues(myLocationDao.search(params));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(patients, containsInRelativeOrder(locId));
|
||||
assertThat(patients, not(containsInRelativeOrder(locId2)));
|
||||
}
|
||||
{
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLoadSynchronous(true);
|
||||
TokenParam param = new TokenParam();
|
||||
param.setMissing(false);
|
||||
params.add(Location.SP_NEAR, param);
|
||||
List<String> patients = toUnqualifiedVersionlessIdValues(myLocationDao.search(params));
|
||||
assertThat(patients, containsInRelativeOrder(locId2));
|
||||
assertThat(patients, not(containsInRelativeOrder(locId)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithMissingDate2() {
|
||||
MedicationRequest mr1 = new MedicationRequest();
|
||||
|
|
|
@ -121,7 +121,6 @@ import org.hl7.fhir.r4.model.Substance;
|
|||
import org.hl7.fhir.r4.model.Task;
|
||||
import org.hl7.fhir.r4.model.Timing;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
@ -148,6 +147,7 @@ import java.util.TreeSet;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.rest.api.Constants.PARAM_TYPE;
|
||||
import static org.apache.commons.lang3.StringUtils.countMatches;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
|
@ -206,7 +206,9 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(StructureDefinition.SP_VALUESET, new ReferenceParam("http://foo2"));
|
||||
myCaptureQueriesListener.clear();
|
||||
List<String> ids = toUnqualifiedVersionlessIdValues(myStructureDefinitionDao.search(map));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(ids, empty());
|
||||
}
|
||||
}
|
||||
|
@ -415,8 +417,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
myCaptureQueriesListener.clear();
|
||||
results = myEncounterDao.search(map);
|
||||
searchSql = myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertEquals(0, StringUtils.countMatches(searchSql, "RES_DELETED_AT"));
|
||||
assertEquals(0, StringUtils.countMatches(searchSql, "RES_TYPE"));
|
||||
assertEquals(0, countMatches(searchSql, "RES_DELETED_AT"));
|
||||
assertEquals(0, countMatches(searchSql, "RES_TYPE"));
|
||||
ids = toUnqualifiedVersionlessIdValues(results);
|
||||
assertThat(ids, hasItems(enc1Id, enc2Id));
|
||||
|
||||
|
@ -478,9 +480,11 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add(Encounter.SP_SUBJECT, new ReferenceParam("subject", "Patient").setChain(PARAM_TYPE));
|
||||
myCaptureQueriesListener.clear();
|
||||
results = myEncounterDao.search(map);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
ids = toUnqualifiedVersionlessIdValues(results);
|
||||
assertThat(ids, contains(enc1Id));
|
||||
assertThat(ids.toString(), ids, contains(enc1Id));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
|
||||
map = new SearchParameterMap();
|
||||
|
@ -540,8 +544,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
map.add(DiagnosticReport.SP_PERFORMER, new ReferenceParam("CareTeam").setChain(PARAM_TYPE));
|
||||
results = myDiagnosticReportDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIdValues(results);
|
||||
assertThat(ids.toString(), ids, contains(drId1.getValue()));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids.toString(), ids, contains(drId1.getValue()));
|
||||
|
||||
}
|
||||
|
||||
|
@ -558,10 +562,18 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
ma.setMedication(new Reference(medId));
|
||||
IIdType moId = myMedicationAdministrationDao.create(ma).getId().toUnqualified();
|
||||
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
runInTransaction(() -> {
|
||||
ourLog.info("Resource Links:\n * {}", myResourceLinkDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
|
||||
ourLog.info("Token indexes:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
|
||||
});
|
||||
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||
map.add(MedicationAdministration.SP_MEDICATION, new ReferenceAndListParam().addAnd(new ReferenceOrListParam().add(new ReferenceParam("code", "04823543"))));
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider results = myMedicationAdministrationDao.search(map);
|
||||
List<String> ids = toUnqualifiedIdValues(results);
|
||||
myCaptureQueriesListener.logSelectQueries();
|
||||
|
||||
assertThat(ids, contains(moId.getValue()));
|
||||
}
|
||||
|
@ -739,23 +751,41 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
pat.getManagingOrganization().setReferenceElement(orgId);
|
||||
IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient pat2 = new Patient();
|
||||
pat2.addAddress().addLine(methodName + "2");
|
||||
pat2.getManagingOrganization().setReferenceElement(orgId);
|
||||
IIdType patId2 = myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
MedicationRequest mo = new MedicationRequest();
|
||||
mo.getSubject().setReferenceElement(patId);
|
||||
mo.setMedication(new Reference(medId));
|
||||
IIdType moId = myMedicationRequestDao.create(mo, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, null, mySrd);
|
||||
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId, patId2));
|
||||
// Nothing links to this one
|
||||
Patient pat2 = new Patient();
|
||||
pat2.addAddress().addLine(methodName + "2");
|
||||
pat2.getManagingOrganization().setReferenceElement(orgId);
|
||||
IIdType patId2 = myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
runInTransaction(()->{
|
||||
ourLog.info("Links:\n * {}", myResourceLinkDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||
});
|
||||
|
||||
// All patient IDs
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
myCaptureQueriesListener.clear();
|
||||
myCaptureQueriesListener.setCaptureQueryStackTrace(true);
|
||||
IBundleProvider resp = myPatientDao.patientTypeEverything(request, null, null, null, null, null, null, null, mySrd);
|
||||
List<IIdType> actual = toUnqualifiedVersionlessIds(resp);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(actual, containsInAnyOrder(orgId, medId, patId, moId, patId2));
|
||||
assertEquals(5, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
|
||||
// Specific patient ID with linked stuff
|
||||
request = mock(HttpServletRequest.class);
|
||||
resp = myPatientDao.patientInstanceEverything(request, patId, null, null, null, null, null, null, null, mySrd);
|
||||
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(orgId, medId, patId, moId));
|
||||
|
||||
// Specific patient ID with no linked stuff
|
||||
request = mock(HttpServletRequest.class);
|
||||
resp = myPatientDao.patientInstanceEverything(request, patId2, null, null, null, null, null, null, null, mySrd);
|
||||
assertThat(toUnqualifiedVersionlessIds(resp), containsInAnyOrder(patId2, orgId));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -921,7 +951,9 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLoadSynchronous(true);
|
||||
params.add("_has", new HasParam("Observation", "subject", "identifier", "urn:system|FOO"));
|
||||
myCaptureQueriesListener.clear();
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(pid0.getValue()));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
|
||||
// No targets exist
|
||||
params = new SearchParameterMap();
|
||||
|
@ -1385,6 +1417,37 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchByIdParamInverse() {
|
||||
String id1;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("001");
|
||||
id1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||
}
|
||||
String id2;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("002");
|
||||
id2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||
}
|
||||
|
||||
SearchParameterMap params;
|
||||
|
||||
// inverse
|
||||
params = SearchParameterMap.newSynchronous();
|
||||
params.add("_id", new TokenParam(id1).setModifier(TokenParamModifier.NOT));
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(id2));
|
||||
|
||||
// Non-inverse
|
||||
params = SearchParameterMap.newSynchronous();
|
||||
params.add("_id", new TokenParam(id1));
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(id1));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchByIdParam_QueryIsMinimal() {
|
||||
// With only an _id parameter
|
||||
|
@ -1398,11 +1461,11 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
assertEquals(1, selectQueries.size());
|
||||
|
||||
String sqlQuery = selectQueries.get(0).getSql(true, true).toLowerCase();
|
||||
ourLog.debug("SQL Query:\n{}", sqlQuery);
|
||||
assertEquals(1, StringUtils.countMatches(sqlQuery, "resourceta0_.res_id in"));
|
||||
assertEquals(0, StringUtils.countMatches(sqlQuery, "join"));
|
||||
assertEquals(1, StringUtils.countMatches(sqlQuery, "resourceta0_.res_type='diagnosticreport'"));
|
||||
assertEquals(1, StringUtils.countMatches(sqlQuery, "resourceta0_.res_deleted_at is null"));
|
||||
ourLog.info("SQL Query:\n{}", sqlQuery);
|
||||
assertEquals(1, countMatches(sqlQuery, "res_id = '123'"), sqlQuery);
|
||||
assertEquals(0, countMatches(sqlQuery, "join"), sqlQuery);
|
||||
assertEquals(1, countMatches(sqlQuery, "res_type = 'diagnosticreport'"), sqlQuery);
|
||||
assertEquals(1, countMatches(sqlQuery, "res_deleted_at is null"), sqlQuery);
|
||||
}
|
||||
// With an _id parameter and a standard search param
|
||||
{
|
||||
|
@ -1417,11 +1480,11 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
|
||||
String sqlQuery = selectQueries.get(0).getSql(true, true).toLowerCase();
|
||||
ourLog.info("SQL Query:\n{}", sqlQuery);
|
||||
assertEquals(1, StringUtils.countMatches(sqlQuery, "resourceta0_.res_id in"));
|
||||
assertEquals(1, StringUtils.countMatches(sqlQuery, "join"));
|
||||
assertEquals(1, StringUtils.countMatches(sqlQuery, "hash_sys_and_value"));
|
||||
assertEquals(0, StringUtils.countMatches(sqlQuery, "diagnosticreport"));
|
||||
assertEquals(0, StringUtils.countMatches(sqlQuery, "res_deleted_at"));
|
||||
assertEquals(1, countMatches(sqlQuery, "res_id = '123'"), sqlQuery);
|
||||
assertEquals(1, countMatches(sqlQuery, "join"), sqlQuery);
|
||||
assertEquals(1, countMatches(sqlQuery, "hash_sys_and_value"), sqlQuery);
|
||||
assertEquals(1, countMatches(sqlQuery, "res_type = 'diagnosticreport"), sqlQuery); // could be 0
|
||||
assertEquals(1, countMatches(sqlQuery, "res_deleted_at"), sqlQuery); // could be 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1438,10 +1501,10 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
|
||||
String sqlQuery = selectQueries.get(0).getSql(true, true).toLowerCase();
|
||||
ourLog.info("SQL Query:\n{}", sqlQuery);
|
||||
assertEquals(1, StringUtils.countMatches(sqlQuery, "resourceta0_.res_id in"));
|
||||
assertEquals(0, StringUtils.countMatches(sqlQuery, "join"));
|
||||
assertEquals(1, StringUtils.countMatches(sqlQuery, "resourceta0_.res_type='diagnosticreport'"));
|
||||
assertEquals(1, StringUtils.countMatches(sqlQuery, "resourceta0_.res_deleted_at is null"));
|
||||
assertEquals(1, countMatches(sqlQuery, "res_id = '123'"), sqlQuery);
|
||||
assertEquals(0, countMatches(sqlQuery, "join"), sqlQuery);
|
||||
assertEquals(1, countMatches(sqlQuery, "res_type = 'diagnosticreport'"), sqlQuery);
|
||||
assertEquals(1, countMatches(sqlQuery, "res_deleted_at is null"), sqlQuery);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1526,10 +1589,14 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
|
||||
// With lastupdated
|
||||
|
||||
params = new SearchParameterMap();
|
||||
params = SearchParameterMap.newSynchronous();
|
||||
params.add("_id", new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam(id2.getIdPart())));
|
||||
params.setLastUpdated(new DateRangeParam(new Date(betweenTime), null));
|
||||
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id2));
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider search = myPatientDao.search(params);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(toUnqualifiedVersionlessIds(search).toString(), toUnqualifiedVersionlessIds(search), containsInAnyOrder(id2));
|
||||
|
||||
}
|
||||
|
||||
|
@ -1706,7 +1773,9 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 150, "http://bar", "code1");
|
||||
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<>(v0, v1);
|
||||
SearchParameterMap map = new SearchParameterMap().setLoadSynchronous(true).add(param, val);
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider result = myObservationDao.search(map);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat("Got: " + toUnqualifiedVersionlessIdValues(result), toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(id2.getValue()));
|
||||
}
|
||||
{
|
||||
|
@ -2171,6 +2240,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
}
|
||||
|
||||
SearchParameterMap params;
|
||||
List result;
|
||||
|
||||
params = new SearchParameterMap();
|
||||
params.setLoadSynchronous(true);
|
||||
params.add("_id", new StringParam("TEST"));
|
||||
|
@ -2179,7 +2250,10 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
params = new SearchParameterMap();
|
||||
params.setLoadSynchronous(true);
|
||||
params.add("_language", new StringParam("TEST"));
|
||||
assertEquals(1, toList(myPatientDao.search(params)).size());
|
||||
myCaptureQueriesListener.clear();
|
||||
result = toList(myPatientDao.search(params));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertEquals(1, result.size());
|
||||
|
||||
params = new SearchParameterMap();
|
||||
params.setLoadSynchronous(true);
|
||||
|
@ -2249,7 +2323,9 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
params.setLoadSynchronous(true);
|
||||
|
||||
params.add(IAnyResource.SP_RES_LANGUAGE, new StringParam("en_CA"));
|
||||
myCaptureQueriesListener.clear();
|
||||
List<IBaseResource> patients = toList(myPatientDao.search(params));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertEquals(1, patients.size());
|
||||
assertEquals(id1.toUnqualifiedVersionless(), patients.get(0).getIdElement().toUnqualifiedVersionless());
|
||||
}
|
||||
|
@ -2668,7 +2744,9 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
e2.addPrediction().setProbability(new DecimalType(4));
|
||||
IIdType id2 = myRiskAssessmentDao.create(e2, mySrd).getId();
|
||||
{
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider found = myRiskAssessmentDao.search(new SearchParameterMap().setLoadSynchronous(true).add(RiskAssessment.SP_PROBABILITY, new NumberParam(">2")));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertEquals(2, found.size().intValue());
|
||||
assertThat(toUnqualifiedVersionlessIds(found), containsInAnyOrder(id1.toUnqualifiedVersionless(), id2.toUnqualifiedVersionless()));
|
||||
}
|
||||
|
@ -3037,8 +3115,10 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
List<IIdType> result;
|
||||
SearchParameterMap params;
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
result = toUnqualifiedVersionlessIds(myObservationDao
|
||||
.search(new SearchParameterMap().setLoadSynchronous(true).add(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_NAME, "testSearchResourceLinkWithChainWithMultipleTypesXX"))));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(result, containsInAnyOrder(obsId01));
|
||||
assertEquals(1, result.size());
|
||||
|
||||
|
@ -3148,24 +3228,6 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchStringParamWithLike() {
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Patient.SP_FAMILY, new StringOrListParam().addOr(new StringParam("AAA")).addOr(new StringParam("BBB")));
|
||||
map.setLoadSynchronous(true);
|
||||
myPatientDao.search(map);
|
||||
|
||||
List<String> queries = myCaptureQueriesListener
|
||||
.getCapturedQueries()
|
||||
.stream()
|
||||
.map(t -> t.getSql(true, true))
|
||||
.filter(t -> t.contains("select"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
ourLog.info("Queries:\n " + queries.stream().collect(Collectors.joining("\n ")));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchTokenListLike() {
|
||||
|
||||
|
@ -3193,8 +3255,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
.collect(Collectors.toList());
|
||||
String resultingQueryNotFormatted = queries.get(0);
|
||||
|
||||
assertEquals(1, StringUtils.countMatches(resultingQueryNotFormatted, "HASH_VALUE"), resultingQueryNotFormatted);
|
||||
assertThat(resultingQueryNotFormatted, containsString("HASH_VALUE in ('3140583648400062149' , '4929264259256651518')"));
|
||||
assertEquals(1, countMatches(resultingQueryNotFormatted, "HASH_VALUE"), resultingQueryNotFormatted);
|
||||
assertThat(resultingQueryNotFormatted, containsString("HASH_VALUE IN ('3140583648400062149','4929264259256651518')"));
|
||||
|
||||
// Ensure that the search actually worked
|
||||
assertEquals(2, search.size().intValue());
|
||||
|
@ -3229,8 +3291,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
.collect(Collectors.toList());
|
||||
String resultingQueryNotFormatted = queries.get(0);
|
||||
|
||||
assertEquals(1, StringUtils.countMatches(resultingQueryNotFormatted, "HASH_VALUE"), resultingQueryNotFormatted);
|
||||
assertThat(resultingQueryNotFormatted, containsString("HASH_VALUE in ('3140583648400062149' , '4929264259256651518')"));
|
||||
assertEquals(2, countMatches(resultingQueryNotFormatted, "HASH_VALUE"), resultingQueryNotFormatted);
|
||||
assertEquals(1, countMatches(resultingQueryNotFormatted, "HASH_SYS"), resultingQueryNotFormatted);
|
||||
|
||||
// Ensure that the search actually worked
|
||||
assertEquals(3, search.size().intValue());
|
||||
|
@ -3394,8 +3456,10 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
String id2 = myPatientDao.create(p2).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
{
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider found = myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Patient.SP_FAMILY, new StringParam("AAA")));
|
||||
assertThat(toUnqualifiedVersionlessIdValues(found), containsInAnyOrder(id1));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(toUnqualifiedVersionlessIdValues(found).toString(), toUnqualifiedVersionlessIdValues(found), containsInAnyOrder(id1));
|
||||
assertEquals(1, found.size().intValue());
|
||||
}
|
||||
{
|
||||
|
@ -3443,8 +3507,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
.collect(Collectors.toList());
|
||||
|
||||
String searchQuery = queries.get(0);
|
||||
assertEquals(3, StringUtils.countMatches(searchQuery.toUpperCase(), "HFJ_SPIDX_TOKEN"), searchQuery);
|
||||
assertEquals(4, StringUtils.countMatches(searchQuery.toUpperCase(), "LEFT OUTER JOIN"), searchQuery);
|
||||
assertEquals(3, countMatches(searchQuery.toUpperCase(), "HFJ_SPIDX_TOKEN"), searchQuery);
|
||||
assertEquals(5, countMatches(searchQuery.toUpperCase(), "LEFT OUTER JOIN"), searchQuery);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3468,9 +3532,9 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
.collect(Collectors.toList());
|
||||
|
||||
String searchQuery = queries.get(0);
|
||||
assertEquals(1, StringUtils.countMatches(searchQuery.toUpperCase(), "HFJ_SPIDX_TOKEN"), searchQuery);
|
||||
assertEquals(1, StringUtils.countMatches(searchQuery.toUpperCase(), "LEFT OUTER JOIN"), searchQuery);
|
||||
assertEquals(2, StringUtils.countMatches(searchQuery.toUpperCase(), "AND RESOURCETA0_.RES_UPDATED"), searchQuery);
|
||||
assertEquals(1, countMatches(searchQuery.toUpperCase(), "HFJ_SPIDX_TOKEN"), searchQuery);
|
||||
assertEquals(2, countMatches(searchQuery.toUpperCase(), "LEFT OUTER JOIN"), searchQuery);
|
||||
assertEquals(2, countMatches(searchQuery.toUpperCase(), "RES_UPDATED"), searchQuery);
|
||||
}
|
||||
|
||||
@Disabled
|
||||
|
@ -3665,20 +3729,33 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
myPatientDao.create(patient, mySrd);
|
||||
|
||||
patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system2").setValue("testSearchTokenParam002");
|
||||
patient.addIdentifier().setSystem("urn:system2").setValue("testSearchTokenParam003");
|
||||
patient.addName().setFamily("Tester").addGiven("testSearchTokenParam2");
|
||||
myPatientDao.create(patient, mySrd);
|
||||
|
||||
patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system2").setValue("testSearchTokenParam004");
|
||||
patient.addName().setFamily("Tester").addGiven("testSearchTokenParam2");
|
||||
myPatientDao.create(patient, mySrd);
|
||||
|
||||
runInTransaction(()->{
|
||||
ourLog.info("Token indexes:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().filter(t->t.getParamName().equals("identifier")).map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||
});
|
||||
|
||||
{
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||
map.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", null));
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider retrieved = myPatientDao.search(map);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertEquals(2, retrieved.size().intValue());
|
||||
}
|
||||
{
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||
map.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", ""));
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider retrieved = myPatientDao.search(map);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertEquals(2, retrieved.size().intValue());
|
||||
}
|
||||
}
|
||||
|
@ -3704,20 +3781,69 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
female = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||
}
|
||||
|
||||
runInTransaction(()->{
|
||||
ourLog.info("Tokens:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||
});
|
||||
|
||||
List<String> patients;
|
||||
SearchParameterMap params;
|
||||
|
||||
// Yes match - one value
|
||||
params = new SearchParameterMap();
|
||||
params.add(Patient.SP_GENDER, new TokenParam(null, "male"));
|
||||
params.setLoadSynchronous(true);
|
||||
patients = toUnqualifiedVersionlessIdValues(myPatientDao.search(params));
|
||||
assertThat(patients, contains(male));
|
||||
|
||||
// Yes match - two values
|
||||
params = new SearchParameterMap();
|
||||
params.add(Patient.SP_GENDER, new TokenOrListParam()
|
||||
.addOr(new TokenParam(null, "male"))
|
||||
.addOr(new TokenParam(null, "blah"))
|
||||
);
|
||||
params.setLoadSynchronous(true);
|
||||
patients = toUnqualifiedVersionlessIdValues(myPatientDao.search(params));
|
||||
assertThat(patients, contains(male));
|
||||
|
||||
// Yes match - two values with different specificities
|
||||
params = new SearchParameterMap();
|
||||
params.add(Patient.SP_GENDER, new TokenOrListParam()
|
||||
.addOr(new TokenParam(null, "male"))
|
||||
.addOr(new TokenParam("http://help-im-a-bug", "blah"))
|
||||
);
|
||||
params.setLoadSynchronous(true);
|
||||
patients = toUnqualifiedVersionlessIdValues(myPatientDao.search(params));
|
||||
assertThat(patients, contains(male));
|
||||
|
||||
// No match - one value
|
||||
params = new SearchParameterMap();
|
||||
params.add(Patient.SP_GENDER, new TokenParam(null, "male").setModifier(TokenParamModifier.NOT));
|
||||
params.setLoadSynchronous(true);
|
||||
myCaptureQueriesListener.clear();
|
||||
patients = toUnqualifiedVersionlessIdValues(myPatientDao.search(params));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(patients, contains(female));
|
||||
|
||||
// No match - two values
|
||||
params = new SearchParameterMap();
|
||||
params.add(Patient.SP_GENDER, new TokenOrListParam()
|
||||
.addOr(new TokenParam(null, "male").setModifier(TokenParamModifier.NOT))
|
||||
.addOr(new TokenParam(null, "blah").setModifier(TokenParamModifier.NOT))
|
||||
);
|
||||
params.setLoadSynchronous(true);
|
||||
patients = toUnqualifiedVersionlessIdValues(myPatientDao.search(params));
|
||||
assertThat(patients, contains(female));
|
||||
|
||||
// No match - two values with different specificities
|
||||
params = new SearchParameterMap();
|
||||
params.add(Patient.SP_GENDER, new TokenOrListParam()
|
||||
.addOr(new TokenParam(null, "male").setModifier(TokenParamModifier.NOT))
|
||||
.addOr(new TokenParam("http://help-im-a-bug", "blah").setModifier(TokenParamModifier.NOT))
|
||||
);
|
||||
params.setLoadSynchronous(true);
|
||||
patients = toUnqualifiedVersionlessIdValues(myPatientDao.search(params));
|
||||
assertThat(patients, contains(female));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -3823,8 +3949,16 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
map.setLoadSynchronous(true);
|
||||
param = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, new BigDecimal("10"), null, null);
|
||||
map.add(Observation.SP_VALUE_QUANTITY, param);
|
||||
myCaptureQueriesListener.clear();
|
||||
found = myObservationDao.search(map);
|
||||
assertThat(toUnqualifiedVersionlessIdValues(found), contains(id1));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(toUnqualifiedVersionlessIdValues(found).toString(), toUnqualifiedVersionlessIdValues(found), contains(id1));
|
||||
|
||||
String searchQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
assertEquals(1, countMatches(searchQuery.toLowerCase(), "join"), searchQuery);
|
||||
assertEquals(0, countMatches(searchQuery.toLowerCase(), "partition"), searchQuery);
|
||||
assertEquals(1, countMatches(searchQuery.toLowerCase(), "hash_identity"), searchQuery);
|
||||
assertEquals(1, countMatches(searchQuery.toLowerCase(), "sp_value"), searchQuery);
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
|
@ -4107,9 +4241,9 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
assertThat(patients.toString(), patients, contains(obsId1));
|
||||
String searchQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search query:\n{}", searchQuery);
|
||||
assertEquals(1, StringUtils.countMatches(searchQuery.toLowerCase(), "join"), searchQuery);
|
||||
assertEquals(1, StringUtils.countMatches(searchQuery.toLowerCase(), "hash_identity"), searchQuery);
|
||||
assertEquals(2, StringUtils.countMatches(searchQuery.toLowerCase(), "sp_value_low"), searchQuery);
|
||||
assertEquals(1, countMatches(searchQuery.toLowerCase(), "join"), searchQuery);
|
||||
assertEquals(1, countMatches(searchQuery.toLowerCase(), "t0.sp_value_low_date_ordinal >= '20200605'"), searchQuery);
|
||||
assertEquals(1, countMatches(searchQuery.toLowerCase(), "t0.sp_value_low_date_ordinal <= '20200606'"), searchQuery);
|
||||
}
|
||||
|
||||
// Two AND instances of 1 SP and 1 instance of another
|
||||
|
@ -4125,9 +4259,10 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
assertThat(patients.toString(), patients, contains(obsId1));
|
||||
String searchQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search query:\n{}", searchQuery);
|
||||
assertEquals(2, StringUtils.countMatches(searchQuery.toLowerCase(), "join"), searchQuery);
|
||||
assertEquals(2, StringUtils.countMatches(searchQuery.toLowerCase(), "hash_identity"), searchQuery);
|
||||
assertEquals(4, StringUtils.countMatches(searchQuery.toLowerCase(), "sp_value_low"), searchQuery);
|
||||
assertEquals(0, countMatches(searchQuery.toLowerCase(), "partition"), searchQuery);
|
||||
assertEquals(2, countMatches(searchQuery.toLowerCase(), "join"), searchQuery);
|
||||
assertEquals(2, countMatches(searchQuery.toLowerCase(), "hash_identity"), searchQuery);
|
||||
assertEquals(4, countMatches(searchQuery.toLowerCase(), "sp_value_low"), searchQuery);
|
||||
}
|
||||
|
||||
// Period search
|
||||
|
@ -4140,9 +4275,9 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
assertThat(patients.toString(), patients, containsInAnyOrder(obsId3, obsId4));
|
||||
String searchQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search query:\n{}", searchQuery);
|
||||
assertEquals(1, StringUtils.countMatches(searchQuery.toLowerCase(), "join"), searchQuery);
|
||||
assertEquals(1, StringUtils.countMatches(searchQuery.toLowerCase(), "hash_identity"), searchQuery);
|
||||
assertEquals(1, StringUtils.countMatches(searchQuery.toLowerCase(), "sp_value_low"), searchQuery);
|
||||
assertEquals(1, countMatches(searchQuery.toLowerCase(), "join"), searchQuery);
|
||||
assertEquals(1, countMatches(searchQuery.toLowerCase(), "hash_identity"), searchQuery);
|
||||
assertEquals(1, countMatches(searchQuery.toLowerCase(), "sp_value_low"), searchQuery);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4160,9 +4295,13 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider values = myPatientDao.search(map);
|
||||
assertEquals(null, values.size());
|
||||
assertEquals(5, values.getResources(0, 1000).size());
|
||||
|
||||
String sql = myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertEquals(1, countMatches(sql, "limit '5'"), sql);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -4627,6 +4766,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
List<IIdType> patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
|
||||
assertThat(patients, containsInAnyOrder(tag2id));
|
||||
}
|
||||
|
||||
// TODO: get multiple/AND working
|
||||
{
|
||||
// And tags
|
||||
|
@ -4675,9 +4815,11 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
|
||||
{
|
||||
// One tag
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
SearchParameterMap params = SearchParameterMap.newSynchronous();
|
||||
params.add("_tag", new TokenParam("urn:taglist", methodName + "1a").setModifier(TokenParamModifier.NOT));
|
||||
myCaptureQueriesListener.clear();
|
||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(patients, containsInAnyOrder(tag2id));
|
||||
assertThat(patients, not(containsInAnyOrder(tag1id)));
|
||||
}
|
||||
|
@ -5116,12 +5258,14 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
// Matches
|
||||
Encounter e1 = new Encounter();
|
||||
e1.setPeriod(new Period().setStartElement(new DateTimeType("2020-09-14T12:00:00Z")).setEndElement(new DateTimeType("2020-09-14T12:00:00Z")));
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e1));
|
||||
String e1Id = myEncounterDao.create(e1).getId().toUnqualifiedVersionless().getValue();
|
||||
Communication c1 = new Communication();
|
||||
c1.getEncounter().setReference(e1Id);
|
||||
myCommunicationDao.create(c1);
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(c1));
|
||||
String c1Id = myCommunicationDao.create(c1).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
// Doesn't match
|
||||
// Doesn't match (wrong date)
|
||||
Encounter e2 = new Encounter();
|
||||
e2.setPeriod(new Period().setStartElement(new DateTimeType("2020-02-14T12:00:00Z")).setEndElement(new DateTimeType("2020-02-14T12:00:00Z")));
|
||||
String e2Id = myEncounterDao.create(e2).getId().toUnqualifiedVersionless().getValue();
|
||||
|
@ -5129,12 +5273,36 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
|||
c2.getEncounter().setReference(e2Id);
|
||||
myCommunicationDao.create(c2);
|
||||
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||
// Doesn't match (wrong field - Encounter.location.period is also indexed in the "location-period" SP)
|
||||
Encounter e3 = new Encounter();
|
||||
e3.addLocation().setPeriod(new Period().setStartElement(new DateTimeType("2020-09-14T12:00:00Z")).setEndElement(new DateTimeType("2020-09-14T12:00:00Z")));
|
||||
String e3Id = myEncounterDao.create(e3).getId().toUnqualifiedVersionless().getValue();
|
||||
Communication c3 = new Communication();
|
||||
c3.getEncounter().setReference(e3Id);
|
||||
myCommunicationDao.create(c3);
|
||||
|
||||
runInTransaction(()->{
|
||||
ourLog.info("Links:\n * {}", myResourceLinkDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||
ourLog.info("Dates:\n * {}", myResourceIndexedSearchParamDateDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||
});
|
||||
|
||||
SearchParameterMap map;
|
||||
|
||||
map = SearchParameterMap.newSynchronous();
|
||||
map.add(Communication.SP_ENCOUNTER, new ReferenceParam("ge2020-09-14").setChain("date"));
|
||||
map.add(Communication.SP_ENCOUNTER, new ReferenceParam("le2020-09-15").setChain("date"));
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider outcome = myCommunicationDao.search(map);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
|
||||
assertThat(toUnqualifiedVersionlessIdValues(outcome).toString(), toUnqualifiedVersionlessIdValues(outcome), containsInAnyOrder(c1Id));
|
||||
assertEquals(1, outcome.sizeOrThrowNpe());
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
assertEquals(4, countMatches(searchSql, "JOIN"));
|
||||
assertEquals(1, countMatches(searchSql, "SELECT"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -93,7 +93,6 @@ import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus;
|
|||
import org.hl7.fhir.r4.model.Substance;
|
||||
import org.hl7.fhir.r4.model.Task;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
@ -501,8 +500,9 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
|
|||
.collect(Collectors.toList());
|
||||
String resultingQueryNotFormatted = queries.get(0);
|
||||
|
||||
assertEquals(1, StringUtils.countMatches(resultingQueryNotFormatted, "SP_VALUE"), resultingQueryNotFormatted);
|
||||
assertThat(resultingQueryNotFormatted, containsString("SP_VALUE in ('BAR' , 'FOO')"));
|
||||
assertEquals(0, StringUtils.countMatches(resultingQueryNotFormatted, "SP_VALUE"), resultingQueryNotFormatted);
|
||||
assertEquals(1, StringUtils.countMatches(resultingQueryNotFormatted, "HASH_VALUE"), resultingQueryNotFormatted);
|
||||
assertThat(resultingQueryNotFormatted, containsString("HASH_VALUE IN ('3140583648400062149','4929264259256651518')"));
|
||||
|
||||
// Ensure that the search actually worked
|
||||
assertEquals(2, search.size().intValue());
|
||||
|
@ -2362,36 +2362,38 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
|
|||
patient.addName().setFamily("Tester").addGiven("testSearchTokenParam1");
|
||||
patient.addCommunication().getLanguage().setText("testSearchTokenParamComText").addCoding().setCode("testSearchTokenParamCode").setSystem("testSearchTokenParamSystem")
|
||||
.setDisplay("testSearchTokenParamDisplay");
|
||||
myPatientDao.create(patient, mySrd);
|
||||
String id1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("testSearchTokenParam002");
|
||||
patient.addName().setFamily("Tester").addGiven("testSearchTokenParam2");
|
||||
myPatientDao.create(patient, mySrd);
|
||||
String id2 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system").setValue(null);
|
||||
patient.addName().setFamily("Tester").addGiven("testSearchTokenParam2");
|
||||
myPatientDao.create(patient, mySrd);
|
||||
String id3 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
patient = new Patient();
|
||||
patient.addIdentifier().setSystem("urn:system2").setValue("testSearchTokenParam002");
|
||||
patient.addName().setFamily("Tester").addGiven("testSearchTokenParam2");
|
||||
myPatientDao.create(patient, mySrd);
|
||||
String id4 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
{
|
||||
// Match system="urn:system" and value = *
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||
map.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", null));
|
||||
IBundleProvider retrieved = myPatientDao.search(map);
|
||||
assertEquals(2, retrieved.size().intValue());
|
||||
myCaptureQueriesListener.clear();
|
||||
List<String> values = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(values, containsInAnyOrder(id1, id2));
|
||||
}
|
||||
{
|
||||
// Match system="urn:system" and value = ""
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||
map.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", ""));
|
||||
IBundleProvider retrieved = myPatientDao.search(map);
|
||||
assertEquals(2, retrieved.size().intValue());
|
||||
myCaptureQueriesListener.clear();
|
||||
List<String> values = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(values, containsInAnyOrder(id1, id2));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||
|
@ -8,6 +9,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
|
||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
|
@ -18,13 +20,15 @@ import ca.uhn.fhir.rest.param.ReferenceParam;
|
|||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.DateTimeType;
|
||||
import org.hl7.fhir.r4.model.Enumerations;
|
||||
import org.hl7.fhir.r4.model.Organization;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.aop.framework.AopProxyUtils;
|
||||
|
@ -41,8 +45,15 @@ import java.util.stream.Collectors;
|
|||
import static org.apache.commons.lang3.StringUtils.leftPad;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.matchesPattern;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
|
||||
public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
|
||||
|
@ -53,6 +64,8 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
|
|||
private ISearchDao mySearchEntityDao;
|
||||
@Autowired
|
||||
private ISearchResultDao mySearchResultDao;
|
||||
@Autowired
|
||||
private MatchUrlService myMatchUrlService;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
|
@ -123,8 +136,22 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
|
|||
String uuid;
|
||||
List<String> ids;
|
||||
|
||||
// Search with count only
|
||||
params = new SearchParameterMap();
|
||||
// Search with count only (synchronous)
|
||||
params = new SearchParameterMap().setLoadSynchronous(true);
|
||||
params.add(Patient.SP_NAME, new StringParam("FAM"));
|
||||
params.setSummaryMode((SummaryEnum.COUNT));
|
||||
myCaptureQueriesListener.clear();
|
||||
results = myPatientDao.search(params);
|
||||
String sql = myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(sql, containsString("COUNT(DISTINCT "));
|
||||
uuid = results.getUuid();
|
||||
ourLog.info("** Search returned UUID: {}", uuid);
|
||||
assertEquals(201, results.size().intValue());
|
||||
ids = toUnqualifiedVersionlessIdValues(results, 0, 10, true);
|
||||
assertThat(ids, empty());
|
||||
|
||||
// Search with count only (non-synchronous)
|
||||
params = new SearchParameterMap().setLoadSynchronous(false);
|
||||
params.add(Patient.SP_NAME, new StringParam("FAM"));
|
||||
params.setSummaryMode((SummaryEnum.COUNT));
|
||||
results = myPatientDao.search(params);
|
||||
|
@ -744,13 +771,28 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
|
|||
|
||||
String resultingQueryNotFormatted = queries.get(0);
|
||||
assertEquals(1, StringUtils.countMatches(resultingQueryNotFormatted, "Patient.managingOrganization"), resultingQueryNotFormatted);
|
||||
assertThat(resultingQueryNotFormatted, containsString("TARGET_RESOURCE_ID in ('" + ids.get(0) + "' , '" + ids.get(1) + "' , '" + ids.get(2) + "' , '" + ids.get(3) + "' , '" + ids.get(4) + "')"));
|
||||
assertThat(resultingQueryNotFormatted, containsString("TARGET_RESOURCE_ID IN ('" + ids.get(0) + "','" + ids.get(1) + "','" + ids.get(2) + "','" + ids.get(3) + "','" + ids.get(4) + "')"));
|
||||
|
||||
// Ensure that the search actually worked
|
||||
assertEquals(5, search.size().intValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChainedSearchUsesJoinNotSubselect() {
|
||||
myCaptureQueriesListener.clear();
|
||||
|
||||
RuntimeResourceDefinition resourceDef = myFhirCtx.getResourceDefinition("Observation");
|
||||
SearchParameterMap params = myMatchUrlService.translateMatchUrl("/Observation?subject:patient.identifier=urn:oid:ZOOP.MRN.OID|1234", resourceDef, null);
|
||||
params.setLoadSynchronous(true);
|
||||
myObservationDao.search(params);
|
||||
myCaptureQueriesListener.logSelectQueries();
|
||||
|
||||
String selectQuery = myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, true);
|
||||
ourLog.info(selectQuery);
|
||||
assertEquals(2, StringUtils.countMatches(selectQuery, "JOIN"));
|
||||
assertEquals(1, StringUtils.countMatches(selectQuery, "SELECT"));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void afterResetDao() {
|
||||
|
@ -1128,7 +1170,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
|
|||
// The search itself
|
||||
resultingQueryNotFormatted = queries.get(1);
|
||||
assertEquals(1, StringUtils.countMatches(resultingQueryNotFormatted, "Patient.managingOrganization"), resultingQueryNotFormatted);
|
||||
assertThat(resultingQueryNotFormatted, matchesPattern(".*TARGET_RESOURCE_ID in \\('[0-9]+' , '[0-9]+' , '[0-9]+' , '[0-9]+' , '[0-9]+'\\).*"));
|
||||
assertThat(resultingQueryNotFormatted, matchesPattern(".*TARGET_RESOURCE_ID IN \\('[0-9]+','[0-9]+','[0-9]+','[0-9]+','[0-9]+'\\).*"));
|
||||
|
||||
// Ensure that the search actually worked
|
||||
assertEquals(5, search.size().intValue());
|
||||
|
|
|
@ -90,12 +90,12 @@ public class FhirResourceDaoR4SortTest extends BaseJpaR4Test {
|
|||
map = new SearchParameterMap();
|
||||
map.setSort(new SortSpec("_id", SortOrderEnum.ASC));
|
||||
ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(ids, contains(id1, id2, "Patient/AA", "Patient/AB"));
|
||||
assertThat(ids, contains("Patient/AA", "Patient/AB", id1, id2));
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setSort(new SortSpec("_id", SortOrderEnum.DESC));
|
||||
ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(ids, contains("Patient/AB", "Patient/AA", id2, id1));
|
||||
assertThat(ids, contains(id2, id1, "Patient/AB", "Patient/AA"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -258,7 +258,7 @@ public class FhirResourceDaoR4SortTest extends BaseJpaR4Test {
|
|||
map.setSort(new SortSpec(Patient.SP_FAMILY, SortOrderEnum.ASC).setChain(new SortSpec(Patient.SP_GIVEN, SortOrderEnum.ASC)));
|
||||
ids = toUnqualifiedVersionlessIds(myPatientDao.search(map));
|
||||
ourLog.info("** Got IDs: {}", ids);
|
||||
assertThat(ids, contains(pid1, pid2, pid3, pid4, pid5));
|
||||
assertThat(ids.toString(), ids, contains(pid2, pid4, pid5, pid3, pid1));
|
||||
assertEquals(5, ids.size());
|
||||
|
||||
}
|
||||
|
@ -316,7 +316,7 @@ public class FhirResourceDaoR4SortTest extends BaseJpaR4Test {
|
|||
map.setSort(new SortSpec("gender").setChain(new SortSpec("family", SortOrderEnum.ASC).setChain(new SortSpec("given", SortOrderEnum.ASC))));
|
||||
ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
ourLog.info("IDS: {}", ids);
|
||||
assertThat(ids, contains("Patient/CA", "Patient/AA", "Patient/AB", "Patient/BA", "Patient/BB"));
|
||||
assertThat(ids, contains("Patient/AA", "Patient/AB", "Patient/BA", "Patient/BB", "Patient/CA"));
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.add(Patient.SP_ACTIVE, new TokenParam(null, "true"));
|
||||
|
|
|
@ -98,7 +98,6 @@ import org.hl7.fhir.r4.model.StringType;
|
|||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
import org.hl7.fhir.r4.model.Timing;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
@ -122,12 +121,14 @@ import java.util.concurrent.ExecutorService;
|
|||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.countMatches;
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
|
@ -2385,7 +2386,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
found = toList(myPatientDao.search(new SearchParameterMap(Patient.SP_BIRTHDATE + "AAAA", new DateParam(ParamPrefixEnum.GREATERTHAN, "2000-01-01")).setLoadSynchronous(true)));
|
||||
assertEquals(0, found.size());
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unknown search parameter \"birthdateAAAA\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, active, address, address-city, address-country, address-postalcode, address-state, address-use, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
assertEquals("Unknown search parameter \"birthdateAAAA\" for resource type \"Patient\". Valid search parameters for this search are: [_id, _language, _lastUpdated, active, address, address-city, address-country, address-postalcode, address-state, address-use, birthdate, death-date, deceased, email, family, gender, general-practitioner, given, identifier, language, link, name, organization, phone, phonetic, telecom]", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3029,16 +3030,14 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
pm.setSort(new SortSpec(Patient.SP_BIRTHDATE));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
// Nulls first for H2
|
||||
assertThat(actual, contains(id4, id1, id2, id3));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testtestSortByDate"));
|
||||
pm.setSort(new SortSpec(Patient.SP_BIRTHDATE).setOrder(SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
// Nulls first for H2
|
||||
assertThat(actual, contains(id4, id1, id2, id3));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testtestSortByDate"));
|
||||
|
@ -3046,7 +3045,6 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id3, id2, id1, id4));
|
||||
// assertThat(actual, contains(id4, id3, id2, id1));
|
||||
|
||||
}
|
||||
|
||||
|
@ -3084,6 +3082,20 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
assertThat(actual, contains(toValues(id3, id2, id1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortByInvalidParameter() {
|
||||
|
||||
SearchParameterMap pm = SearchParameterMap.newSynchronous();
|
||||
pm.setSort(new SortSpec("hello", SortOrderEnum.DESC));
|
||||
try {
|
||||
myObservationDao.search(pm);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unknown _sort parameter value \"hello\" for resource type \"Observation\" (Note: sort parameters values must use a valid Search Parameter). Valid values for this search are: [_id, _language, _lastUpdated, based-on, category, code, code-value-concept, code-value-date, code-value-quantity, code-value-string, combo-code, combo-code-value-concept, combo-code-value-quantity, combo-data-absent-reason, combo-value-concept, combo-value-quantity, component-code, component-code-value-concept, component-code-value-quantity, component-data-absent-reason, component-value-concept, component-value-quantity, data-absent-reason, date, derived-from, device, encounter, focus, has-member, identifier, method, part-of, patient, performer, specimen, status, subject, value-concept, value-date, value-quantity, value-string]", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSortById() {
|
||||
String methodName = "testSortBTyId";
|
||||
|
@ -3097,15 +3109,21 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
IIdType id2 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.setId(methodName);
|
||||
p.setId(methodName+"1");
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
IIdType idMethodName = myPatientDao.update(p, mySrd).getId().toUnqualifiedVersionless();
|
||||
assertEquals(methodName, idMethodName.getIdPart());
|
||||
IIdType idMethodName1 = myPatientDao.update(p, mySrd).getId().toUnqualifiedVersionless();
|
||||
assertEquals(methodName+"1", idMethodName1.getIdPart());
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
IIdType id3 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
p = new Patient();
|
||||
p.setId(methodName+"2");
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
IIdType idMethodName2 = myPatientDao.update(p, mySrd).getId().toUnqualifiedVersionless();
|
||||
assertEquals(methodName+"2", idMethodName2.getIdPart());
|
||||
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
IIdType id4 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
@ -3113,26 +3131,26 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
SearchParameterMap pm;
|
||||
List<IIdType> actual;
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm = SearchParameterMap.newSynchronous();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
|
||||
pm.setSort(new SortSpec(IAnyResource.SP_RES_ID));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(5, actual.size());
|
||||
assertThat(actual, contains(id1, id2, id3, id4, idMethodName));
|
||||
assertEquals(6, actual.size());
|
||||
assertThat(actual, contains(idMethodName1, idMethodName2, id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm = SearchParameterMap.newSynchronous();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
|
||||
pm.setSort(new SortSpec(IAnyResource.SP_RES_ID).setOrder(SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(5, actual.size());
|
||||
assertThat(actual, contains(id1, id2, id3, id4, idMethodName));
|
||||
assertEquals(6, actual.size());
|
||||
assertThat(actual, contains(idMethodName1, idMethodName2, id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm = SearchParameterMap.newSynchronous();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName));
|
||||
pm.setSort(new SortSpec(IAnyResource.SP_RES_ID).setOrder(SortOrderEnum.DESC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(5, actual.size());
|
||||
assertThat(actual, contains(idMethodName, id4, id3, id2, id1));
|
||||
assertEquals(6, actual.size());
|
||||
assertThat(actual, contains(id4, id3, id2, id1, idMethodName2, idMethodName1));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -3140,6 +3158,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
String methodName = "testSortByLastUpdated";
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setActive(true);
|
||||
p.addIdentifier().setSystem("urn:system1").setValue(methodName);
|
||||
p.addName().setFamily(methodName);
|
||||
IIdType id1 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
@ -3147,6 +3166,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
TestUtil.sleepOneClick();
|
||||
|
||||
p = new Patient();
|
||||
p.setActive(true);
|
||||
p.addIdentifier().setSystem("urn:system2").setValue(methodName);
|
||||
p.addName().setFamily(methodName);
|
||||
IIdType id2 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
@ -3154,6 +3174,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
TestUtil.sleepOneClick();
|
||||
|
||||
p = new Patient();
|
||||
p.setActive(true);
|
||||
p.addIdentifier().setSystem("urn:system3").setValue(methodName);
|
||||
p.addName().setFamily(methodName);
|
||||
IIdType id3 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
@ -3161,6 +3182,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
TestUtil.sleepOneClick();
|
||||
|
||||
p = new Patient();
|
||||
p.setActive(true);
|
||||
p.addIdentifier().setSystem("urn:system4").setValue(methodName);
|
||||
p.addName().setFamily(methodName);
|
||||
IIdType id4 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
@ -3168,22 +3190,30 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
SearchParameterMap pm;
|
||||
List<IIdType> actual;
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
// With no search parameter
|
||||
pm = SearchParameterMap.newSynchronous();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
// With a search parameter
|
||||
pm = SearchParameterMap.newSynchronous();
|
||||
pm.add(Patient.SP_ACTIVE, new TokenParam("true"));
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = SearchParameterMap.newSynchronous();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm = SearchParameterMap.newSynchronous();
|
||||
pm.setSort(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.DESC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertThat(actual, contains(id4, id3, id2, id1));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm = SearchParameterMap.newSynchronous();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam(null, methodName));
|
||||
pm.setSort(new SortSpec(Patient.SP_NAME).setChain(new SortSpec(Constants.PARAM_LASTUPDATED, SortOrderEnum.DESC)));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
|
@ -3224,7 +3254,6 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testSortByQuantity() {
|
||||
Observation res;
|
||||
|
||||
|
@ -3246,13 +3275,13 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
|
||||
SearchParameterMap pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Observation.SP_VALUE_QUANTITY));
|
||||
List<IIdType> actual = toUnqualifiedVersionlessIds(myConceptMapDao.search(pm));
|
||||
List<IIdType> actual = toUnqualifiedVersionlessIds(myObservationDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Observation.SP_VALUE_QUANTITY, SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myConceptMapDao.search(pm));
|
||||
actual = toUnqualifiedVersionlessIds(myObservationDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
|
@ -3353,27 +3382,30 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
SearchParameterMap pm;
|
||||
List<IIdType> actual;
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
myCaptureQueriesListener.clear();
|
||||
pm = SearchParameterMap.newSynchronous();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", string));
|
||||
pm.setSort(new SortSpec(Patient.SP_FAMILY));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id4, id1, id2, id3));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
String sql = myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(sql, countMatches(sql, "JOIN"), equalTo(2));
|
||||
assertThat(sql, countMatches(sql, "ORDER BY"), equalTo(1));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm = SearchParameterMap.newSynchronous();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", string));
|
||||
pm.setSort(new SortSpec(Patient.SP_FAMILY).setOrder(SortOrderEnum.ASC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id4, id1, id2, id3));
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm = SearchParameterMap.newSynchronous();
|
||||
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", string));
|
||||
pm.setSort(new SortSpec(Patient.SP_FAMILY).setOrder(SortOrderEnum.DESC));
|
||||
actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id3, id2, id1, id4));
|
||||
// assertThat(actual, contains(id4, id3, id2, id1));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3492,32 +3524,31 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testSortByUri() {
|
||||
ConceptMap res = new ConceptMap();
|
||||
res.addGroup().setSource("http://foo2");
|
||||
res.setUrl("http://foo2");
|
||||
IIdType id2 = myConceptMapDao.create(res, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
res = new ConceptMap();
|
||||
res.addGroup().setSource("http://foo1");
|
||||
res.setUrl("http://foo1");
|
||||
IIdType id1 = myConceptMapDao.create(res, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
res = new ConceptMap();
|
||||
res.addGroup().setSource("http://bar3");
|
||||
res.setUrl("http://foo3");
|
||||
IIdType id3 = myConceptMapDao.create(res, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
res = new ConceptMap();
|
||||
res.addGroup().setSource("http://bar4");
|
||||
res.setUrl("http://foo4");
|
||||
IIdType id4 = myConceptMapDao.create(res, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameterMap pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(ConceptMap.SP_SOURCE));
|
||||
pm.setSort(new SortSpec(ConceptMap.SP_URL));
|
||||
List<IIdType> actual = toUnqualifiedVersionlessIds(myConceptMapDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id1, id2, id3, id4));
|
||||
|
||||
pm = new SearchParameterMap();
|
||||
pm.setSort(new SortSpec(Encounter.SP_LENGTH, SortOrderEnum.DESC));
|
||||
pm.setSort(new SortSpec(ConceptMap.SP_URL, SortOrderEnum.DESC));
|
||||
actual = toUnqualifiedVersionlessIds(myConceptMapDao.search(pm));
|
||||
assertEquals(4, actual.size());
|
||||
assertThat(actual, contains(id4, id3, id2, id1));
|
||||
|
@ -3534,13 +3565,13 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
|
|||
|
||||
SearchParameterMap map;
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map = SearchParameterMap.newSynchronous();
|
||||
map.add(IAnyResource.SP_RES_ID, new StringParam(id1.getIdPart()));
|
||||
map.setLastUpdated(new DateRangeParam("2001", "2003"));
|
||||
map.setSort(new SortSpec(Constants.PARAM_LASTUPDATED));
|
||||
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), empty());
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map = SearchParameterMap.newSynchronous();
|
||||
map.add(IAnyResource.SP_RES_ID, new StringParam(id1.getIdPart()));
|
||||
map.setLastUpdated(new DateRangeParam("2001", "2003"));
|
||||
map.setSort(new SortSpec(Patient.SP_NAME));
|
||||
|
|
|
@ -442,8 +442,8 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
|||
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
|
||||
unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertThat(unformattedSql, stringContainsInOrder(
|
||||
"IDX_STRING='Patient?identifier=urn%7C111'",
|
||||
"HASH_SYS_AND_VALUE in ('-3122824860083758210')"
|
||||
"IDX_STRING = 'Patient?identifier=urn%7C111'",
|
||||
"HASH_SYS_AND_VALUE = '-3122824860083758210'"
|
||||
));
|
||||
assertThat(unformattedSql, not(containsString(("RES_DELETED_AT"))));
|
||||
assertThat(unformattedSql, not(containsString(("RES_TYPE"))));
|
||||
|
@ -461,7 +461,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
|||
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
|
||||
assertThat(toUnqualifiedVersionlessIdValues(outcome), containsInAnyOrder(id1));
|
||||
unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertThat(unformattedSql, containsString("HASH_SYS_AND_VALUE in ('4101160957635429999' , '-3122824860083758210')"));
|
||||
assertThat(unformattedSql, containsString("HASH_SYS_AND_VALUE IN ('4101160957635429999','-3122824860083758210')"));
|
||||
assertThat(unformattedSql, not(containsString(("IDX_STRING"))));
|
||||
assertThat(unformattedSql, not(containsString(("RES_DELETED_AT"))));
|
||||
assertThat(unformattedSql, not(containsString(("RES_TYPE"))));
|
||||
|
@ -579,8 +579,8 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
|||
assertThat(toUnqualifiedVersionlessIdValues(outcome), containsInAnyOrder(srId));
|
||||
unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertThat(unformattedSql, stringContainsInOrder(
|
||||
"IDX_STRING='ServiceRequest?identifier=sys%7C111&patient=Patient%2F" + ptId.getIdPart() + "&performer=Practitioner%2F" + practId.getIdPart() + "'",
|
||||
"HASH_SYS_AND_VALUE in ('6795110643554413877')"
|
||||
"IDX_STRING = 'ServiceRequest?identifier=sys%7C111&patient=Patient%2F" + ptId.getIdPart() + "&performer=Practitioner%2F" + practId.getIdPart() + "'",
|
||||
"HASH_SYS_AND_VALUE = '6795110643554413877'"
|
||||
));
|
||||
assertThat(unformattedSql, not(containsString(("RES_DELETED_AT"))));
|
||||
assertThat(unformattedSql, not(containsString(("RES_TYPE"))));
|
||||
|
@ -601,8 +601,8 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
|||
assertThat(toUnqualifiedVersionlessIdValues(outcome), containsInAnyOrder(srId));
|
||||
unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertThat(unformattedSql, stringContainsInOrder(
|
||||
"SRC_PATH in ('ServiceRequest.subject.where(resolve() is Patient)')",
|
||||
"SRC_PATH in ('ServiceRequest.performer')"
|
||||
"SRC_PATH = 'ServiceRequest.subject.where(resolve() is Patient)'",
|
||||
"SRC_PATH = 'ServiceRequest.performer'"
|
||||
));
|
||||
assertThat(unformattedSql, not(containsString(("RES_DELETED_AT"))));
|
||||
assertThat(unformattedSql, not(containsString(("RES_TYPE"))));
|
||||
|
|
|
@ -439,6 +439,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
myGroupDao.update(group);
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.getText().setStatus(Narrative.NarrativeStatus.GENERATED).setDivAsString("<div>Hello</div>");
|
||||
patient.setId("DEF");
|
||||
patient.setActive(true);
|
||||
myPatientDao.update(patient);
|
||||
|
@ -450,7 +451,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
|
||||
Observation obs = new Observation();
|
||||
obs.getMeta().addProfile("http://example.com/fhir/StructureDefinition/vitalsigns-2");
|
||||
obs.getText().setDivAsString("<div>Hello</div>");
|
||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED).setDivAsString("<div>Hello</div>");
|
||||
obs.getCategoryFirstRep().addCoding().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs");
|
||||
obs.addPerformer(new Reference("Practitioner/P"));
|
||||
obs.setEffective(DateTimeType.now());
|
||||
|
@ -459,9 +460,11 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("123-4").setDisplay("Display 3");
|
||||
|
||||
OperationOutcome oo;
|
||||
|
||||
// Non-existent target
|
||||
obs.setSubject(new Reference("Group/123"));
|
||||
OperationOutcome oo = validateAndReturnOutcome(obs);
|
||||
oo = validateAndReturnOutcome(obs);
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo));
|
||||
assertEquals("Unable to resolve resource 'Group/123'", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
||||
|
||||
|
@ -523,9 +526,11 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
|||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("123-4").setDisplay("Display 3");
|
||||
|
||||
OperationOutcome oo;
|
||||
|
||||
// Non-existent target
|
||||
obs.setSubject(new Reference("Group/123"));
|
||||
OperationOutcome oo = validateAndReturnOutcome(obs);
|
||||
oo = validateAndReturnOutcome(obs);
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo));
|
||||
assertEquals("Unable to resolve resource 'Group/123'", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
||||
|
||||
|
|
|
@ -28,6 +28,9 @@ import ca.uhn.fhir.rest.param.DateAndListParam;
|
|||
import ca.uhn.fhir.rest.param.DateOrListParam;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.HasAndListParam;
|
||||
import ca.uhn.fhir.rest.param.HasOrListParam;
|
||||
import ca.uhn.fhir.rest.param.HasParam;
|
||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
|
@ -50,6 +53,8 @@ import org.hl7.fhir.r4.model.IdType;
|
|||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.hl7.fhir.r4.model.Organization;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.Practitioner;
|
||||
import org.hl7.fhir.r4.model.PractitionerRole;
|
||||
import org.hl7.fhir.r4.model.SearchParameter;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -645,7 +650,6 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateInTransaction_ServerId_WithPartition() {
|
||||
createUniqueCompositeSp();
|
||||
|
@ -769,7 +773,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
assertEquals(myPartitionDate, resourceTable.getPartitionId().getPartitionDate());
|
||||
|
||||
// HFJ_SPIDX_TOKEN
|
||||
ourLog.info("Tokens:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||
ourLog.info("Tokens:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
|
||||
assertEquals(3, myResourceIndexedSearchParamTokenDao.countForResourceId(patientId));
|
||||
});
|
||||
|
||||
|
@ -790,7 +794,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
assertEquals(myPartitionDate, resourceTable.getPartitionId().getPartitionDate());
|
||||
|
||||
// HFJ_SPIDX_TOKEN
|
||||
ourLog.info("Tokens:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||
ourLog.info("Tokens:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
|
||||
assertEquals(3, myResourceIndexedSearchParamTokenDao.countForResourceId(patientId));
|
||||
|
||||
// HFJ_RES_VER
|
||||
|
@ -1031,7 +1035,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING='true'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING = 'true'"));
|
||||
}
|
||||
|
||||
// :missing=false
|
||||
|
@ -1048,7 +1052,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING='false'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING = 'false'"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1072,8 +1076,8 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myparamsto1_.PARTITION_ID='1'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING='true'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID = '1'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING = 'true'"), searchSql);
|
||||
}
|
||||
|
||||
// :missing=false
|
||||
|
@ -1089,8 +1093,8 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myparamsst1_.PARTITION_ID='1'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING='false'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID = '1'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING = 'false'"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1113,8 +1117,8 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING='true'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID IS NULL"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING = 'true'"));
|
||||
}
|
||||
|
||||
// :missing=false
|
||||
|
@ -1130,8 +1134,8 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING='false'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID IS NULL"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING = 'false'"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1158,7 +1162,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HFJ_RES_PARAM_PRESENT"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE='1919227773735728687'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE = '1919227773735728687'"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1183,10 +1187,10 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "mysearchpa1_.PARTITION_ID='1'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HFJ_RES_PARAM_PRESENT"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE='-3438137196820602023'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID = '1'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HFJ_RES_PARAM_PRESENT"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE = '-3438137196820602023'"), searchSql);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1211,10 +1215,10 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "mysearchpa1_.PARTITION_ID='1'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HFJ_RES_PARAM_PRESENT"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE='1919227773735728687'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID = '1'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HFJ_RES_PARAM_PRESENT"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE = '1919227773735728687'"), searchSql);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1237,10 +1241,10 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "mysearchpa1_.PARTITION_ID is null"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HFJ_RES_PARAM_PRESENT"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE='1919227773735728687'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID IS NULL"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HFJ_RES_PARAM_PRESENT"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE = '1919227773735728687'"), searchSql);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1390,8 +1394,8 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_VALUE_LOW"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_VALUE_LOW"), searchSql);
|
||||
|
||||
// Date OR param
|
||||
|
||||
|
@ -1521,6 +1525,45 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_HasParam_SearchOnePartition() {
|
||||
addReadPartition(1);
|
||||
addCreatePartition(1, null);
|
||||
Organization org = new Organization();
|
||||
org.setId("ORG");
|
||||
org.setName("ORG");
|
||||
myOrganizationDao.update(org);
|
||||
|
||||
addReadPartition(1);
|
||||
addCreatePartition(1, null);
|
||||
Practitioner practitioner = new Practitioner();
|
||||
practitioner.setId("PRACT");
|
||||
practitioner.addName().setFamily("PRACT");
|
||||
myPractitionerDao.update(practitioner);
|
||||
|
||||
addReadPartition(1);
|
||||
addCreatePartition(1, null);
|
||||
PractitionerRole role = new PractitionerRole();
|
||||
role.setId("ROLE");
|
||||
role.getPractitioner().setReference("Practitioner/PRACT");
|
||||
role.getOrganization().setReference("Organization/ORG");
|
||||
myPractitionerRoleDao.update(role);
|
||||
|
||||
addReadPartition(1);
|
||||
SearchParameterMap params = SearchParameterMap.newSynchronous();
|
||||
HasAndListParam value = new HasAndListParam();
|
||||
value.addAnd(new HasOrListParam().addOr(new HasParam("PractitionerRole", "practitioner", "_id", "ROLE")));
|
||||
params.add("_has", value);
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider outcome = myPractitionerDao.search(params);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(1);
|
||||
assertEquals(1, outcome.getResources(0, 1).size());
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearch_StringParam_SearchAllPartitions() {
|
||||
|
@ -1684,7 +1727,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM='http://system'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM = 'http://system'"));
|
||||
|
||||
// And with another param
|
||||
|
||||
|
@ -1694,15 +1737,15 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.add(Constants.PARAM_TAG, new TokenParam("http://system", "code2").setModifier(TokenParamModifier.NOT));
|
||||
map.add(Patient.SP_IDENTIFIER, new TokenParam("http://foo", "bar"));
|
||||
map.setLoadSynchronous(true);
|
||||
results = myPatientDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
results = myPatientDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1));
|
||||
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM='http://system'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myparamsto1_.HASH_SYS_AND_VALUE in"));
|
||||
assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM = 'http://system'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t1.HASH_SYS_AND_VALUE ="), searchSql);
|
||||
|
||||
|
||||
}
|
||||
|
@ -1725,8 +1768,8 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM='http://system'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID IS NULL"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM = 'http://system'"));
|
||||
|
||||
assertThat(ids.toString(), ids, Matchers.contains(patientIdNull));
|
||||
}
|
||||
|
@ -1753,7 +1796,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM='http://system'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM = 'http://system'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1775,7 +1818,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM='http://system'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM = 'http://system'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1790,16 +1833,18 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Constants.PARAM_TAG, new TokenParam("http://system", "code"));
|
||||
map.setLoadSynchronous(true);
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
||||
// If this ever got optimized down to 1 that would be OK too
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM='http://system'"));
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "JOIN"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM = 'http://system'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1824,7 +1869,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM='http://system'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM = 'http://system'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1849,7 +1894,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM='http://system'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM = 'http://system'"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1872,7 +1917,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "IDX_STRING='Patient?birthdate=2020-01-01'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "IDX_STRING = 'Patient?birthdate=2020-01-01'"));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1895,7 +1940,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "IDX_STRING='Patient?birthdate=2020-01-01'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "IDX_STRING = 'Patient?birthdate=2020-01-01'"));
|
||||
|
||||
// Same query, different partition
|
||||
addReadPartition(2);
|
||||
|
@ -1929,10 +1974,10 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myresource1_.PARTITION_ID='1'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myresource1_.SRC_PATH in ('Observation.subject')"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myresource1_.TARGET_RESOURCE_ID='" + patientId.getIdPartAsLong() + "'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID = '1'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.SRC_PATH = 'Observation.subject'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.TARGET_RESOURCE_ID = '" + patientId.getIdPartAsLong() + "'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
|
||||
// Same query, different partition
|
||||
addReadPartition(2);
|
||||
|
@ -1962,14 +2007,13 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myObservationDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.contains(observationId));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myresource1_.PARTITION_ID is null"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myresource1_.SRC_PATH in ('Observation.subject')"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "myresource1_.TARGET_RESOURCE_ID='" + patientId.getIdPartAsLong() + "'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID IS NULL"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.SRC_PATH = 'Observation.subject'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.TARGET_RESOURCE_ID = '" + patientId.getIdPartAsLong() + "'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
|
||||
// Same query, different partition
|
||||
|
@ -2004,9 +2048,9 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.PARTITION_ID='1' "));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "and forcedid0_.RESOURCE_TYPE='Patient'"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.PARTITION_ID='1'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "and forcedid0_.RESOURCE_TYPE='Patient'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
|
||||
// Same query, different partition
|
||||
addReadPartition(2);
|
||||
|
@ -2040,9 +2084,9 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.PARTITION_ID is null"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.RESOURCE_TYPE='Patient' "));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.PARTITION_ID is null"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.RESOURCE_TYPE='Patient'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
|
||||
// Same query, different partition
|
||||
addReadPartition(2);
|
||||
|
@ -2090,24 +2134,26 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
assertEquals(4, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
|
||||
// Resolve resource
|
||||
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(0, countMatches(sql, "PARTITION_ID="));
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("SQL:{}", searchSql);
|
||||
assertEquals(0, countMatches(searchSql, "PARTITION_ID="), searchSql);
|
||||
|
||||
// Fetch history resource
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true);
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(0, countMatches(sql, "PARTITION_ID"));
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true);
|
||||
ourLog.info("SQL:{}", searchSql);
|
||||
assertEquals(0, countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
|
||||
// Fetch history resource
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true);
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(0, countMatches(sql, "PARTITION_IDAA"));
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true);
|
||||
ourLog.info("SQL:{}", searchSql);
|
||||
assertEquals(0, countMatches(searchSql, "PARTITION_ID="), searchSql.replace(" ", "").toUpperCase());
|
||||
assertEquals(0, countMatches(searchSql, "PARTITION_IDIN"), searchSql.replace(" ", "").toUpperCase());
|
||||
|
||||
// Fetch history resource
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(3).getSql(true, true);
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(0, countMatches(sql, "PARTITION_IDAA"));
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(3).getSql(true, true);
|
||||
ourLog.info("SQL:{}", searchSql);
|
||||
assertEquals(0, countMatches(searchSql, "PARTITION_ID="), searchSql.replace(" ", "").toUpperCase());
|
||||
assertEquals(0, countMatches(searchSql, "PARTITION_IDIN"), searchSql.replace(" ", "").toUpperCase());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -2158,17 +2204,17 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
// Fetch history resource
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true);
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(0, countMatches(sql, "PARTITION_ID"));
|
||||
assertEquals(0, countMatches(sql, "PARTITION_ID="));
|
||||
|
||||
// Fetch history resource
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true);
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(0, countMatches(sql, "PARTITION_IDAA"));
|
||||
assertEquals(0, countMatches(sql, "PARTITION_ID="));
|
||||
|
||||
// Fetch history resource
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(3).getSql(true, true);
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(0, countMatches(sql, "PARTITION_IDAA"));
|
||||
assertEquals(0, countMatches(sql, "PARTITION_ID="));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -2221,20 +2267,21 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
assertEquals(3, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
|
||||
// Count
|
||||
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(1, countMatches(sql, "count("));
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID='1'"));
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("SQL:{}", searchSql);
|
||||
assertEquals(1, countMatches(searchSql, "count("));
|
||||
assertEquals(1, countMatches(searchSql, "PARTITION_ID='1'"));
|
||||
|
||||
// Fetch history
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true);
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID='1'"));
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true);
|
||||
ourLog.info("SQL:{}", searchSql);
|
||||
assertEquals(1, countMatches(searchSql, "PARTITION_ID='1'"));
|
||||
|
||||
// Fetch history resource
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true);
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(0, countMatches(sql, "PARTITION_IDAA"));
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true);
|
||||
ourLog.info("SQL:{}", searchSql);
|
||||
assertEquals(0, countMatches(searchSql, "PARTITION_ID="), searchSql.replace(" ", "").toUpperCase());
|
||||
assertEquals(0, countMatches(searchSql, "PARTITION_IDIN"), searchSql.replace(" ", "").toUpperCase());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -2257,19 +2304,20 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
assertEquals(3, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
|
||||
// Count
|
||||
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID is null"));
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("SQL:{}", searchSql);
|
||||
assertEquals(1, countMatches(searchSql, "PARTITION_ID is null"), searchSql);
|
||||
|
||||
// Fetch history
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true);
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID is null"));
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true);
|
||||
ourLog.info("SQL:{}", searchSql);
|
||||
assertEquals(1, countMatches(searchSql, "PARTITION_ID is null"), searchSql);
|
||||
|
||||
// Fetch history resource
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true);
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(0, countMatches(sql, "PARTITION_IDzzz"));
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true);
|
||||
ourLog.info("SQL:{}", searchSql);
|
||||
assertEquals(0, countMatches(searchSql, "PARTITION_ID="), searchSql.replace(" ", "").toUpperCase());
|
||||
assertEquals(0, countMatches(searchSql, "PARTITION_IDIN"), searchSql.replace(" ", "").toUpperCase());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -2341,18 +2389,18 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
assertEquals(3, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
|
||||
// Resolve resource
|
||||
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID is null"));
|
||||
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true).toUpperCase();
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID IS NULL"));
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID"));
|
||||
|
||||
// Fetch history resource
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true);
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID is null"));
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true).toUpperCase();
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID IS NULL"));
|
||||
|
||||
// Resolve forced IDs
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true);
|
||||
assertEquals(1, countMatches(sql, "forcedid0_.RESOURCE_PID in"), sql);
|
||||
assertEquals(0, countMatches(sql, "PARTITION_ID is null"), sql);
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true).toUpperCase();
|
||||
assertEquals(1, countMatches(sql, "FORCEDID0_.RESOURCE_PID IN"), sql);
|
||||
assertEquals(0, countMatches(sql, "PARTITION_ID IS NULL"), sql);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -68,7 +68,7 @@ public class SearchWithInterceptorR4Test extends BaseJpaR4Test {
|
|||
String query = list.get(0).getSql(true, false);
|
||||
ourLog.info("Query: {}", query);
|
||||
|
||||
assertThat(query, containsString("HASH_SYS_AND_VALUE in ('3788488238034018567')"));
|
||||
assertThat(query, containsString("HASH_SYS_AND_VALUE = '3788488238034018567'"));
|
||||
|
||||
} finally {
|
||||
myInterceptorRegistry.unregisterInterceptor(interceptor);
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.junit.jupiter.api.AfterAll;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
@ -95,11 +96,17 @@ public class FhirResourceDaoR5SearchNoFtTest extends BaseJpaR5Test {
|
|||
role.getOrganization().setReference("Organization/ORG");
|
||||
myPractitionerRoleDao.update(role);
|
||||
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
runInTransaction(()->{
|
||||
ourLog.info("Links:\n * {}", myResourceLinkDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||
});
|
||||
|
||||
SearchParameterMap params = SearchParameterMap.newSynchronous();
|
||||
HasAndListParam value = new HasAndListParam();
|
||||
value.addAnd(new HasOrListParam().addOr(new HasParam("PractitionerRole", "practitioner", "_id", "ROLE")));
|
||||
params.add("_has", value);
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider outcome = myPractitionerDao.search(params);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(1);
|
||||
assertEquals(1, outcome.getResources(0, 1).size());
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ public class HookInterceptorR4Test extends BaseResourceProviderR4Test {
|
|||
@Autowired
|
||||
IdHelperService myIdHelperService;
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
public void before() throws Exception {
|
||||
super.before();
|
||||
|
@ -39,6 +40,7 @@ public class HookInterceptorR4Test extends BaseResourceProviderR4Test {
|
|||
myDaoConfig.setExpungeEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@AfterEach
|
||||
public void after() throws Exception {
|
||||
myDaoConfig.setExpungeEnabled(new DaoConfig().isExpungeEnabled());
|
||||
|
|
|
@ -49,9 +49,16 @@ import static org.apache.commons.lang3.time.DateUtils.MILLIS_PER_SECOND;
|
|||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.Mockito.atLeast;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.timeout;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Test {
|
||||
|
||||
|
@ -95,11 +102,11 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
|
||||
Bundle results = myClient.search().forResource(Patient.class).returnBundle(Bundle.class).execute();
|
||||
|
||||
verify(interceptor, times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED), myParamsCaptor.capture());
|
||||
verify(interceptor, times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, times(0)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_PASS_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, times(0)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_FAILED), myParamsCaptor.capture());
|
||||
verify(interceptor, timeout(10000).times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED), myParamsCaptor.capture());
|
||||
verify(interceptor, timeout(10000).times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, timeout(10000).times(0)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, timeout(10000).times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_PASS_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, timeout(10000).times(0)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_FAILED), myParamsCaptor.capture());
|
||||
|
||||
SearchRuntimeDetails details = myParamsCaptor.getAllValues().get(0).get(SearchRuntimeDetails.class);
|
||||
assertEquals(SearchStatusEnum.PASSCMPLET, details.getSearchStatus());
|
||||
|
@ -108,11 +115,11 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
reset(interceptor);
|
||||
results = myClient.loadPage().next(results).execute();
|
||||
assertNotNull(results);
|
||||
verify(interceptor, times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED), myParamsCaptor.capture());
|
||||
verify(interceptor, times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, times(0)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_PASS_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, times(0)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_FAILED), myParamsCaptor.capture());
|
||||
verify(interceptor, timeout(10000).times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_FIRST_RESULT_LOADED), myParamsCaptor.capture());
|
||||
verify(interceptor, timeout(10000).times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_SELECT_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, timeout(10000).times(1)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, timeout(10000).times(0)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_PASS_COMPLETE), myParamsCaptor.capture());
|
||||
verify(interceptor, timeout(10000).times(0)).invoke(eq(Pointcut.JPA_PERFTRACE_SEARCH_FAILED), myParamsCaptor.capture());
|
||||
|
||||
}
|
||||
|
||||
|
@ -327,32 +334,12 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
}
|
||||
}
|
||||
|
||||
public class ReflexInterceptor extends ServerOperationInterceptorAdapter {
|
||||
@Override
|
||||
public void resourceCreated(RequestDetails theRequest, IBaseResource theResource) {
|
||||
if (theResource instanceof Patient) {
|
||||
((ServletRequestDetails) theRequest).getServletRequest().setAttribute("CREATED_PATIENT", theResource);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processingCompletedNormally(ServletRequestDetails theRequestDetails) {
|
||||
Patient createdPatient = (Patient) theRequestDetails.getServletRequest().getAttribute("CREATED_PATIENT");
|
||||
if (createdPatient != null) {
|
||||
Observation observation = new Observation();
|
||||
observation.setSubject(new Reference(createdPatient.getId()));
|
||||
|
||||
myClient.create().resource(observation).execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInterceptorExpandsSearch() {
|
||||
|
||||
@Interceptor
|
||||
class SearchExpandingInterceptor {
|
||||
|
||||
|
||||
@Hook(Pointcut.SERVER_INCOMING_REQUEST_POST_PROCESSED)
|
||||
public void enrich(RequestDetails theRequestDetails) {
|
||||
|
||||
|
@ -368,7 +355,7 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Patient p1 = new Patient();
|
||||
|
@ -380,7 +367,7 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
o1.setId("o1");
|
||||
o1.getSubject().setReference("Patient/p1");
|
||||
myObservationDao.update(o1);
|
||||
|
||||
|
||||
Patient p2 = new Patient();
|
||||
p2.setId("p2");
|
||||
p2.addIdentifier().setValue("p2");
|
||||
|
@ -417,11 +404,30 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
} finally {
|
||||
ourRestServer.unregisterInterceptor(interceptor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class ReflexInterceptor extends ServerOperationInterceptorAdapter {
|
||||
@Override
|
||||
public void resourceCreated(RequestDetails theRequest, IBaseResource theResource) {
|
||||
if (theResource instanceof Patient) {
|
||||
((ServletRequestDetails) theRequest).getServletRequest().setAttribute("CREATED_PATIENT", theResource);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processingCompletedNormally(ServletRequestDetails theRequestDetails) {
|
||||
Patient createdPatient = (Patient) theRequestDetails.getServletRequest().getAttribute("CREATED_PATIENT");
|
||||
if (createdPatient != null) {
|
||||
Observation observation = new Observation();
|
||||
observation.setSubject(new Reference(createdPatient.getId()));
|
||||
|
||||
myClient.create().resource(observation).execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void verifyDaoInterceptor(IServerInterceptor theDaoInterceptor) {
|
||||
ArgumentCaptor<ActionRequestDetails> ardCaptor;
|
||||
ArgumentCaptor<RestOperationTypeEnum> opTypeCaptor;
|
||||
|
|
|
@ -2050,13 +2050,21 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
c.getSubject().setReferenceElement(pId);
|
||||
IIdType cId = myClient.create().resource(c).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
ourLog.info("Resource IDs:\n * {}\n * {}\n * {}", oId, pId, cId);
|
||||
runInTransaction(() -> {
|
||||
ourLog.info("Resource Links:\n * {}", myResourceLinkDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
|
||||
ourLog.info("Resources:\n * {}", myResourceTableDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
|
||||
});
|
||||
|
||||
Thread.sleep(10);
|
||||
long time3 = System.currentTimeMillis();
|
||||
|
||||
// %3E=> %3C=<
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
HttpGet get = new HttpGet(ourServerBase + "/Patient/" + pId.getIdPart() + "/$everything?_lastUpdated=%3E" + new InstantType(new Date(time1)).getValueAsString());
|
||||
CloseableHttpResponse response = ourHttpClient.execute(get);
|
||||
myCaptureQueriesListener.logSelectQueries();
|
||||
try {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
|
@ -2918,6 +2926,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@Test
|
||||
public void testPagingOverEverythingSetWithNoPagingProvider() {
|
||||
ourRestServer.setPagingProvider(null);
|
||||
|
@ -2956,7 +2965,9 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
assertEquals(null, response.getTotalElement().getValue());
|
||||
assertThat(response.getLink("next").getUrl(), not(emptyString()));
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
response = myClient.fetchResourceFromUrl(Bundle.class, response.getLink("next").getUrl());
|
||||
myCaptureQueriesListener.logSelectQueries();
|
||||
|
||||
assertEquals(1, response.getEntry().size());
|
||||
assertEquals(21, response.getTotalElement().getValue().intValue());
|
||||
|
@ -4494,6 +4505,19 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), containsString("Unable to handle number prefix \"eb\" for value: eb100"));
|
||||
}
|
||||
|
||||
try {
|
||||
myClient
|
||||
.search()
|
||||
.forResource(MolecularSequence.class)
|
||||
.where(MolecularSequence.VARIANT_END.withPrefix(ParamPrefixEnum.STARTS_AFTER).number(100))
|
||||
.prettyPrint()
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), containsString("Unable to handle number prefix \"sa\" for value: sa100"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test()
|
||||
|
|
|
@ -233,11 +233,14 @@ public class ResourceProviderR5Test extends BaseResourceProviderR5Test {
|
|||
observation.setEffective(new DateTimeType("1965-08-10"));
|
||||
myObservationDao.create(observation).getId().toUnqualified();
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
Bundle output = myClient
|
||||
.search()
|
||||
.byUrl("Observation?_count=0")
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
ourLog.info("Output: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
myCaptureQueriesListener.logSelectQueries();
|
||||
|
||||
assertEquals(2, output.getTotal());
|
||||
assertEquals(0, output.getEntry().size());
|
||||
|
|
|
@ -121,10 +121,12 @@ public class PagingMultinodeProviderDstu3Test extends BaseResourceProviderDstu3T
|
|||
assertThat(found.getLink().stream().filter(l -> l.getRelation().equals("next")).map(l -> l.getUrl()).findAny()
|
||||
.orElseThrow(() -> new IllegalStateException("No next page link")).contains("_offset=10"), is(true));
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
found = ourClient
|
||||
.loadPage()
|
||||
.next(found)
|
||||
.execute();
|
||||
myCaptureQueriesListener.logSelectQueries();
|
||||
assertThat(toUnqualifiedVersionlessIdValues(found), contains("Patient/A010", "Patient/A011", "Patient/A012", "Patient/A013", "Patient/A014", "Patient/A015", "Patient/A016", "Patient/A017", "Patient/A018", "Patient/A019"));
|
||||
|
||||
found = ourClient
|
||||
|
|
|
@ -10,7 +10,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
|||
import ca.uhn.fhir.jpa.config.dstu3.BaseDstu3Config;
|
||||
import ca.uhn.fhir.jpa.dao.IResultIterator;
|
||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.LegacySearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
|
||||
|
@ -94,7 +94,7 @@ public class SearchCoordinatorSvcImplTest {
|
|||
private EntityManager myEntityManager;
|
||||
private int myExpectedNumberOfSearchBuildersCreated = 2;
|
||||
@Mock
|
||||
private SearchBuilder mySearchBuilder;
|
||||
private LegacySearchBuilder mySearchBuilder;
|
||||
@Mock
|
||||
private ISearchCacheSvc mySearchCacheSvc;
|
||||
@Mock
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package ca.uhn.fhir.jpa.search.builder.sql;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
public class GeneratedSqlTest {
|
||||
|
||||
@Test
|
||||
public void testBlockInlineNonBoundParameters() {
|
||||
assertThrows(AssertionError.class, () -> new GeneratedSql(false, "SELECT * FROM FOO WHERE t = '123'", Collections.emptyList()));
|
||||
assertThrows(AssertionError.class, () -> new GeneratedSql(false, "SELECT * FROM FOO WHERE t='123'", Collections.emptyList()));
|
||||
assertThrows(AssertionError.class, () -> new GeneratedSql(false, "SELECT * FROM FOO WHERE t in ('123')", Collections.emptyList()));
|
||||
assertThrows(AssertionError.class, () -> new GeneratedSql(false, "SELECT * FROM FOO WHERE t IN ('123')", Collections.emptyList()));
|
||||
assertThrows(AssertionError.class, () -> new GeneratedSql(false, "SELECT * FROM FOO WHERE t IN('123')", Collections.emptyList()));
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
|
@ -21,8 +21,20 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapsId;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Table(name = "HFJ_RES_VER_PROV", indexes = {
|
||||
@Index(name = "IDX_RESVERPROV_SOURCEURI", columnList = "SOURCE_URI"),
|
||||
|
@ -55,6 +67,15 @@ public class ResourceHistoryProvenanceEntity extends BasePartitionable {
|
|||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
b.append("resourceId", myResourceTable.getId());
|
||||
b.append("sourceUri", mySourceUri);
|
||||
b.append("requestId", myRequestId);
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
public void setResourceTable(ResourceTable theResourceTable) {
|
||||
myResourceTable = theResourceTable;
|
||||
}
|
||||
|
|
|
@ -169,8 +169,12 @@ public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchP
|
|||
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
b.append("paramName", getParamName());
|
||||
b.append("resourceId", getResourcePid());
|
||||
b.append("lat", getLatitude());
|
||||
b.append("lon", getLongitude());
|
||||
if (isMissing()) {
|
||||
b.append("missing", isMissing());
|
||||
} else {
|
||||
b.append("lat", getLatitude());
|
||||
b.append("lon", getLongitude());
|
||||
}
|
||||
return b.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -257,6 +257,8 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
|||
b.append("resourceId", getResourcePid());
|
||||
b.append("valueLow", new InstantDt(getValueLow()));
|
||||
b.append("valueHigh", new InstantDt(getValueHigh()));
|
||||
b.append("ordLow", myValueLowDateOrdinal);
|
||||
b.append("ordHigh", myValueHighDateOrdinal);
|
||||
b.append("hashIdentity", myHashIdentity);
|
||||
b.append("missing", isMissing());
|
||||
return b.build();
|
||||
|
|
|
@ -246,9 +246,15 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
b.append("resourceType", getResourceType());
|
||||
b.append("paramName", getParamName());
|
||||
b.append("system", getSystem());
|
||||
b.append("value", getValue());
|
||||
if (isMissing()) {
|
||||
b.append("missing", true);
|
||||
} else {
|
||||
b.append("system", getSystem());
|
||||
b.append("value", getValue());
|
||||
}
|
||||
b.append("hashIdentity", myHashIdentity);
|
||||
b.append("hashSystem", myHashSystem);
|
||||
b.append("hashValue", myHashValue);
|
||||
return b.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -36,11 +36,13 @@ import javax.persistence.Id;
|
|||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import javax.persistence.Transient;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
@Entity
|
||||
|
@ -90,6 +92,7 @@ public class ResourceLink extends BaseResourceIndex {
|
|||
@Column(name = "SP_UPDATED", nullable = true) // TODO: make this false after HAPI 2.3
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date myUpdated;
|
||||
|
||||
@Transient
|
||||
private transient String myTargetResourceId;
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue