Instance level reindex operations (#4699)
* Reindex improvements * Work * Work on reindex * Test fixes * Generator now working * Add instance reindex * Work * Build fixes * Build fix * Fixes * Add changelog * Test fixes * Resolve checkstyle fix * Version bump * Address review comments * Resolve imports * Address review comments * Test fix
This commit is contained in:
parent
182ac3b36c
commit
e500b23fe6
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-bom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
<name>HAPI FHIR BOM</name>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-cli</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../hapi-deployable-pom</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: add
|
||||
issue: 4699
|
||||
title: "Two new operations have been added to the JPA server:
|
||||
* Instance level $reindex performs a synchronous reindex of a single resource and returns a Parameters object containing all previous and new indexes
|
||||
* Instance level $reindex-dryrun simulates a reindex and shows the previous and new indexes generated"
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 4699
|
||||
title: "The BundleBuilder utility class did not work with DSTU2 bundles. This has been
|
||||
corrected."
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 4699
|
||||
title: "When updating resource fields targeted by a Combo Non-Unique SearchParameter, previous
|
||||
indexes were not deleted meaning that old search values could still find the resource. This
|
||||
has been corrected."
|
|
@ -11,7 +11,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -96,6 +96,7 @@ import ca.uhn.fhir.jpa.partition.PartitionManagementProvider;
|
|||
import ca.uhn.fhir.jpa.partition.RequestPartitionHelperSvc;
|
||||
import ca.uhn.fhir.jpa.provider.DiffProvider;
|
||||
import ca.uhn.fhir.jpa.provider.ProcessMessageProvider;
|
||||
import ca.uhn.fhir.jpa.provider.InstanceReindexProvider;
|
||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
|
||||
|
@ -138,7 +139,9 @@ import ca.uhn.fhir.jpa.search.cache.DatabaseSearchResultCacheSvcImpl;
|
|||
import ca.uhn.fhir.jpa.search.cache.ISearchCacheSvc;
|
||||
import ca.uhn.fhir.jpa.search.cache.ISearchResultCacheSvc;
|
||||
import ca.uhn.fhir.jpa.search.elastic.IndexNamePrefixLayoutStrategy;
|
||||
import ca.uhn.fhir.jpa.search.reindex.IInstanceReindexService;
|
||||
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
||||
import ca.uhn.fhir.jpa.search.reindex.InstanceReindexServiceImpl;
|
||||
import ca.uhn.fhir.jpa.search.reindex.ResourceReindexer;
|
||||
import ca.uhn.fhir.jpa.search.reindex.ResourceReindexingSvcImpl;
|
||||
import ca.uhn.fhir.jpa.search.warm.CacheWarmingSvcImpl;
|
||||
|
@ -376,6 +379,18 @@ public class JpaConfig {
|
|||
return new ResourceReindexingSvcImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
public IInstanceReindexService instanceReindexService() {
|
||||
return new InstanceReindexServiceImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Lazy
|
||||
public InstanceReindexProvider instanceReindexProvider(IInstanceReindexService theInstanceReindexService) {
|
||||
return new InstanceReindexProvider(theInstanceReindexService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResourceReindexer resourceReindexer(FhirContext theFhirContext) {
|
||||
return new ResourceReindexer(theFhirContext);
|
||||
|
|
|
@ -1013,8 +1013,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
|
||||
failIfPartitionMismatch(theRequest, entity);
|
||||
|
||||
// Extract search params for resource
|
||||
mySearchParamWithInlineReferencesExtractor.populateFromResource(requestPartitionId, newParams, theTransactionDetails, entity, theResource, existingParams, theRequest, thePerformIndexing);
|
||||
|
||||
// Actually persist the ResourceTable and ResourceHistoryTable entities
|
||||
changed = populateResourceIntoEntity(theTransactionDetails, theRequest, theResource, entity, true);
|
||||
|
||||
if (theForceUpdate) {
|
||||
|
@ -1044,11 +1046,11 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
|
||||
} else {
|
||||
|
||||
changed = populateResourceIntoEntity(theTransactionDetails, theRequest, theResource, entity, false);
|
||||
|
||||
entity.setUpdated(theTransactionDetails.getTransactionDate());
|
||||
entity.setIndexStatus(null);
|
||||
|
||||
changed = populateResourceIntoEntity(theTransactionDetails, theRequest, theResource, entity, false);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1105,9 +1107,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
* those by path and not by parameter name.
|
||||
*/
|
||||
if (thePerformIndexing && newParams != null) {
|
||||
Map<String, Boolean> searchParamPresenceMap = getSearchParamPresenceMap(entity, newParams);
|
||||
|
||||
AddRemoveCount presenceCount = mySearchParamPresenceSvc.updatePresence(entity, searchParamPresenceMap);
|
||||
AddRemoveCount presenceCount = mySearchParamPresenceSvc.updatePresence(entity, newParams.mySearchParamPresentEntities);
|
||||
|
||||
// Interceptor broadcast: JPA_PERFTRACE_INFO
|
||||
if (!presenceCount.isEmpty()) {
|
||||
|
@ -1248,22 +1248,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
encodedResource.setEncoding(theEncoding);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private Map<String, Boolean> getSearchParamPresenceMap(ResourceTable entity, ResourceIndexedSearchParams newParams) {
|
||||
Map<String, Boolean> retval = new HashMap<>();
|
||||
|
||||
for (String nextKey : newParams.getPopulatedResourceLinkParameters()) {
|
||||
retval.put(nextKey, Boolean.TRUE);
|
||||
}
|
||||
|
||||
ResourceSearchParams activeSearchParams = mySearchParamRegistry.getActiveSearchParams(entity.getResourceType());
|
||||
activeSearchParams.getReferenceSearchParamNames().forEach(key -> {
|
||||
if (!retval.containsKey(key)) {
|
||||
retval.put(key, Boolean.FALSE);
|
||||
}
|
||||
});
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO eventually consider refactoring this to be part of an interceptor.
|
||||
|
|
|
@ -28,6 +28,7 @@ import ca.uhn.fhir.context.FhirVersionEnum;
|
|||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
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.ReadPartitionIdRequestDetails;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
|
@ -1232,7 +1233,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
//If the resolved fhir model is null, we don't need to run pre-access over or pre-show over it.
|
||||
if (retVal != null) {
|
||||
invokeStoragePreaccessResources(theId, theRequest, retVal);
|
||||
invokeStoragePreAccessResources(theId, theRequest, retVal);
|
||||
retVal = invokeStoragePreShowResources(theRequest, retVal);
|
||||
}
|
||||
|
||||
|
@ -1241,29 +1242,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
|
||||
private T invokeStoragePreShowResources(RequestDetails theRequest, T retVal) {
|
||||
// Interceptor broadcast: STORAGE_PRESHOW_RESOURCES
|
||||
SimplePreResourceShowDetails showDetails = new SimplePreResourceShowDetails(retVal);
|
||||
HookParams params = new HookParams()
|
||||
.add(IPreResourceShowDetails.class, showDetails)
|
||||
.add(RequestDetails.class, theRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequest);
|
||||
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
|
||||
//noinspection unchecked
|
||||
retVal = (T) showDetails.getResource(0);//TODO GGG/JA : getting resource 0 is interesting. We apparently allow null values in the list. Should we?
|
||||
retVal = invokeStoragePreShowResources(myInterceptorBroadcaster, theRequest, retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void invokeStoragePreaccessResources(IIdType theId, RequestDetails theRequest, T theResource) {
|
||||
// Interceptor broadcast: STORAGE_PREACCESS_RESOURCES
|
||||
SimplePreResourceAccessDetails accessDetails = new SimplePreResourceAccessDetails(theResource);
|
||||
HookParams params = new HookParams()
|
||||
.add(IPreResourceAccessDetails.class, accessDetails)
|
||||
.add(RequestDetails.class, theRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequest);
|
||||
CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
|
||||
if (accessDetails.isDontReturnResourceAtIndex(0)) {
|
||||
throw new ResourceNotFoundException(Msg.code(1995) + "Resource " + theId + " is not known");
|
||||
}
|
||||
private void invokeStoragePreAccessResources(IIdType theId, RequestDetails theRequest, T theResource) {
|
||||
invokeStoragePreAccessResources(myInterceptorBroadcaster, theRequest, theId, theResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1954,6 +1938,37 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
myIdHelperService = theIdHelperService;
|
||||
}
|
||||
|
||||
public static <T extends IBaseResource> T invokeStoragePreShowResources(IInterceptorBroadcaster theInterceptorBroadcaster, RequestDetails theRequest, T retVal) {
|
||||
if (CompositeInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PRESHOW_RESOURCES, theInterceptorBroadcaster, theRequest)) {
|
||||
SimplePreResourceShowDetails showDetails = new SimplePreResourceShowDetails(retVal);
|
||||
HookParams params = new HookParams()
|
||||
.add(IPreResourceShowDetails.class, showDetails)
|
||||
.add(RequestDetails.class, theRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequest);
|
||||
CompositeInterceptorBroadcaster.doCallHooks(theInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PRESHOW_RESOURCES, params);
|
||||
//noinspection unchecked
|
||||
retVal = (T) showDetails.getResource(0);//TODO GGG/JA : getting resource 0 is interesting. We apparently allow null values in the list. Should we?
|
||||
return retVal;
|
||||
} else {
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
public static void invokeStoragePreAccessResources(IInterceptorBroadcaster theInterceptorBroadcaster, RequestDetails theRequest, IIdType theId, IBaseResource theResource) {
|
||||
if (CompositeInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES, theInterceptorBroadcaster, theRequest)) {
|
||||
SimplePreResourceAccessDetails accessDetails = new SimplePreResourceAccessDetails(theResource);
|
||||
HookParams params = new HookParams()
|
||||
.add(IPreResourceAccessDetails.class, accessDetails)
|
||||
.add(RequestDetails.class, theRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequest);
|
||||
CompositeInterceptorBroadcaster.doCallHooks(theInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
|
||||
if (accessDetails.isDontReturnResourceAtIndex(0)) {
|
||||
throw new ResourceNotFoundException(Msg.code(1995) + "Resource " + theId + " is not known");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class IdChecker implements IValidatorModule {
|
||||
|
||||
private final ValidationModeEnum myMode;
|
||||
|
|
|
@ -126,8 +126,8 @@ public class DaoSearchParamSynchronizer {
|
|||
}
|
||||
|
||||
|
||||
<T> List<T> subtract(Collection<T> theSubtractFrom, Collection<T> theToSubtract) {
|
||||
assert theSubtractFrom != theToSubtract;
|
||||
public static <T> List<T> subtract(Collection<T> theSubtractFrom, Collection<T> theToSubtract) {
|
||||
assert theSubtractFrom != theToSubtract || (theSubtractFrom.isEmpty());
|
||||
|
||||
if (theSubtractFrom.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
|
|
|
@ -32,6 +32,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique;
|
|||
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamWithInlineReferencesExtractor;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamWithInlineReferencesExtractor;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService;
|
||||
|
@ -95,12 +96,7 @@ public class SearchParamWithInlineReferencesExtractor extends BaseSearchParamWit
|
|||
extractInlineReferences(theRequest, theResource, theTransactionDetails);
|
||||
}
|
||||
|
||||
mySearchParamExtractorService.extractFromResource(theRequestPartitionId, theRequest, theParams, theExistingParams, theEntity, theResource, theTransactionDetails, thePerformIndexing);
|
||||
|
||||
ResourceSearchParams activeSearchParams = mySearchParamRegistry.getActiveSearchParams(theEntity.getResourceType());
|
||||
if (myStorageSettings.getIndexMissingFields() == JpaStorageSettings.IndexEnabledEnum.ENABLED) {
|
||||
theParams.findMissingSearchParams(myPartitionSettings, myStorageSettings, theEntity, activeSearchParams);
|
||||
}
|
||||
mySearchParamExtractorService.extractFromResource(theRequestPartitionId, theRequest, theParams, theExistingParams, theEntity, theResource, theTransactionDetails, thePerformIndexing, ISearchParamExtractor.ALL_PARAMS);
|
||||
|
||||
/*
|
||||
* If the existing resource already has links and those match links we still want, use them instead of removing them and re adding them
|
||||
|
@ -113,12 +109,6 @@ public class SearchParamWithInlineReferencesExtractor extends BaseSearchParamWit
|
|||
}
|
||||
}
|
||||
|
||||
extractComboParameters(theEntity, theParams);
|
||||
}
|
||||
|
||||
private void extractComboParameters(ResourceTable theEntity, ResourceIndexedSearchParams theParams) {
|
||||
mySearchParamExtractorService.extractSearchParamComboUnique(theEntity, theParams);
|
||||
mySearchParamExtractorService.extractSearchParamComboNonUnique(theEntity, theParams);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -169,13 +159,13 @@ public class SearchParamWithInlineReferencesExtractor extends BaseSearchParamWit
|
|||
* String Uniques
|
||||
*/
|
||||
if (myStorageSettings.isUniqueIndexesEnabled()) {
|
||||
for (ResourceIndexedComboStringUnique next : myDaoSearchParamSynchronizer.subtract(theExistingParams.myComboStringUniques, theParams.myComboStringUniques)) {
|
||||
for (ResourceIndexedComboStringUnique next : DaoSearchParamSynchronizer.subtract(theExistingParams.myComboStringUniques, theParams.myComboStringUniques)) {
|
||||
ourLog.debug("Removing unique index: {}", next);
|
||||
myEntityManager.remove(next);
|
||||
theEntity.getParamsComboStringUnique().remove(next);
|
||||
}
|
||||
boolean haveNewStringUniqueParams = false;
|
||||
for (ResourceIndexedComboStringUnique next : myDaoSearchParamSynchronizer.subtract(theParams.myComboStringUniques, theExistingParams.myComboStringUniques)) {
|
||||
for (ResourceIndexedComboStringUnique next : DaoSearchParamSynchronizer.subtract(theParams.myComboStringUniques, theExistingParams.myComboStringUniques)) {
|
||||
if (myStorageSettings.isUniqueIndexesCheckedBeforeSave()) {
|
||||
ResourceIndexedComboStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString());
|
||||
if (existing != null) {
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
import ca.uhn.fhir.jpa.search.reindex.IInstanceReindexService;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class InstanceReindexProvider {
|
||||
|
||||
private final IInstanceReindexService myInstanceReindexService;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public InstanceReindexProvider(@Nonnull IInstanceReindexService theInstanceReindexService) {
|
||||
Validate.notNull(theInstanceReindexService);
|
||||
myInstanceReindexService = theInstanceReindexService;
|
||||
}
|
||||
|
||||
@Operation(name = ProviderConstants.OPERATION_REINDEX_DRYRUN, idempotent = true, global = true)
|
||||
public IBaseParameters reindexInstanceDryRun(
|
||||
@IdParam IIdType theId,
|
||||
@OperationParam(name="code", typeName = "code", min = 0, max = OperationParam.MAX_UNLIMITED) List<IPrimitiveType<String>> theCodes,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
Set<String> codes = null;
|
||||
if (theCodes != null && theCodes.size() > 0) {
|
||||
codes = theCodes
|
||||
.stream()
|
||||
.map(IPrimitiveType::getValueAsString)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
return myInstanceReindexService.reindexDryRun(theRequestDetails, theId, codes);
|
||||
}
|
||||
|
||||
@Operation(name = ProviderConstants.OPERATION_REINDEX, idempotent = false, global = true)
|
||||
public IBaseParameters reindexInstance(
|
||||
@IdParam IIdType theId,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
return myInstanceReindexService.reindex(theRequestDetails, theId);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.jpa.search.reindex;
|
||||
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Set;
|
||||
|
||||
public interface IInstanceReindexService {
|
||||
|
||||
/**
|
||||
* Simulate a reindex and return the details about what would change
|
||||
*/
|
||||
IBaseParameters reindexDryRun(RequestDetails theRequestDetails, IIdType theResourceId, @Nullable Set<String> theParameters);
|
||||
|
||||
/**
|
||||
* Perform a reindex on a single resource and return details about what changed
|
||||
*/
|
||||
IBaseParameters reindex(RequestDetails theRequestDetails, IIdType theResourceId);
|
||||
}
|
|
@ -0,0 +1,616 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.jpa.search.reindex;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
import ca.uhn.fhir.interceptor.model.ReadPartitionIdRequestDetails;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.dao.IJpaStorageResourceParser;
|
||||
import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.partition.BaseRequestPartitionHelperSvc;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService;
|
||||
import ca.uhn.fhir.narrative.CustomThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.rest.server.util.ResourceSearchParams;
|
||||
import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.DecimalType;
|
||||
import org.hl7.fhir.r4.model.InstantType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.hl7.fhir.r4.model.UrlType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.jpa.dao.index.DaoSearchParamSynchronizer.subtract;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static org.apache.commons.collections4.CollectionUtils.intersection;
|
||||
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class InstanceReindexServiceImpl implements IInstanceReindexService {
|
||||
|
||||
private final FhirContext myContextR4 = FhirContext.forR4Cached();
|
||||
@Autowired
|
||||
protected IJpaStorageResourceParser myJpaStorageResourceParser;
|
||||
@Autowired
|
||||
private SearchParamExtractorService mySearchParamExtractorService;
|
||||
@Autowired
|
||||
private BaseRequestPartitionHelperSvc myPartitionHelperSvc;
|
||||
@Autowired
|
||||
private IHapiTransactionService myTransactionService;
|
||||
@Autowired
|
||||
private IInterceptorService myInterceptorService;
|
||||
@Autowired
|
||||
private DaoRegistry myDaoRegistry;
|
||||
@Autowired
|
||||
private VersionCanonicalizer myVersionCanonicalizer;
|
||||
@Autowired
|
||||
private PartitionSettings myPartitionSettings;
|
||||
private final CustomThymeleafNarrativeGenerator myNarrativeGenerator;
|
||||
@Autowired
|
||||
private ISearchParamRegistry mySearchParamRegistry;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public InstanceReindexServiceImpl() {
|
||||
myNarrativeGenerator = new CustomThymeleafNarrativeGenerator("classpath:ca/uhn/fhir/jpa/search/reindex/reindex-outcome-narrative.properties");
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseParameters reindexDryRun(RequestDetails theRequestDetails, IIdType theResourceId, @Nullable Set<String> theParameters) {
|
||||
RequestPartitionId partitionId = determinePartition(theRequestDetails, theResourceId);
|
||||
TransactionDetails transactionDetails = new TransactionDetails();
|
||||
|
||||
Parameters retValCanonical = myTransactionService
|
||||
.withRequest(theRequestDetails)
|
||||
.withTransactionDetails(transactionDetails)
|
||||
.withRequestPartitionId(partitionId)
|
||||
.execute(() -> reindexDryRunInTransaction(theRequestDetails, theResourceId, partitionId, transactionDetails, theParameters));
|
||||
|
||||
return myVersionCanonicalizer.parametersFromCanonical(retValCanonical);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseParameters reindex(RequestDetails theRequestDetails, IIdType theResourceId) {
|
||||
RequestPartitionId partitionId = determinePartition(theRequestDetails, theResourceId);
|
||||
TransactionDetails transactionDetails = new TransactionDetails();
|
||||
|
||||
Parameters retValCanonical = myTransactionService
|
||||
.withRequest(theRequestDetails)
|
||||
.withTransactionDetails(transactionDetails)
|
||||
.withRequestPartitionId(partitionId)
|
||||
.execute(() -> reindexInTransaction(theRequestDetails, theResourceId));
|
||||
|
||||
return myVersionCanonicalizer.parametersFromCanonical(retValCanonical);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Nonnull
|
||||
private Parameters reindexInTransaction(RequestDetails theRequestDetails, IIdType theResourceId) {
|
||||
IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResourceId.getResourceType());
|
||||
ResourceTable entity = (ResourceTable) dao.readEntity(theResourceId, theRequestDetails);
|
||||
IBaseResource resource = myJpaStorageResourceParser.toResource(entity, false);
|
||||
|
||||
// Invoke the pre-access and pre-show interceptors in case there are any security
|
||||
// restrictions or audit requirements around the user accessing this resource
|
||||
BaseHapiFhirResourceDao.invokeStoragePreAccessResources(myInterceptorService, theRequestDetails, theResourceId, resource);
|
||||
BaseHapiFhirResourceDao.invokeStoragePreShowResources(myInterceptorService, theRequestDetails, resource);
|
||||
|
||||
ResourceIndexedSearchParams existingParamsToPopulate = new ResourceIndexedSearchParams(entity);
|
||||
existingParamsToPopulate.mySearchParamPresentEntities.addAll(entity.getSearchParamPresents());
|
||||
|
||||
dao.reindex(resource, entity);
|
||||
|
||||
ResourceIndexedSearchParams newParamsToPopulate = new ResourceIndexedSearchParams(entity);
|
||||
newParamsToPopulate.mySearchParamPresentEntities.addAll(entity.getSearchParamPresents());
|
||||
|
||||
return buildIndexResponse(existingParamsToPopulate, newParamsToPopulate, true);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private Parameters reindexDryRunInTransaction(RequestDetails theRequestDetails, IIdType theResourceId, RequestPartitionId theRequestPartitionId, TransactionDetails theTransactionDetails, Set<String> theParameters) {
|
||||
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(theResourceId.getResourceType());
|
||||
ResourceTable entity = (ResourceTable) dao.readEntity(theResourceId, theRequestDetails);
|
||||
IBaseResource resource = myJpaStorageResourceParser.toResource(entity, false);
|
||||
|
||||
// Invoke the pre-access and pre-show interceptors in case there are any security
|
||||
// restrictions or audit requirements around the user accessing this resource
|
||||
BaseHapiFhirResourceDao.invokeStoragePreAccessResources(myInterceptorService, theRequestDetails, theResourceId, resource);
|
||||
BaseHapiFhirResourceDao.invokeStoragePreShowResources(myInterceptorService, theRequestDetails, resource);
|
||||
|
||||
ISearchParamExtractor.ISearchParamFilter searchParamFilter = ISearchParamExtractor.ALL_PARAMS;
|
||||
if (theParameters != null) {
|
||||
searchParamFilter = params -> params
|
||||
.stream()
|
||||
.filter(t -> theParameters.contains(t.getName()))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
ResourceIndexedSearchParams newParamsToPopulate = new ResourceIndexedSearchParams();
|
||||
mySearchParamExtractorService.extractFromResource(theRequestPartitionId, theRequestDetails, newParamsToPopulate, new ResourceIndexedSearchParams(), entity, resource, theTransactionDetails, false, searchParamFilter);
|
||||
|
||||
ResourceIndexedSearchParams existingParamsToPopulate;
|
||||
boolean showAction;
|
||||
if (theParameters == null) {
|
||||
existingParamsToPopulate = new ResourceIndexedSearchParams(entity);
|
||||
existingParamsToPopulate.mySearchParamPresentEntities.addAll(entity.getSearchParamPresents());
|
||||
fillInParamNames(entity, existingParamsToPopulate.mySearchParamPresentEntities, theResourceId.getResourceType());
|
||||
showAction = true;
|
||||
} else {
|
||||
existingParamsToPopulate = new ResourceIndexedSearchParams();
|
||||
showAction = false;
|
||||
}
|
||||
|
||||
return buildIndexResponse(existingParamsToPopulate, newParamsToPopulate, showAction);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private RequestPartitionId determinePartition(RequestDetails theRequestDetails, IIdType theResourceId) {
|
||||
ReadPartitionIdRequestDetails details = ReadPartitionIdRequestDetails.forRead(theResourceId);
|
||||
return myPartitionHelperSvc.determineReadPartitionForRequest(theRequestDetails, details);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@VisibleForTesting
|
||||
Parameters buildIndexResponse(ResourceIndexedSearchParams theExistingParams, ResourceIndexedSearchParams theNewParams, boolean theShowAction) {
|
||||
Parameters parameters = new Parameters();
|
||||
|
||||
Parameters.ParametersParameterComponent narrativeParameter = parameters.addParameter();
|
||||
narrativeParameter.setName("Narrative");
|
||||
|
||||
// Normal indexes
|
||||
addParamsNonMissing(parameters, "CoordinateIndexes", "Coords", theExistingParams.myCoordsParams, theNewParams.myCoordsParams, new CoordsParamPopulator(), theShowAction);
|
||||
addParamsNonMissing(parameters, "DateIndexes", "Date", theExistingParams.myDateParams, theNewParams.myDateParams, new DateParamPopulator(), theShowAction);
|
||||
addParamsNonMissing(parameters, "NumberIndexes", "Number", theExistingParams.myNumberParams, theNewParams.myNumberParams, new NumberParamPopulator(), theShowAction);
|
||||
addParamsNonMissing(parameters, "QuantityIndexes", "Quantity", theExistingParams.myQuantityParams, theNewParams.myQuantityParams, new QuantityParamPopulator(), theShowAction);
|
||||
addParamsNonMissing(parameters, "QuantityIndexes", "QuantityNormalized", theExistingParams.myQuantityNormalizedParams, theNewParams.myQuantityNormalizedParams, new QuantityNormalizedParamPopulator(), theShowAction);
|
||||
addParamsNonMissing(parameters, "UriIndexes", "Uri", theExistingParams.myUriParams, theNewParams.myUriParams, new UriParamPopulator(), theShowAction);
|
||||
addParamsNonMissing(parameters, "StringIndexes", "String", theExistingParams.myStringParams, theNewParams.myStringParams, new StringParamPopulator(), theShowAction);
|
||||
addParamsNonMissing(parameters, "TokenIndexes", "Token", theExistingParams.myTokenParams, theNewParams.myTokenParams, new TokenParamPopulator(), theShowAction);
|
||||
|
||||
// Resource links
|
||||
addParams(parameters, "ResourceLinks", "Reference", normalizeLinks(theExistingParams.myLinks), normalizeLinks(theNewParams.myLinks), new ResourceLinkPopulator(), theShowAction);
|
||||
|
||||
// Combo search params
|
||||
addParams(parameters, "UniqueIndexes", "ComboStringUnique", theExistingParams.myComboStringUniques, theNewParams.myComboStringUniques, new ComboStringUniquePopulator(), theShowAction);
|
||||
addParams(parameters, "NonUniqueIndexes", "ComboTokenNonUnique", theExistingParams.myComboTokenNonUnique, theNewParams.myComboTokenNonUnique, new ComboTokenNonUniquePopulator(), theShowAction);
|
||||
|
||||
// Missing (:missing) indexes
|
||||
addParamsMissing(parameters, "Coords", theExistingParams.myCoordsParams, theNewParams.myCoordsParams, new MissingIndexParamPopulator<>(), theShowAction);
|
||||
addParamsMissing(parameters, "Date", theExistingParams.myDateParams, theNewParams.myDateParams, new MissingIndexParamPopulator<>(), theShowAction);
|
||||
addParamsMissing(parameters, "Number", theExistingParams.myNumberParams, theNewParams.myNumberParams, new MissingIndexParamPopulator<>(), theShowAction);
|
||||
addParamsMissing(parameters, "Quantity", theExistingParams.myQuantityParams, theNewParams.myQuantityParams, new MissingIndexParamPopulator<>(), theShowAction);
|
||||
addParamsMissing(parameters, "QuantityNormalized", theExistingParams.myQuantityNormalizedParams, theNewParams.myQuantityNormalizedParams, new MissingIndexParamPopulator<>(), theShowAction);
|
||||
addParamsMissing(parameters, "Uri", theExistingParams.myUriParams, theNewParams.myUriParams, new MissingIndexParamPopulator<>(), theShowAction);
|
||||
addParamsMissing(parameters, "String", theExistingParams.myStringParams, theNewParams.myStringParams, new MissingIndexParamPopulator<>(), theShowAction);
|
||||
addParamsMissing(parameters, "Token", theExistingParams.myTokenParams, theNewParams.myTokenParams, new MissingIndexParamPopulator<>(), theShowAction);
|
||||
addParams(parameters, "MissingIndexes", "Reference", theExistingParams.mySearchParamPresentEntities, theNewParams.mySearchParamPresentEntities, new SearchParamPresentParamPopulator(), theShowAction);
|
||||
|
||||
String narrativeText = myNarrativeGenerator.generateResourceNarrative(myContextR4, parameters);
|
||||
narrativeParameter.setValue(new StringType(narrativeText));
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link SearchParamPresentEntity} entity doesn't actually store the parameter names
|
||||
* in the database entity, it only stores a hash. So we brute force possible hashes here
|
||||
* to figure out the associated param names.
|
||||
*/
|
||||
private void fillInParamNames(ResourceTable theEntity, Collection<SearchParamPresentEntity> theTarget, String theResourceName) {
|
||||
Map<Long, String> hashes = new HashMap<>();
|
||||
ResourceSearchParams searchParams = mySearchParamRegistry.getActiveSearchParams(theResourceName);
|
||||
for (RuntimeSearchParam next : searchParams.values()) {
|
||||
hashes.put(SearchParamPresentEntity.calculateHashPresence(myPartitionSettings, theEntity.getPartitionId(), theResourceName, next.getName(), true), next.getName());
|
||||
hashes.put(SearchParamPresentEntity.calculateHashPresence(myPartitionSettings, theEntity.getPartitionId(), theResourceName, next.getName(), false), next.getName());
|
||||
}
|
||||
|
||||
for (SearchParamPresentEntity next : theTarget) {
|
||||
if (next.getParamName() == null) {
|
||||
String name = hashes.get(next.getHashPresence());
|
||||
name = defaultIfBlank(name, "(unknown)");
|
||||
next.setParamName(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum ActionEnum {
|
||||
|
||||
ADD,
|
||||
REMOVE,
|
||||
UNKNOWN,
|
||||
NO_CHANGE
|
||||
|
||||
}
|
||||
|
||||
private static abstract class BaseParamPopulator<T> {
|
||||
|
||||
|
||||
@Nonnull
|
||||
public Parameters.ParametersParameterComponent addIndexValue(ActionEnum theAction, Parameters.ParametersParameterComponent theParent, T theParam, String theParamTypeName) {
|
||||
Parameters.ParametersParameterComponent retVal = theParent
|
||||
.addPart()
|
||||
.setName(toPartName(theParam));
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("Action")
|
||||
.setValue(new CodeType(theAction.name()));
|
||||
if (theParamTypeName != null) {
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("Type")
|
||||
.setValue(new CodeType(theParamTypeName));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
protected abstract String toPartName(T theParam);
|
||||
|
||||
public void sort(List<T> theParams) {
|
||||
theParams.sort(comparing(this::toPartName));
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class BaseIndexParamPopulator<T extends BaseResourceIndexedSearchParam> extends BaseParamPopulator<T> {
|
||||
@Override
|
||||
protected String toPartName(T theParam) {
|
||||
return theParam.getParamName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ComboStringUniquePopulator extends BaseParamPopulator<ResourceIndexedComboStringUnique> {
|
||||
@Override
|
||||
protected String toPartName(ResourceIndexedComboStringUnique theParam) {
|
||||
return theParam.getIndexString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ComboTokenNonUniquePopulator extends BaseParamPopulator<ResourceIndexedComboTokenNonUnique> {
|
||||
@Override
|
||||
protected String toPartName(ResourceIndexedComboTokenNonUnique theParam) {
|
||||
return theParam.getIndexString();
|
||||
}
|
||||
}
|
||||
|
||||
private static class CoordsParamPopulator extends BaseIndexParamPopulator<ResourceIndexedSearchParamCoords> {
|
||||
@Nonnull
|
||||
@Override
|
||||
public Parameters.ParametersParameterComponent addIndexValue(ActionEnum theAction, Parameters.ParametersParameterComponent theParent, ResourceIndexedSearchParamCoords theParam, String theParamTypeName) {
|
||||
Parameters.ParametersParameterComponent retVal = super.addIndexValue(theAction, theParent, theParam, theParamTypeName);
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("Latitude")
|
||||
.setValue(new DecimalType(theParam.getLatitude()));
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("Longitude")
|
||||
.setValue(new DecimalType(theParam.getLongitude()));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static class DateParamPopulator extends BaseIndexParamPopulator<ResourceIndexedSearchParamDate> {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Parameters.ParametersParameterComponent addIndexValue(ActionEnum theAction, Parameters.ParametersParameterComponent theParent, ResourceIndexedSearchParamDate theParam, String theParamTypeName) {
|
||||
Parameters.ParametersParameterComponent retVal = super.addIndexValue(theAction, theParent, theParam, theParamTypeName);
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("High")
|
||||
.setValue(new InstantType(theParam.getValueHigh()));
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("Low")
|
||||
.setValue(new InstantType(theParam.getValueLow()));
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MissingIndexParamPopulator<T extends BaseResourceIndexedSearchParam> extends BaseIndexParamPopulator<T> {
|
||||
@Nonnull
|
||||
@Override
|
||||
public Parameters.ParametersParameterComponent addIndexValue(ActionEnum theAction, Parameters.ParametersParameterComponent theParent, T theParam, String theParamTypeName) {
|
||||
Parameters.ParametersParameterComponent retVal = super.addIndexValue(theAction, theParent, theParam, theParamTypeName);
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("Missing")
|
||||
.setValue(new BooleanType(theParam.isMissing()));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static class NumberParamPopulator extends BaseIndexParamPopulator<ResourceIndexedSearchParamNumber> {
|
||||
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Parameters.ParametersParameterComponent addIndexValue(ActionEnum theAction, Parameters.ParametersParameterComponent theParent, ResourceIndexedSearchParamNumber theParam, String theParamTypeName) {
|
||||
Parameters.ParametersParameterComponent retVal = super.addIndexValue(theAction, theParent, theParam, theParamTypeName);
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("Value")
|
||||
.setValue(new DecimalType(theParam.getValue()));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class QuantityParamPopulator extends BaseIndexParamPopulator<ResourceIndexedSearchParamQuantity> {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Parameters.ParametersParameterComponent addIndexValue(ActionEnum theAction, Parameters.ParametersParameterComponent theParent, ResourceIndexedSearchParamQuantity theParam, String theParamTypeName) {
|
||||
Parameters.ParametersParameterComponent retVal = super.addIndexValue(theAction, theParent, theParam, theParamTypeName);
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("Value")
|
||||
.setValue(new DecimalType(theParam.getValue()));
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("System")
|
||||
.setValue(new UriType(theParam.getSystem()));
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("Units")
|
||||
.setValue(new CodeType(theParam.getUnits()));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class QuantityNormalizedParamPopulator extends BaseIndexParamPopulator<ResourceIndexedSearchParamQuantityNormalized> {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Parameters.ParametersParameterComponent addIndexValue(ActionEnum theAction, Parameters.ParametersParameterComponent theParent, ResourceIndexedSearchParamQuantityNormalized theParam, String theParamTypeName) {
|
||||
Parameters.ParametersParameterComponent retVal = super.addIndexValue(theAction, theParent, theParam, theParamTypeName);
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("Value")
|
||||
.setValue(new DecimalType(theParam.getValue()));
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("System")
|
||||
.setValue(new UriType(theParam.getSystem()));
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("Units")
|
||||
.setValue(new CodeType(theParam.getUnits()));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ResourceLinkPopulator extends BaseParamPopulator<ResourceLink> {
|
||||
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Parameters.ParametersParameterComponent addIndexValue(ActionEnum theAction, Parameters.ParametersParameterComponent theParent, ResourceLink theParam, String theParamTypeName) {
|
||||
Parameters.ParametersParameterComponent retVal = super.addIndexValue(theAction, theParent, theParam, theParamTypeName);
|
||||
if (theParam.getTargetResourceId() != null) {
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("TargetId")
|
||||
.setValue(new StringType(theParam.getTargetResourceType() + "/" + theParam.getTargetResourceId()));
|
||||
} else if (theParam.getTargetResourceUrl() != null) {
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("TargetUrl")
|
||||
.setValue(new UrlType(theParam.getTargetResourceUrl()));
|
||||
}
|
||||
|
||||
if (theParam.getTargetResourceVersion() != null) {
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("TargetVersion")
|
||||
.setValue(new StringType(theParam.getTargetResourceVersion().toString()));
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toPartName(ResourceLink theParam) {
|
||||
return theParam.getSourcePath();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SearchParamPresentParamPopulator extends BaseParamPopulator<SearchParamPresentEntity> {
|
||||
@Nonnull
|
||||
@Override
|
||||
public Parameters.ParametersParameterComponent addIndexValue(ActionEnum theAction, Parameters.ParametersParameterComponent theParent, SearchParamPresentEntity theParam, String theParamTypeName) {
|
||||
Parameters.ParametersParameterComponent retVal = super.addIndexValue(theAction, theParent, theParam, theParamTypeName);
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("Missing")
|
||||
.setValue(new BooleanType(!theParam.isPresent()));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toPartName(SearchParamPresentEntity theParam) {
|
||||
return theParam.getParamName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class StringParamPopulator extends BaseIndexParamPopulator<ResourceIndexedSearchParamString> {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Parameters.ParametersParameterComponent addIndexValue(ActionEnum theAction, Parameters.ParametersParameterComponent theParent, ResourceIndexedSearchParamString theParam, String theParamTypeName) {
|
||||
Parameters.ParametersParameterComponent retVal = super.addIndexValue(theAction, theParent, theParam, theParamTypeName);
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("ValueNormalized")
|
||||
.setValue(new StringType(theParam.getValueNormalized()));
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("ValueExact")
|
||||
.setValue(new StringType(theParam.getValueExact()));
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TokenParamPopulator extends BaseIndexParamPopulator<ResourceIndexedSearchParamToken> {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Parameters.ParametersParameterComponent addIndexValue(ActionEnum theAction, Parameters.ParametersParameterComponent theParent, ResourceIndexedSearchParamToken theParam, String theParamTypeName) {
|
||||
Parameters.ParametersParameterComponent retVal = super.addIndexValue(theAction, theParent, theParam, theParamTypeName);
|
||||
if (isNotBlank(theParam.getSystem())) {
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("System")
|
||||
.setValue(new StringType(theParam.getSystem()));
|
||||
}
|
||||
if (isNotBlank(theParam.getValue())) {
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("Value")
|
||||
.setValue(new StringType(theParam.getValue()));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
private static class UriParamPopulator extends BaseIndexParamPopulator<ResourceIndexedSearchParamUri> {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Parameters.ParametersParameterComponent addIndexValue(ActionEnum theAction, Parameters.ParametersParameterComponent theParent, ResourceIndexedSearchParamUri theParam, String theParamTypeName) {
|
||||
Parameters.ParametersParameterComponent retVal = super.addIndexValue(theAction, theParent, theParam, theParamTypeName);
|
||||
retVal
|
||||
.addPart()
|
||||
.setName("Value")
|
||||
.setValue(new UriType(theParam.getUri()));
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Links loaded from the database have a PID link to their target, but the ones
|
||||
* extracted from the resource in memory won't have the PID. So this method
|
||||
* strips the PIDs so that the generated hashCodes and equals comparisons
|
||||
* will actually be equal.
|
||||
*/
|
||||
private static List<ResourceLink> normalizeLinks(Collection<ResourceLink> theLinks) {
|
||||
return theLinks
|
||||
.stream()
|
||||
.map(ResourceLink::cloneWithoutTargetPid)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static <T> void addParams(Parameters theParameters, String theSectionName, String theTypeName, Collection<T> theExistingParams, Collection<T> theNewParams, BaseParamPopulator<T> thePopulator, boolean theShowAction) {
|
||||
List<T> addedParams = subtract(theNewParams, theExistingParams);
|
||||
thePopulator.sort(addedParams);
|
||||
for (T next : addedParams) {
|
||||
Parameters.ParametersParameterComponent parent = getOrCreateSection(theParameters, theSectionName);
|
||||
if (theShowAction) {
|
||||
thePopulator.addIndexValue(ActionEnum.ADD, parent, next, theTypeName);
|
||||
} else {
|
||||
thePopulator.addIndexValue(ActionEnum.UNKNOWN, parent, next, theTypeName);
|
||||
}
|
||||
}
|
||||
|
||||
List<T> removedParams = subtract(theExistingParams, theNewParams);
|
||||
addedParams.sort(comparing(thePopulator::toPartName));
|
||||
for (T next : removedParams) {
|
||||
Parameters.ParametersParameterComponent parent = getOrCreateSection(theParameters, theSectionName);
|
||||
thePopulator.addIndexValue(ActionEnum.REMOVE, parent, next, theTypeName);
|
||||
}
|
||||
|
||||
List<T> unchangedParams = new ArrayList<>(intersection(theNewParams, theExistingParams));
|
||||
addedParams.sort(comparing(thePopulator::toPartName));
|
||||
for (T next : unchangedParams) {
|
||||
Parameters.ParametersParameterComponent parent = getOrCreateSection(theParameters, theSectionName);
|
||||
thePopulator.addIndexValue(ActionEnum.NO_CHANGE, parent, next, theTypeName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static <T extends BaseResourceIndexedSearchParam> void addParamsNonMissing(Parameters theParameters, String theSectionName, String theTypeName, Collection<T> theExistingParams, Collection<T> theNewParams, BaseParamPopulator<T> thePopulator, boolean theShowAction) {
|
||||
Collection<T> existingParams = filterWantMissing(theExistingParams, false);
|
||||
Collection<T> newParams = filterWantMissing(theNewParams, false);
|
||||
addParams(theParameters, theSectionName, theTypeName, existingParams, newParams, thePopulator, theShowAction);
|
||||
}
|
||||
|
||||
private static <T extends BaseResourceIndexedSearchParam> void addParamsMissing(Parameters theParameters, String theTypeName, Collection<T> theExistingParams, Collection<T> theNewParams, BaseParamPopulator<T> thePopulator, boolean theShowAction) {
|
||||
Collection<T> existingParams = filterWantMissing(theExistingParams, true);
|
||||
Collection<T> newParams = filterWantMissing(theNewParams, true);
|
||||
addParams(theParameters, "MissingIndexes", theTypeName, existingParams, newParams, thePopulator, theShowAction);
|
||||
}
|
||||
|
||||
private static <T extends BaseResourceIndexedSearchParam> Collection<T> filterWantMissing(Collection<T> theNewParams, boolean theWantMissing) {
|
||||
return theNewParams
|
||||
.stream()
|
||||
.filter(t -> t.isMissing() == theWantMissing)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static Parameters.ParametersParameterComponent getOrCreateSection(Parameters theParameters, String theSectionName) {
|
||||
Parameters.ParametersParameterComponent parent = theParameters.getParameter(theSectionName);
|
||||
if (parent == null) {
|
||||
parent = theParameters.addParameter();
|
||||
parent.setName(theSectionName);
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -20,12 +20,13 @@
|
|||
package ca.uhn.fhir.jpa.sp;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.model.entity.SearchParamPresentEntity;
|
||||
import ca.uhn.fhir.jpa.util.AddRemoveCount;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
|
||||
public interface ISearchParamPresenceSvc {
|
||||
|
||||
AddRemoveCount updatePresence(ResourceTable theResource, Map<String, Boolean> theParamNameToPresence);
|
||||
AddRemoveCount updatePresence(ResourceTable theResource, Collection<SearchParamPresentEntity> thePresenceEntities);
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.sp;
|
|||
|
||||
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchParamPresentDao;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.model.entity.SearchParamPresentEntity;
|
||||
import ca.uhn.fhir.jpa.util.AddRemoveCount;
|
||||
|
@ -42,8 +41,6 @@ public class SearchParamPresenceSvcImpl implements ISearchParamPresenceSvc {
|
|||
@Autowired
|
||||
private ISearchParamPresentDao mySearchParamPresentDao;
|
||||
|
||||
@Autowired
|
||||
private PartitionSettings myPartitionSettings;
|
||||
@Autowired
|
||||
private JpaStorageSettings myStorageSettings;
|
||||
|
||||
|
@ -53,14 +50,12 @@ public class SearchParamPresenceSvcImpl implements ISearchParamPresenceSvc {
|
|||
}
|
||||
|
||||
@Override
|
||||
public AddRemoveCount updatePresence(ResourceTable theResource, Map<String, Boolean> theParamNameToPresence) {
|
||||
public AddRemoveCount updatePresence(ResourceTable theResource, Collection<SearchParamPresentEntity> thePresenceEntities) {
|
||||
AddRemoveCount retVal = new AddRemoveCount();
|
||||
if (myStorageSettings.getIndexMissingFields() == JpaStorageSettings.IndexEnabledEnum.DISABLED) {
|
||||
return retVal;
|
||||
}
|
||||
|
||||
Map<String, Boolean> presenceMap = new HashMap<>(theParamNameToPresence);
|
||||
|
||||
// Find existing entries
|
||||
Collection<SearchParamPresentEntity> existing = theResource.getSearchParamPresents();
|
||||
Map<Long, SearchParamPresentEntity> existingHashToPresence = new HashMap<>();
|
||||
|
@ -70,18 +65,8 @@ public class SearchParamPresenceSvcImpl implements ISearchParamPresenceSvc {
|
|||
|
||||
// Find newly wanted set of entries
|
||||
Map<Long, SearchParamPresentEntity> newHashToPresence = new HashMap<>();
|
||||
for (Entry<String, Boolean> next : presenceMap.entrySet()) {
|
||||
String paramName = next.getKey();
|
||||
|
||||
SearchParamPresentEntity present = new SearchParamPresentEntity();
|
||||
present.setPartitionSettings(myPartitionSettings);
|
||||
present.setResource(theResource);
|
||||
present.setParamName(paramName);
|
||||
present.setPresent(next.getValue());
|
||||
present.setPartitionId(theResource.getPartitionId());
|
||||
present.calculateHashes();
|
||||
|
||||
newHashToPresence.put(present.getHashPresence(), present);
|
||||
for (SearchParamPresentEntity next : thePresenceEntities) {
|
||||
newHashToPresence.put(next.getHashPresence(), next);
|
||||
}
|
||||
|
||||
// Delete any that should be deleted
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<div xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<!--/*
|
||||
Each index will be in a Patameters.part instance that looks like the following:
|
||||
|
||||
"part": [ {
|
||||
"name": "uri",
|
||||
"part": [ {
|
||||
"name": "Action",
|
||||
"valueCode": "ADD"
|
||||
}, {
|
||||
"name": "Type",
|
||||
"valueCode": "Uri"
|
||||
}, {
|
||||
"name": "Value",
|
||||
"valueUri": "http://some-codesystem"
|
||||
}
|
||||
]
|
||||
|
||||
This fragment takes the containing part (via thePart) and the name of the
|
||||
child part (via theName) and outputs the primitive value of the child part's
|
||||
value object. So, given the example above, for name="Type" this would
|
||||
output "Uri".
|
||||
*/-->
|
||||
<th:block th:fragment="renderPart (thePart, theName)">
|
||||
<th:block th:each="childPart : ${thePart.getPart()}">
|
||||
<th:block th:if="${childPart.name == theName}" th:text="${childPart.value.getValueAsString()}"/>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
||||
</div>
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
parameters.resourceType = Parameters
|
||||
parameters.style = thymeleaf
|
||||
parameters.narrative = classpath:ca/uhn/fhir/jpa/search/reindex/reindex-outcome.html
|
||||
|
||||
parameters-fragments.fragmentName=ParametersFragments
|
||||
parameters-fragments.narrative=classpath:ca/uhn/fhir/jpa/search/reindex/fragments.html
|
|
@ -0,0 +1,219 @@
|
|||
<div xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<!--/* Number Indexes */-->
|
||||
<div th:if="${resource.hasParameter('NumberIndexes')}" id="NumberIndexes">
|
||||
<h1>Number Indexes</h1>
|
||||
<table id="NumberIndexesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Action</th>
|
||||
<th>Type</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="part : ${resource.getParameter('NumberIndexes').part}">
|
||||
<td th:text="${part.name}"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Action')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Type')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Value')"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!--/* Quantity Indexes */-->
|
||||
<div th:if="${resource.hasParameter('QuantityIndexes')}" id="QuantityIndexes">
|
||||
<h1>Quantity Indexes</h1>
|
||||
<table id="QuantityIndexesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Action</th>
|
||||
<th>Type</th>
|
||||
<th>Value</th>
|
||||
<th>System</th>
|
||||
<th>Units</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="part : ${resource.getParameter('QuantityIndexes').part}">
|
||||
<td th:text="${part.name}"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Action')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Type')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Value')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'System')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Units')"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!--/* String Indexes */-->
|
||||
<div th:if="${resource.hasParameter('StringIndexes')}" id="StringIndexes">
|
||||
<h1>String Indexes</h1>
|
||||
<table id="StringIndexesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Action</th>
|
||||
<th>Type</th>
|
||||
<th>ValueNormalized</th>
|
||||
<th>ValueExact</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="part : ${resource.getParameter('StringIndexes').part}">
|
||||
<td th:text="${part.name}"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Action')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Type')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'ValueNormalized')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'ValueExact')"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!--/* Token Indexes */-->
|
||||
<div th:if="${resource.hasParameter('TokenIndexes')}" id="TokenIndexes">
|
||||
<h1>Token Indexes</h1>
|
||||
<table id="TokenIndexesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Action</th>
|
||||
<th>Type</th>
|
||||
<th>System</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="part : ${resource.getParameter('TokenIndexes').part}">
|
||||
<td th:text="${part.name}"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Action')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Type')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'System')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Value')"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!--/* URI Indexes */-->
|
||||
<div th:if="${resource.hasParameter('UriIndexes')}" id="UriIndexes">
|
||||
<h1>URI Indexes</h1>
|
||||
<table id="UriIndexesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Action</th>
|
||||
<th>Type</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="part : ${resource.getParameter('UriIndexes').part}">
|
||||
<td th:text="${part.name}"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Action')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Type')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Value')"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!--/* Resource Links */-->
|
||||
<div th:if="${resource.hasParameter('ResourceLinks')}" id="ResourceLinks">
|
||||
<h1>Resource Links</h1>
|
||||
<table id="ResourceLinksTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Action</th>
|
||||
<th>Type</th>
|
||||
<th>Target ID (Local)</th>
|
||||
<th>Target Version (Local)</th>
|
||||
<th>Target URL (Non-Local)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="part : ${resource.getParameter('ResourceLinks').part}">
|
||||
<td th:text="${part.name}"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Action')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Type')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'TargetId')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'TargetVersion')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'TargetUrl')"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!--/* Missing Indexes */-->
|
||||
<div th:if="${resource.hasParameter('MissingIndexes')}" id="MissingIndexes">
|
||||
<h1>Missing Indexes (:missing)</h1>
|
||||
<table id="MissingIndexesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Action</th>
|
||||
<th>Type</th>
|
||||
<th>Missing</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="part : ${resource.getParameter('MissingIndexes').part}">
|
||||
<td th:text="${part.name}"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Action')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Type')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Missing')"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!--/* Combo Non-Unique Indexes */-->
|
||||
<div th:if="${resource.hasParameter('NonUniqueIndexes')}" id="NonUniqueIndexes">
|
||||
<h1>Combo Indexes (Non-Unique)</h1>
|
||||
<table id="NonUniqueIndexesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Action</th>
|
||||
<th>Type</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="part : ${resource.getParameter('NonUniqueIndexes').part}">
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Action')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Type')"></td>
|
||||
<td th:text="${part.name}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!--/* Combo Unique Indexes */-->
|
||||
<div th:if="${resource.hasParameter('UniqueIndexes')}" id="UniqueIndexes">
|
||||
<h1>Combo Indexes (Unique)</h1>
|
||||
<table id="UniqueIndexesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Action</th>
|
||||
<th>Type</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="part : ${resource.getParameter('UniqueIndexes').part}">
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Action')"></td>
|
||||
<td th:insert="ParametersFragments :: renderPart(${part}, 'Type')"></td>
|
||||
<td th:text="${part.name}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package ca.uhn.fhir.jpa.provider;
|
||||
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.jpa.search.reindex.IInstanceReindexService;
|
||||
import ca.uhn.fhir.test.utilities.server.HashMapResourceProviderExtension;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static ca.uhn.fhir.rest.server.provider.ProviderConstants.OPERATION_REINDEX_DRYRUN;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class InstanceReindexProviderTest {
|
||||
|
||||
@Mock
|
||||
private IInstanceReindexService myDryRunService;
|
||||
@RegisterExtension
|
||||
@Order(0)
|
||||
private RestfulServerExtension myServer = new RestfulServerExtension(FhirVersionEnum.R4)
|
||||
.withServer(server -> server.registerProvider(new InstanceReindexProvider(myDryRunService)));
|
||||
@RegisterExtension
|
||||
@Order(1)
|
||||
private HashMapResourceProviderExtension<Patient> myPatientProvider = new HashMapResourceProviderExtension<>(myServer, Patient.class);
|
||||
@Captor
|
||||
private ArgumentCaptor<Set<String>> myCodeCaptor;
|
||||
|
||||
@Test
|
||||
public void testDryRun() {
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter("foo", "bar");
|
||||
when(myDryRunService.reindexDryRun(any(), any(), any())).thenReturn(parameters);
|
||||
|
||||
Parameters outcome = myServer
|
||||
.getFhirClient()
|
||||
.operation()
|
||||
.onInstance(new IdType("Patient/123"))
|
||||
.named(OPERATION_REINDEX_DRYRUN)
|
||||
.withNoParameters(Parameters.class)
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
assertEquals("foo", outcome.getParameter().get(0).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDryRun_WithCodes() {
|
||||
Parameters parameters = new Parameters();
|
||||
parameters.addParameter("foo", "bar");
|
||||
when(myDryRunService.reindexDryRun(any(), any(), any())).thenReturn(parameters);
|
||||
|
||||
Parameters outcome = myServer
|
||||
.getFhirClient()
|
||||
.operation()
|
||||
.onInstance(new IdType("Patient/123"))
|
||||
.named(OPERATION_REINDEX_DRYRUN)
|
||||
.withParameter(Parameters.class, "code", new CodeType("blah"))
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
assertEquals("foo", outcome.getParameter().get(0).getName());
|
||||
|
||||
verify(myDryRunService, times(1)).reindexDryRun(any(), any(), myCodeCaptor.capture());
|
||||
|
||||
assertThat(myCodeCaptor.getValue(), contains("blah"));
|
||||
}
|
||||
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -110,15 +110,17 @@ public class ResourceIndexedComboTokenNonUnique extends BaseResourceIndex implem
|
|||
|
||||
ResourceIndexedComboTokenNonUnique that = (ResourceIndexedComboTokenNonUnique) theO;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(myResource, that.myResource)
|
||||
.append(myHashComplete, that.myHashComplete)
|
||||
.isEquals();
|
||||
EqualsBuilder b = new EqualsBuilder();
|
||||
b.append(myIndexString, that.myIndexString);
|
||||
return b.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BaseResourceIndex> void copyMutableValuesFrom(T theSource) {
|
||||
throw new IllegalStateException(Msg.code(1528));
|
||||
ResourceIndexedComboTokenNonUnique source = (ResourceIndexedComboTokenNonUnique) theSource;
|
||||
myPartitionSettings = source.myPartitionSettings;
|
||||
myHashComplete = source.myHashComplete;
|
||||
myIndexString = source.myIndexString;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -151,8 +153,7 @@ public class ResourceIndexedComboTokenNonUnique extends BaseResourceIndex implem
|
|||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(myResource)
|
||||
.append(myHashComplete)
|
||||
.append(myIndexString)
|
||||
.toHashCode();
|
||||
}
|
||||
|
||||
|
@ -164,6 +165,7 @@ public class ResourceIndexedComboTokenNonUnique extends BaseResourceIndex implem
|
|||
myPartitionSettings = thePartitionSettings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceTable getResource() {
|
||||
return myResource;
|
||||
}
|
||||
|
|
|
@ -128,11 +128,18 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
|
|||
b.append(getResourceType(), obj.getResourceType());
|
||||
b.append(getParamName(), obj.getParamName());
|
||||
b.append(getHashIdentity(), obj.getHashIdentity());
|
||||
b.append(getValue(), obj.getValue());
|
||||
b.append(normalizeForEqualityComparison(getValue()), normalizeForEqualityComparison(obj.getValue()));
|
||||
b.append(isMissing(), obj.isMissing());
|
||||
return b.isEquals();
|
||||
}
|
||||
|
||||
private Double normalizeForEqualityComparison(BigDecimal theValue) {
|
||||
if (theValue == null) {
|
||||
return null;
|
||||
}
|
||||
return theValue.doubleValue();
|
||||
}
|
||||
|
||||
public void setHashIdentity(Long theHashIdentity) {
|
||||
myHashIdentity = theHashIdentity;
|
||||
}
|
||||
|
@ -161,7 +168,7 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
|
|||
b.append(getResourceType());
|
||||
b.append(getParamName());
|
||||
b.append(getHashIdentity());
|
||||
b.append(getValue());
|
||||
b.append(normalizeForEqualityComparison(getValue()));
|
||||
b.append(isMissing());
|
||||
return b.toHashCode();
|
||||
}
|
||||
|
|
|
@ -131,6 +131,18 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
calculateHashes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResourceIndexedSearchParamToken(PartitionSettings thePartitionSettings, String theResourceType, String theParamName, boolean theMissing) {
|
||||
super();
|
||||
setPartitionSettings(thePartitionSettings);
|
||||
setResourceType(theResourceType);
|
||||
setParamName(theParamName);
|
||||
setMissing(theMissing);
|
||||
calculateHashes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BaseResourceIndex> void copyMutableValuesFrom(T theSource) {
|
||||
super.copyMutableValuesFrom(theSource);
|
||||
|
|
|
@ -294,6 +294,28 @@ public class ResourceLink extends BaseResourceIndex {
|
|||
return myTargetResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a clone of this resourcelink which doesn't contain the internal PID
|
||||
* of the target resource.
|
||||
*/
|
||||
public ResourceLink cloneWithoutTargetPid() {
|
||||
ResourceLink retVal = new ResourceLink();
|
||||
retVal.mySourceResource = mySourceResource;
|
||||
retVal.mySourceResourcePid = mySourceResource.getId();
|
||||
retVal.mySourceResourceType = mySourceResource.getResourceType();
|
||||
retVal.mySourcePath = mySourcePath;
|
||||
retVal.myUpdated = myUpdated;
|
||||
retVal.myTargetResourceType = myTargetResourceType;
|
||||
if (myTargetResourceId != null) {
|
||||
retVal.myTargetResourceId = myTargetResourceId;
|
||||
} else if (myTargetResource != null) {
|
||||
retVal.myTargetResourceId = myTargetResource.getIdDt().getIdPart();
|
||||
}
|
||||
retVal.myTargetResourceUrl = myTargetResourceUrl;
|
||||
retVal.myTargetResourceVersion = myTargetResourceVersion;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static ResourceLink forAbsoluteReference(String theSourcePath, ResourceTable theSourceResource, IIdType theTargetResourceUrl, Date theUpdated) {
|
||||
ResourceLink retVal = new ResourceLink();
|
||||
retVal.setSourcePath(theSourcePath);
|
||||
|
|
|
@ -598,7 +598,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
|
|||
}
|
||||
|
||||
public void setParamsComboTokensNonUniquePresent(boolean theParamsComboTokensNonUniquePresent) {
|
||||
myParamsComboStringUniquePresent = theParamsComboTokensNonUniquePresent;
|
||||
myParamsComboTokensNonUniquePresent = theParamsComboTokensNonUniquePresent;
|
||||
}
|
||||
|
||||
public boolean isParamsCoordsPopulated() {
|
||||
|
|
|
@ -21,6 +21,9 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
|
@ -63,6 +66,14 @@ public class SearchParamPresentEntity extends BasePartitionable implements Seria
|
|||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public SearchParamPresentEntity(String theParamName, boolean thePresent) {
|
||||
myParamName = theParamName;
|
||||
myPresent = thePresent;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@PrePersist
|
||||
public void calculateHashes() {
|
||||
|
@ -75,6 +86,7 @@ public class SearchParamPresentEntity extends BasePartitionable implements Seria
|
|||
}
|
||||
|
||||
public Long getHashPresence() {
|
||||
Validate.notNull(myHashPresence);
|
||||
return myHashPresence;
|
||||
}
|
||||
|
||||
|
@ -106,6 +118,26 @@ public class SearchParamPresentEntity extends BasePartitionable implements Seria
|
|||
myPresent = thePresent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object theO) {
|
||||
if (this == theO) return true;
|
||||
|
||||
if (theO == null || getClass() != theO.getClass()) return false;
|
||||
|
||||
SearchParamPresentEntity that = (SearchParamPresentEntity) theO;
|
||||
|
||||
EqualsBuilder b = new EqualsBuilder();
|
||||
b.append(getHashPresence(), that.getHashPresence());
|
||||
return b.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder b = new HashCodeBuilder(17, 37);
|
||||
b.append(getHashPresence());
|
||||
return b.toHashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
|
|
|
@ -116,6 +116,8 @@ public class StorageSettings {
|
|||
private boolean myAllowMdmExpansion = false;
|
||||
private boolean myAutoSupportDefaultSearchParams = true;
|
||||
private boolean myIndexIdentifierOfType = false;
|
||||
private IndexEnabledEnum myIndexMissingFieldsEnabled = IndexEnabledEnum.DISABLED;
|
||||
|
||||
/**
|
||||
* Since 6.4.0
|
||||
*/
|
||||
|
@ -259,6 +261,53 @@ public class StorageSettings {
|
|||
myEnableInMemorySubscriptionMatching = theEnableInMemorySubscriptionMatching;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@link IndexEnabledEnum#DISABLED} (default is {@link IndexEnabledEnum#DISABLED})
|
||||
* the server will not create search indexes for search parameters with no values in resources.
|
||||
* <p>
|
||||
* Disabling this feature means that the <code>:missing</code> search modifier will not be
|
||||
* supported on the server, but also means that storage and indexing (i.e. writes to the
|
||||
* database) may be much faster on servers which have lots of search parameters and need
|
||||
* to write quickly.
|
||||
* </p>
|
||||
* <p>
|
||||
* This feature may be enabled on servers where supporting the use of the :missing parameter is
|
||||
* of higher importance than raw write performance
|
||||
* </p>
|
||||
*/
|
||||
public IndexEnabledEnum getIndexMissingFields() {
|
||||
return myIndexMissingFieldsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@link IndexEnabledEnum#DISABLED} (default is {@link IndexEnabledEnum#DISABLED})
|
||||
* the server will not create search indexes for search parameters with no values in resources.
|
||||
* <p>
|
||||
* Disabling this feature means that the <code>:missing</code> search modifier will not be
|
||||
* supported on the server, but also means that storage and indexing (i.e. writes to the
|
||||
* database) may be much faster on servers which have lots of search parameters and need
|
||||
* to write quickly.
|
||||
* </p>
|
||||
* <p>
|
||||
* This feature may be enabled on servers where supporting the use of the :missing parameter is
|
||||
* of higher importance than raw write performance
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that this setting also has an impact on sorting (i.e. using the
|
||||
* <code>_sort</code> parameter on searches): If the server is configured
|
||||
* to not index missing field.
|
||||
* </p>
|
||||
* <p>
|
||||
* The following index may need to be added into the indexed tables such as <code>HFJ_SPIDX_TOKEN</code>
|
||||
* to improve the search performance while <code>:missing</code> is enabled.
|
||||
* <code>RES_TYPE, SP_NAME, SP_MISSING</code>
|
||||
* </p>
|
||||
*/
|
||||
public void setIndexMissingFields(IndexEnabledEnum theIndexMissingFields) {
|
||||
Validate.notNull(theIndexMissingFields, "theIndexMissingFields must not be null");
|
||||
myIndexMissingFieldsEnabled = theIndexMissingFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is enabled (disabled by default), Mass Ingestion Mode is enabled. In this mode, a number of
|
||||
* runtime checks are disabled. This mode is designed for rapid backloading of data while the system is not
|
||||
|
@ -1220,4 +1269,9 @@ public class StorageSettings {
|
|||
|
||||
}
|
||||
|
||||
public enum IndexEnabledEnum {
|
||||
ENABLED,
|
||||
DISABLED
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -269,9 +269,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
}
|
||||
|
||||
@Override
|
||||
public SearchParamSet<ResourceIndexedSearchParamComposite> extractSearchParamComposites(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
public SearchParamSet<ResourceIndexedSearchParamComposite> extractSearchParamComposites(IBaseResource theResource, ISearchParamFilter theSearchParamFilter) {
|
||||
IExtractor<ResourceIndexedSearchParamComposite> extractor = createCompositeExtractor(theResource);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.COMPOSITE, false, theParamsToIndex);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.COMPOSITE, false, theSearchParamFilter);
|
||||
}
|
||||
|
||||
private IExtractor<ResourceIndexedSearchParamComposite> createCompositeExtractor(IBaseResource theResource) {
|
||||
|
@ -557,9 +557,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
}
|
||||
|
||||
@Override
|
||||
public SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamTokens(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
public SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamTokens(IBaseResource theResource, ISearchParamFilter theSearchParamFilter) {
|
||||
IExtractor<BaseResourceIndexedSearchParam> extractor = createTokenExtractor(theResource);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.TOKEN, false, theParamsToIndex);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.TOKEN, false, theSearchParamFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -592,10 +592,10 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
}
|
||||
|
||||
@Override
|
||||
public SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamSpecial(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
public SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamSpecial(IBaseResource theResource, ISearchParamFilter theSearchParamFilter) {
|
||||
String resourceTypeName = toRootTypeName(theResource);
|
||||
IExtractor<BaseResourceIndexedSearchParam> extractor = createSpecialExtractor(resourceTypeName);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.SPECIAL, false, theParamsToIndex);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.SPECIAL, false, theSearchParamFilter);
|
||||
}
|
||||
|
||||
private IExtractor<BaseResourceIndexedSearchParam> createSpecialExtractor(String theResourceTypeName) {
|
||||
|
@ -612,9 +612,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
}
|
||||
|
||||
@Override
|
||||
public SearchParamSet<ResourceIndexedSearchParamUri> extractSearchParamUri(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
public SearchParamSet<ResourceIndexedSearchParamUri> extractSearchParamUri(IBaseResource theResource, ISearchParamFilter theSearchParamFilter) {
|
||||
IExtractor<ResourceIndexedSearchParamUri> extractor = createUriExtractor(theResource);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.URI, false, theParamsToIndex);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.URI, false, theSearchParamFilter);
|
||||
}
|
||||
|
||||
private IExtractor<ResourceIndexedSearchParamUri> createUriExtractor(IBaseResource theResource) {
|
||||
|
@ -637,9 +637,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
}
|
||||
|
||||
@Override
|
||||
public SearchParamSet<ResourceIndexedSearchParamDate> extractSearchParamDates(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
public SearchParamSet<ResourceIndexedSearchParamDate> extractSearchParamDates(IBaseResource theResource, ISearchParamFilter theSearchParamFilter) {
|
||||
IExtractor<ResourceIndexedSearchParamDate> extractor = createDateExtractor(theResource);
|
||||
return extractSearchParams(theResource, extractor, DATE, false, theParamsToIndex);
|
||||
return extractSearchParams(theResource, extractor, DATE, false, theSearchParamFilter);
|
||||
}
|
||||
|
||||
private IExtractor<ResourceIndexedSearchParamDate> createDateExtractor(IBaseResource theResource) {
|
||||
|
@ -653,9 +653,9 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
}
|
||||
|
||||
@Override
|
||||
public SearchParamSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
public SearchParamSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(IBaseResource theResource, ISearchParamFilter theSearchParamFilter) {
|
||||
IExtractor<ResourceIndexedSearchParamNumber> extractor = createNumberExtractor(theResource);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.NUMBER, false, theParamsToIndex);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.NUMBER, false, theSearchParamFilter);
|
||||
}
|
||||
|
||||
private IExtractor<ResourceIndexedSearchParamNumber> createNumberExtractor(IBaseResource theResource) {
|
||||
|
@ -688,16 +688,16 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
}
|
||||
|
||||
@Override
|
||||
public SearchParamSet<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
public SearchParamSet<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(IBaseResource theResource, ISearchParamFilter theSearchParamFilter) {
|
||||
IExtractor<ResourceIndexedSearchParamQuantity> extractor = createQuantityUnnormalizedExtractor(theResource);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.QUANTITY, false, theParamsToIndex);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.QUANTITY, false, theSearchParamFilter);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SearchParamSet<ResourceIndexedSearchParamQuantityNormalized> extractSearchParamQuantityNormalized(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
public SearchParamSet<ResourceIndexedSearchParamQuantityNormalized> extractSearchParamQuantityNormalized(IBaseResource theResource, ISearchParamFilter theSearchParamFilter) {
|
||||
IExtractor<ResourceIndexedSearchParamQuantityNormalized> extractor = createQuantityNormalizedExtractor(theResource);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.QUANTITY, false, theParamsToIndex);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.QUANTITY, false, theSearchParamFilter);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@ -767,10 +767,10 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
}
|
||||
|
||||
@Override
|
||||
public SearchParamSet<ResourceIndexedSearchParamString> extractSearchParamStrings(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
public SearchParamSet<ResourceIndexedSearchParamString> extractSearchParamStrings(IBaseResource theResource, ISearchParamFilter theSearchParamFilter) {
|
||||
IExtractor<ResourceIndexedSearchParamString> extractor = createStringExtractor(theResource);
|
||||
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.STRING, false, theParamsToIndex);
|
||||
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.STRING, false, theSearchParamFilter);
|
||||
}
|
||||
|
||||
private IExtractor<ResourceIndexedSearchParamString> createStringExtractor(IBaseResource theResource) {
|
||||
|
@ -1321,22 +1321,22 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
|||
* This is the only way you could actually specify a FhirPath expression for those
|
||||
* prior to 6.2.0 so this isn't a breaking change.
|
||||
*/
|
||||
<T> SearchParamSet<T> extractSearchParams(IBaseResource theResource, IExtractor<T> theExtractor, RestSearchParameterTypeEnum theSearchParamType, boolean theWantLocalReferences, Set<String> theParamsToIndex) {
|
||||
<T> SearchParamSet<T> extractSearchParams(IBaseResource theResource, IExtractor<T> theExtractor, RestSearchParameterTypeEnum theSearchParamType, boolean theWantLocalReferences, ISearchParamFilter theSearchParamFilter) {
|
||||
SearchParamSet<T> retVal = new SearchParamSet<>();
|
||||
|
||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||
|
||||
cleanUpContainedResourceReferences(theResource, theSearchParamType, searchParams);
|
||||
int preFilterSize = searchParams.size();
|
||||
Collection<RuntimeSearchParam> filteredSearchParams = theSearchParamFilter.filterSearchParams(searchParams);
|
||||
assert filteredSearchParams.size() == preFilterSize || searchParams != filteredSearchParams;
|
||||
|
||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||
cleanUpContainedResourceReferences(theResource, theSearchParamType, filteredSearchParams);
|
||||
|
||||
for (RuntimeSearchParam nextSpDef : filteredSearchParams) {
|
||||
if (nextSpDef.getParamType() != theSearchParamType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!theParamsToIndex.equals(ISearchParamExtractor.ALL_PARAMS) && !theParamsToIndex.contains(nextSpDef.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// See the method javadoc for an explanation of this
|
||||
if (startsWith(nextSpDef.getPath(), "Resource.")) {
|
||||
continue;
|
||||
|
|
|
@ -21,78 +21,77 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
|
|||
|
||||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboTokenNonUnique;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface ISearchParamExtractor {
|
||||
|
||||
/**
|
||||
* Constant for the {@literal theParamsToIndex} parameters on this interface
|
||||
* Constant for the {@literal theSearchParamFilter} parameters on this interface
|
||||
* indicating that all search parameters should be indexed.
|
||||
*/
|
||||
Set<String> ALL_PARAMS = Set.of("*");
|
||||
ISearchParamFilter ALL_PARAMS = t -> t;
|
||||
|
||||
/**
|
||||
* Constant for the {@literal theSearchParamFilter} parameters on this interface
|
||||
* indicating that no search parameters should be indexed.
|
||||
*/
|
||||
ISearchParamFilter NO_PARAMS = t -> Collections.emptyList();
|
||||
|
||||
default SearchParamSet<ResourceIndexedSearchParamDate> extractSearchParamDates(IBaseResource theResource) {
|
||||
return extractSearchParamDates(theResource, ALL_PARAMS);
|
||||
}
|
||||
|
||||
SearchParamSet<ResourceIndexedSearchParamDate> extractSearchParamDates(IBaseResource theResource, Set<String> theParamsToIndex);
|
||||
SearchParamSet<ResourceIndexedSearchParamDate> extractSearchParamDates(IBaseResource theResource, ISearchParamFilter theSearchParamFilter);
|
||||
|
||||
default SearchParamSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(IBaseResource theResource) {
|
||||
return extractSearchParamNumber(theResource, ALL_PARAMS);
|
||||
}
|
||||
|
||||
SearchParamSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(IBaseResource theResource, Set<String> theParamsToIndex);
|
||||
SearchParamSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(IBaseResource theResource, ISearchParamFilter theSearchParamFilter);
|
||||
|
||||
default SearchParamSet<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(IBaseResource theResource) {
|
||||
return extractSearchParamQuantity(theResource, ALL_PARAMS);
|
||||
}
|
||||
|
||||
SearchParamSet<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(IBaseResource theResource, Set<String> theParamsToIndex);
|
||||
SearchParamSet<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(IBaseResource theResource, ISearchParamFilter theSearchParamFilter);
|
||||
|
||||
default SearchParamSet<ResourceIndexedSearchParamQuantityNormalized> extractSearchParamQuantityNormalized(IBaseResource theResource) {
|
||||
return extractSearchParamQuantityNormalized(theResource, ALL_PARAMS);
|
||||
}
|
||||
|
||||
SearchParamSet<ResourceIndexedSearchParamQuantityNormalized> extractSearchParamQuantityNormalized(IBaseResource theResource, Set<String> theParamsToIndex);
|
||||
SearchParamSet<ResourceIndexedSearchParamQuantityNormalized> extractSearchParamQuantityNormalized(IBaseResource theResource, ISearchParamFilter theSearchParamFilter);
|
||||
|
||||
default SearchParamSet<ResourceIndexedSearchParamString> extractSearchParamStrings(IBaseResource theResource) {
|
||||
return extractSearchParamStrings(theResource, ALL_PARAMS);
|
||||
}
|
||||
|
||||
SearchParamSet<ResourceIndexedSearchParamString> extractSearchParamStrings(IBaseResource theResource, Set<String> theParamsToIndex);
|
||||
SearchParamSet<ResourceIndexedSearchParamString> extractSearchParamStrings(IBaseResource theResource, ISearchParamFilter theSearchParamFilter);
|
||||
|
||||
default SearchParamSet<ResourceIndexedSearchParamComposite> extractSearchParamComposites(IBaseResource theResource) {
|
||||
return extractSearchParamComposites(theResource, ALL_PARAMS);
|
||||
}
|
||||
|
||||
SearchParamSet<ResourceIndexedSearchParamComposite> extractSearchParamComposites(IBaseResource theResource, Set<String> theParamsToIndex);
|
||||
SearchParamSet<ResourceIndexedSearchParamComposite> extractSearchParamComposites(IBaseResource theResource, ISearchParamFilter theSearchParamFilter);
|
||||
|
||||
default SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamTokens(IBaseResource theResource) {
|
||||
return extractSearchParamTokens(theResource, ALL_PARAMS);
|
||||
}
|
||||
|
||||
SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamTokens(IBaseResource theResource, Set<String> theParamsToIndex);
|
||||
SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamTokens(IBaseResource theResource, ISearchParamFilter theSearchParamFilter);
|
||||
|
||||
SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamTokens(IBaseResource theResource, RuntimeSearchParam theSearchParam);
|
||||
|
||||
SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamSpecial(IBaseResource theResource, Set<String> theParamsToIndex);
|
||||
SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamSpecial(IBaseResource theResource, ISearchParamFilter theSearchParamFilter);
|
||||
|
||||
SearchParamSet<ResourceIndexedComboStringUnique> extractSearchParamComboUnique(String theResourceType, ResourceIndexedSearchParams theParams);
|
||||
|
||||
SearchParamSet<ResourceIndexedComboTokenNonUnique> extractSearchParamComboNonUnique(String theResourceType, ResourceIndexedSearchParams theParams);
|
||||
|
@ -102,7 +101,7 @@ public interface ISearchParamExtractor {
|
|||
}
|
||||
|
||||
|
||||
SearchParamSet<ResourceIndexedSearchParamUri> extractSearchParamUri(IBaseResource theResource, Set<String> theParamsToIndex);
|
||||
SearchParamSet<ResourceIndexedSearchParamUri> extractSearchParamUri(IBaseResource theResource, ISearchParamFilter theSearchParamFilter);
|
||||
|
||||
SearchParamSet<PathAndRef> extractResourceLinks(IBaseResource theResource, boolean theWantLocalReferences);
|
||||
|
||||
|
@ -130,6 +129,20 @@ public interface ISearchParamExtractor {
|
|||
|
||||
String getDisplayTextFromCodeableConcept(IBase theValue);
|
||||
|
||||
@FunctionalInterface
|
||||
interface ISearchParamFilter {
|
||||
|
||||
/**
|
||||
* Given the list of search parameters for indexing, an implementation of this
|
||||
* interface may selectively remove any that it wants to remove (or can add if desired).
|
||||
* <p>
|
||||
* Implementations must not modify the list that is passed in. If changes are
|
||||
* desired, a new list must be created and returned.
|
||||
*/
|
||||
Collection<RuntimeSearchParam> filterSearchParams(Collection<RuntimeSearchParam> theSearchParams);
|
||||
|
||||
}
|
||||
|
||||
class SearchParamSet<T> extends HashSet<T> {
|
||||
|
||||
private List<String> myWarnings;
|
||||
|
|
|
@ -59,6 +59,7 @@ public final class ResourceIndexedSearchParams {
|
|||
final public Collection<ResourceIndexedComboTokenNonUnique> myComboTokenNonUnique = new HashSet<>();
|
||||
final public Collection<ResourceLink> myLinks = new HashSet<>();
|
||||
final public Set<String> myPopulatedResourceLinkParameters = new HashSet<>();
|
||||
final public Collection<SearchParamPresentEntity> mySearchParamPresentEntities = new HashSet<>();
|
||||
final public Collection<ResourceIndexedSearchParamComposite> myCompositeParams = new HashSet<>();
|
||||
|
||||
public ResourceIndexedSearchParams() {
|
||||
|
@ -116,6 +117,7 @@ public final class ResourceIndexedSearchParams {
|
|||
theEntity.setParamsUriPopulated(myUriParams.isEmpty() == false);
|
||||
theEntity.setParamsCoordsPopulated(myCoordsParams.isEmpty() == false);
|
||||
theEntity.setParamsComboStringUniquePresent(myComboStringUniques.isEmpty() == false);
|
||||
theEntity.setParamsComboTokensNonUniquePresent(myComboTokenNonUnique.isEmpty() == false);
|
||||
theEntity.setHasLinks(myLinks.isEmpty() == false);
|
||||
}
|
||||
|
||||
|
|
|
@ -47,9 +47,9 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
|||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.model.entity.SearchParamPresentEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
|
||||
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
|
||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||
|
@ -57,6 +57,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.rest.server.util.ResourceSearchParams;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -70,10 +71,13 @@ import javax.annotation.Nullable;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -94,8 +98,6 @@ public class SearchParamExtractorService {
|
|||
private PartitionSettings myPartitionSettings;
|
||||
@Autowired(required = false)
|
||||
private IResourceLinkResolver myResourceLinkResolver;
|
||||
@Autowired
|
||||
private IRequestPartitionHelperSvc myPartitionHelperSvc;
|
||||
|
||||
@VisibleForTesting
|
||||
public void setSearchParamExtractor(ISearchParamExtractor theSearchParamExtractor) {
|
||||
|
@ -104,7 +106,7 @@ public class SearchParamExtractorService {
|
|||
|
||||
|
||||
public void extractFromResource(RequestPartitionId theRequestPartitionId, RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, TransactionDetails theTransactionDetails, boolean theFailOnInvalidReference) {
|
||||
extractFromResource(theRequestPartitionId, theRequestDetails, theParams, new ResourceIndexedSearchParams(), theEntity, theResource, theTransactionDetails, theFailOnInvalidReference);
|
||||
extractFromResource(theRequestPartitionId, theRequestDetails, theParams, new ResourceIndexedSearchParams(), theEntity, theResource, theTransactionDetails, theFailOnInvalidReference, ISearchParamExtractor.ALL_PARAMS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -113,11 +115,11 @@ public class SearchParamExtractorService {
|
|||
* a given resource type, it extracts the associated indexes and populates
|
||||
* {@literal theParams}.
|
||||
*/
|
||||
public void extractFromResource(RequestPartitionId theRequestPartitionId, RequestDetails theRequestDetails, ResourceIndexedSearchParams theNewParams, ResourceIndexedSearchParams theExistingParams, ResourceTable theEntity, IBaseResource theResource, TransactionDetails theTransactionDetails, boolean theFailOnInvalidReference) {
|
||||
public void extractFromResource(RequestPartitionId theRequestPartitionId, RequestDetails theRequestDetails, ResourceIndexedSearchParams theNewParams, ResourceIndexedSearchParams theExistingParams, ResourceTable theEntity, IBaseResource theResource, TransactionDetails theTransactionDetails, boolean theFailOnInvalidReference, @Nonnull ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
|
||||
|
||||
// All search parameter types except Reference
|
||||
ResourceIndexedSearchParams normalParams = new ResourceIndexedSearchParams();
|
||||
extractSearchIndexParameters(theRequestDetails, normalParams, theResource, ISearchParamExtractor.ALL_PARAMS);
|
||||
extractSearchIndexParameters(theRequestDetails, normalParams, theResource, theSearchParamFilter);
|
||||
mergeParams(normalParams, theNewParams);
|
||||
|
||||
boolean indexOnContainedResources = myStorageSettings.isIndexOnContainedResources();
|
||||
|
@ -146,9 +148,51 @@ public class SearchParamExtractorService {
|
|||
extractResourceLinksForContainedResources(theRequestPartitionId, theNewParams, theEntity, theResource, theTransactionDetails, theFailOnInvalidReference, theRequestDetails);
|
||||
}
|
||||
|
||||
// Missing (:missing) Indexes - These are indexes to satisfy the :missing
|
||||
// modifier
|
||||
if (myStorageSettings.getIndexMissingFields() == StorageSettings.IndexEnabledEnum.ENABLED) {
|
||||
|
||||
// References
|
||||
Map<String, Boolean> presenceMap = getReferenceSearchParamPresenceMap(theEntity, theNewParams);
|
||||
presenceMap.forEach((key, value) -> {
|
||||
SearchParamPresentEntity present = new SearchParamPresentEntity();
|
||||
present.setPartitionSettings(myPartitionSettings);
|
||||
present.setResource(theEntity);
|
||||
present.setParamName(key);
|
||||
present.setPresent(value);
|
||||
present.setPartitionId(theEntity.getPartitionId());
|
||||
present.calculateHashes();
|
||||
theNewParams.mySearchParamPresentEntities.add(present);
|
||||
});
|
||||
|
||||
// Everything else
|
||||
ResourceSearchParams activeSearchParams = mySearchParamRegistry.getActiveSearchParams(theEntity.getResourceType());
|
||||
theNewParams.findMissingSearchParams(myPartitionSettings, myStorageSettings, theEntity, activeSearchParams);
|
||||
}
|
||||
|
||||
extractSearchParamComboUnique(theEntity, theNewParams);
|
||||
|
||||
extractSearchParamComboNonUnique(theEntity, theNewParams);
|
||||
|
||||
theNewParams.setUpdatedTime(theTransactionDetails.getTransactionDate());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private Map<String, Boolean> getReferenceSearchParamPresenceMap(ResourceTable entity, ResourceIndexedSearchParams newParams) {
|
||||
Map<String, Boolean> retval = new HashMap<>();
|
||||
|
||||
for (String nextKey : newParams.getPopulatedResourceLinkParameters()) {
|
||||
retval.put(nextKey, Boolean.TRUE);
|
||||
}
|
||||
|
||||
ResourceSearchParams activeSearchParams = mySearchParamRegistry.getActiveSearchParams(entity.getResourceType());
|
||||
activeSearchParams
|
||||
.getReferenceSearchParamNames()
|
||||
.forEach(key -> retval.putIfAbsent(key, Boolean.FALSE));
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
public void setStorageSettings(StorageSettings theStorageSettings) {
|
||||
myStorageSettings = theStorageSettings;
|
||||
|
@ -169,8 +213,9 @@ public class SearchParamExtractorService {
|
|||
|
||||
// Extract search parameters
|
||||
IChainedSearchParameterExtractionStrategy strategy = new IChainedSearchParameterExtractionStrategy() {
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<String> getChainedSearchParametersToIndexForPath(PathAndRef thePathAndRef) {
|
||||
public ISearchParamExtractor.ISearchParamFilter getSearchParamFilter(@Nonnull PathAndRef thePathAndRef) {
|
||||
// Currently for contained resources we always index all search parameters
|
||||
// on all contained resources. A potential nice future optimization would
|
||||
// be to make this configurable, perhaps with an optional extension you could
|
||||
|
@ -179,7 +224,7 @@ public class SearchParamExtractorService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource fetchResourceAtPath(PathAndRef thePathAndRef) {
|
||||
public IBaseResource fetchResourceAtPath(@Nonnull PathAndRef thePathAndRef) {
|
||||
return findContainedResource(containedResources, thePathAndRef.getRef());
|
||||
}
|
||||
};
|
||||
|
@ -197,15 +242,23 @@ public class SearchParamExtractorService {
|
|||
private void extractSearchIndexParametersForUpliftedRefchains(RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, ResourceTable theEntity, RequestPartitionId theRequestPartitionId, TransactionDetails theTransactionDetails, ISearchParamExtractor.SearchParamSet<PathAndRef> theIndexedReferences) {
|
||||
IChainedSearchParameterExtractionStrategy strategy = new IChainedSearchParameterExtractionStrategy() {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<String> getChainedSearchParametersToIndexForPath(PathAndRef thePathAndRef) {
|
||||
public ISearchParamExtractor.ISearchParamFilter getSearchParamFilter(@Nonnull PathAndRef thePathAndRef) {
|
||||
String searchParamName = thePathAndRef.getSearchParamName();
|
||||
RuntimeSearchParam searchParam = mySearchParamRegistry.getActiveSearchParam(theEntity.getResourceType(), searchParamName);
|
||||
return searchParam.getUpliftRefchainCodes();
|
||||
Set<String> upliftRefchainCodes = searchParam.getUpliftRefchainCodes();
|
||||
if (upliftRefchainCodes.isEmpty()) {
|
||||
return ISearchParamExtractor.NO_PARAMS;
|
||||
}
|
||||
return sp -> sp
|
||||
.stream()
|
||||
.filter(t -> upliftRefchainCodes.contains(t.getName()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource fetchResourceAtPath(PathAndRef thePathAndRef) {
|
||||
public IBaseResource fetchResourceAtPath(@Nonnull PathAndRef thePathAndRef) {
|
||||
// The PathAndRef will contain a resource if the SP path was inside a Bundle
|
||||
// and pointed to a resource (e.g. Bundle.entry.resource) as opposed to
|
||||
// pointing to a reference (e.g. Observation.subject)
|
||||
|
@ -264,8 +317,8 @@ public class SearchParamExtractorService {
|
|||
continue;
|
||||
|
||||
// 3.1.2 check if this ref actually applies here
|
||||
Set<String> searchParamsToIndex = theTargetIndexingStrategy.getChainedSearchParametersToIndexForPath(nextPathAndRef);
|
||||
if (searchParamsToIndex.isEmpty()) {
|
||||
ISearchParamExtractor.ISearchParamFilter searchParamsToIndex = theTargetIndexingStrategy.getSearchParamFilter(nextPathAndRef);
|
||||
if (searchParamsToIndex == ISearchParamExtractor.NO_PARAMS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -332,42 +385,42 @@ public class SearchParamExtractorService {
|
|||
theTargetParams.myCompositeParams.addAll(theSrcParams.myCompositeParams);
|
||||
}
|
||||
|
||||
void extractSearchIndexParameters(RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
void extractSearchIndexParameters(RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, IBaseResource theResource, @Nonnull ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
|
||||
|
||||
// Strings
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamString> strings = extractSearchParamStrings(theResource, theParamsToIndex);
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamString> strings = extractSearchParamStrings(theResource, theSearchParamFilter);
|
||||
handleWarnings(theRequestDetails, myInterceptorBroadcaster, strings);
|
||||
theParams.myStringParams.addAll(strings);
|
||||
|
||||
// Numbers
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamNumber> numbers = extractSearchParamNumber(theResource, theParamsToIndex);
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamNumber> numbers = extractSearchParamNumber(theResource, theSearchParamFilter);
|
||||
handleWarnings(theRequestDetails, myInterceptorBroadcaster, numbers);
|
||||
theParams.myNumberParams.addAll(numbers);
|
||||
|
||||
// Quantities
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantity> quantities = extractSearchParamQuantity(theResource, theParamsToIndex);
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantity> quantities = extractSearchParamQuantity(theResource, theSearchParamFilter);
|
||||
handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantities);
|
||||
theParams.myQuantityParams.addAll(quantities);
|
||||
|
||||
if (myStorageSettings.getNormalizedQuantitySearchLevel().equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED) || myStorageSettings.getNormalizedQuantitySearchLevel().equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED)) {
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantityNormalized> quantitiesNormalized = extractSearchParamQuantityNormalized(theResource, theParamsToIndex);
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantityNormalized> quantitiesNormalized = extractSearchParamQuantityNormalized(theResource, theSearchParamFilter);
|
||||
handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantitiesNormalized);
|
||||
theParams.myQuantityNormalizedParams.addAll(quantitiesNormalized);
|
||||
}
|
||||
|
||||
// Dates
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamDate> dates = extractSearchParamDates(theResource, theParamsToIndex);
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamDate> dates = extractSearchParamDates(theResource, theSearchParamFilter);
|
||||
handleWarnings(theRequestDetails, myInterceptorBroadcaster, dates);
|
||||
theParams.myDateParams.addAll(dates);
|
||||
|
||||
// URIs
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamUri> uris = extractSearchParamUri(theResource, theParamsToIndex);
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamUri> uris = extractSearchParamUri(theResource, theSearchParamFilter);
|
||||
handleWarnings(theRequestDetails, myInterceptorBroadcaster, uris);
|
||||
theParams.myUriParams.addAll(uris);
|
||||
|
||||
// Tokens (can result in both Token and String, as we index the display name for
|
||||
// the types: Coding, CodeableConcept)
|
||||
ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> tokens = extractSearchParamTokens(theResource, theParamsToIndex);
|
||||
ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> tokens = extractSearchParamTokens(theResource, theSearchParamFilter);
|
||||
for (BaseResourceIndexedSearchParam next : tokens) {
|
||||
if (next instanceof ResourceIndexedSearchParamToken) {
|
||||
theParams.myTokenParams.add((ResourceIndexedSearchParamToken) next);
|
||||
|
@ -381,13 +434,13 @@ public class SearchParamExtractorService {
|
|||
// Composites
|
||||
// dst2 composites use stuff like value[x] , and we don't support them.
|
||||
if (myContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamComposite> composites = extractSearchParamComposites(theResource, theParamsToIndex);
|
||||
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamComposite> composites = extractSearchParamComposites(theResource, theSearchParamFilter);
|
||||
handleWarnings(theRequestDetails, myInterceptorBroadcaster, composites);
|
||||
theParams.myCompositeParams.addAll(composites);
|
||||
}
|
||||
|
||||
// Specials
|
||||
ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> specials = extractSearchParamSpecial(theResource, theParamsToIndex);
|
||||
ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> specials = extractSearchParamSpecial(theResource, theSearchParamFilter);
|
||||
for (BaseResourceIndexedSearchParam next : specials) {
|
||||
if (next instanceof ResourceIndexedSearchParamCoords) {
|
||||
theParams.myCoordsParams.add((ResourceIndexedSearchParamCoords) next);
|
||||
|
@ -543,7 +596,7 @@ public class SearchParamExtractorService {
|
|||
*/
|
||||
myResourceLinkResolver.validateTypeOrThrowException(type);
|
||||
|
||||
/**
|
||||
/*
|
||||
* We need to obtain a resourceLink out of the provided {@literal thePathAndRef}. In the case
|
||||
* where we are updating a resource that already has resourceLinks (stored in {@literal theExistingParams.getResourceLinks()}),
|
||||
* let's try to match thePathAndRef to an already existing resourceLink to avoid the
|
||||
|
@ -664,9 +717,8 @@ public class SearchParamExtractorService {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private ResourceLink resolveTargetAndCreateResourceLinkOrReturnNull(@Nonnull RequestPartitionId theRequestPartitionId, String theSourceResourceName, PathAndRef thePathAndRef, ResourceTable theEntity, Date theUpdateTime, IIdType theNextId, RequestDetails theRequest, TransactionDetails theTransactionDetails) {
|
||||
assert theRequestPartitionId != null;
|
||||
|
||||
JpaPid resolvedResourceId = (JpaPid) theTransactionDetails.getResolvedResourceId(theNextId);
|
||||
if (resolvedResourceId != null) {
|
||||
String targetResourceType = theNextId.getResourceType();
|
||||
|
@ -742,40 +794,40 @@ public class SearchParamExtractorService {
|
|||
}
|
||||
}
|
||||
|
||||
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamDate> extractSearchParamDates(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
return mySearchParamExtractor.extractSearchParamDates(theResource, theParamsToIndex);
|
||||
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamDate> extractSearchParamDates(IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
|
||||
return mySearchParamExtractor.extractSearchParamDates(theResource, theSearchParamFilter);
|
||||
}
|
||||
|
||||
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
return mySearchParamExtractor.extractSearchParamNumber(theResource, theParamsToIndex);
|
||||
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
|
||||
return mySearchParamExtractor.extractSearchParamNumber(theResource, theSearchParamFilter);
|
||||
}
|
||||
|
||||
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
return mySearchParamExtractor.extractSearchParamQuantity(theResource, theParamsToIndex);
|
||||
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
|
||||
return mySearchParamExtractor.extractSearchParamQuantity(theResource, theSearchParamFilter);
|
||||
}
|
||||
|
||||
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantityNormalized> extractSearchParamQuantityNormalized(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
return mySearchParamExtractor.extractSearchParamQuantityNormalized(theResource, theParamsToIndex);
|
||||
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantityNormalized> extractSearchParamQuantityNormalized(IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
|
||||
return mySearchParamExtractor.extractSearchParamQuantityNormalized(theResource, theSearchParamFilter);
|
||||
}
|
||||
|
||||
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamString> extractSearchParamStrings(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
return mySearchParamExtractor.extractSearchParamStrings(theResource, theParamsToIndex);
|
||||
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamString> extractSearchParamStrings(IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
|
||||
return mySearchParamExtractor.extractSearchParamStrings(theResource, theSearchParamFilter);
|
||||
}
|
||||
|
||||
private ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamTokens(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
return mySearchParamExtractor.extractSearchParamTokens(theResource, theParamsToIndex);
|
||||
private ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamTokens(IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
|
||||
return mySearchParamExtractor.extractSearchParamTokens(theResource, theSearchParamFilter);
|
||||
}
|
||||
|
||||
private ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamSpecial(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
return mySearchParamExtractor.extractSearchParamSpecial(theResource, theParamsToIndex);
|
||||
private ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamSpecial(IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
|
||||
return mySearchParamExtractor.extractSearchParamSpecial(theResource, theSearchParamFilter);
|
||||
}
|
||||
|
||||
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamUri> extractSearchParamUri(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
return mySearchParamExtractor.extractSearchParamUri(theResource, theParamsToIndex);
|
||||
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamUri> extractSearchParamUri(IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
|
||||
return mySearchParamExtractor.extractSearchParamUri(theResource, theSearchParamFilter);
|
||||
}
|
||||
|
||||
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamComposite> extractSearchParamComposites(IBaseResource theResource, Set<String> theParamsToIndex) {
|
||||
return mySearchParamExtractor.extractSearchParamComposites(theResource, theParamsToIndex);
|
||||
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamComposite> extractSearchParamComposites(IBaseResource theResource, ISearchParamExtractor.ISearchParamFilter theSearchParamFilter) {
|
||||
return mySearchParamExtractor.extractSearchParamComposites(theResource, theSearchParamFilter);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
@ -812,12 +864,13 @@ public class SearchParamExtractorService {
|
|||
/**
|
||||
* Which search parameters should be indexed for the resource target
|
||||
* at the given path. In other words if thePathAndRef contains
|
||||
* "Patient/123", then we could return a Set containing "name" and "gender"
|
||||
* if we only want those two parameters to be indexed for the
|
||||
* resolved Patient resource with that ID.
|
||||
* "Patient/123", then we could return a filter that only lets the
|
||||
* "name" and "gender" search params through if we only want those
|
||||
* two parameters to be indexed for the resolved Patient resource
|
||||
* with that ID.
|
||||
*/
|
||||
@Nonnull
|
||||
Set<String> getChainedSearchParametersToIndexForPath(@Nonnull PathAndRef thePathAndRef);
|
||||
ISearchParamExtractor.ISearchParamFilter getSearchParamFilter(@Nonnull PathAndRef thePathAndRef);
|
||||
|
||||
/**
|
||||
* Actually fetch the resource at the given path, or return
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -28,8 +28,10 @@ import java.util.Comparator;
|
|||
import java.util.List;
|
||||
|
||||
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.in;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -167,6 +169,59 @@ public class FhirResourceDaoR4ComboNonUniqueParamTest extends BaseComboParamsR4T
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateAndUpdateResource() {
|
||||
createNamesAndGenderSp();
|
||||
|
||||
// Create a resource patching the unique SP
|
||||
myCaptureQueriesListener.clear();
|
||||
IIdType id1 = createPatient1();
|
||||
assertNotNull(id1);
|
||||
|
||||
assertEquals(0, myCaptureQueriesListener.countSelectQueries());
|
||||
assertEquals(12, myCaptureQueriesListener.countInsertQueries());
|
||||
assertEquals(0, myCaptureQueriesListener.countUpdateQueries());
|
||||
assertEquals(0, myCaptureQueriesListener.countDeleteQueries());
|
||||
assertEquals(1, myCaptureQueriesListener.countCommits());
|
||||
assertEquals(0, myCaptureQueriesListener.countRollbacks());
|
||||
|
||||
runInTransaction(()->{
|
||||
List<String> indexes = myResourceIndexedComboTokensNonUniqueDao
|
||||
.findAll()
|
||||
.stream()
|
||||
.map(ResourceIndexedComboTokenNonUnique::getIndexString)
|
||||
.toList();
|
||||
assertThat(indexes.toString(), indexes, contains("Patient?family=FAMILY1%5C%7C&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale&given=GIVEN1"));
|
||||
});
|
||||
|
||||
/*
|
||||
* Now update the resource
|
||||
*/
|
||||
|
||||
Patient patient = myPatientDao.read(id1, mySrd);
|
||||
patient.getNameFirstRep().setFamily("Family2");
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
myPatientDao.update(patient, mySrd);
|
||||
|
||||
assertEquals(6, myCaptureQueriesListener.countSelectQueries());
|
||||
assertEquals(1, myCaptureQueriesListener.countInsertQueries());
|
||||
assertEquals(5, myCaptureQueriesListener.countUpdateQueries());
|
||||
assertEquals(0, myCaptureQueriesListener.countDeleteQueries());
|
||||
assertEquals(1, myCaptureQueriesListener.countCommits());
|
||||
assertEquals(0, myCaptureQueriesListener.countRollbacks());
|
||||
|
||||
runInTransaction(()->{
|
||||
List<String> indexes = myResourceIndexedComboTokensNonUniqueDao
|
||||
.findAll()
|
||||
.stream()
|
||||
.map(ResourceIndexedComboTokenNonUnique::getIndexString)
|
||||
.toList();
|
||||
assertThat(indexes.toString(), indexes, contains("Patient?family=FAMILY2%5C%7C&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale&given=GIVEN1"));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithExtraParameters() {
|
||||
createNamesAndGenderSp();
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
@ -28,6 +28,12 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.htmlunit</groupId>
|
||||
<artifactId>htmlunit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -453,7 +453,6 @@ public abstract class BaseJpaR5Test extends BaseJpaTest implements ITestDataBuil
|
|||
purgeHibernateSearch(myEntityManager);
|
||||
|
||||
myStorageSettings.setSchedulingDisabled(true);
|
||||
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.ENABLED);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
|
|
|
@ -0,0 +1,312 @@
|
|||
package ca.uhn.fhir.jpa.search.reindex;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboTokenNonUnique;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.model.entity.SearchParamPresentEntity;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||
import ca.uhn.fhir.test.utilities.HtmlUtil;
|
||||
import com.gargoylesoftware.htmlunit.html.HtmlPage;
|
||||
import com.gargoylesoftware.htmlunit.html.HtmlTable;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests the narrative generation in {@link InstanceReindexServiceImpl}. This is a separate test
|
||||
* from {@literal ReindexDryRunServiceImplTest} because this test doesn't need the JPA
|
||||
* infrastructure.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "SqlDialectInspection"})
|
||||
public class InstanceReindexServiceImplNarrativeR5Test {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(InstanceReindexServiceImplNarrativeR5Test.class);
|
||||
private final FhirContext myCtx = FhirContext.forR4Cached();
|
||||
private final InstanceReindexServiceImpl mySvc = new InstanceReindexServiceImpl();
|
||||
private final PartitionSettings myPartitionSettings = new PartitionSettings();
|
||||
private final JpaStorageSettings myStorageSettings = new JpaStorageSettings();
|
||||
private final ResourceTable myEntity = new ResourceTable();
|
||||
|
||||
@Test
|
||||
public void testIndexComboNonUnique() throws IOException {
|
||||
// Setup
|
||||
ResourceIndexedSearchParams newParams = newParams();
|
||||
newParams.myComboTokenNonUnique.add(new ResourceIndexedComboTokenNonUnique(myPartitionSettings, myEntity, "Patient?identifier=123"));
|
||||
|
||||
// Test
|
||||
Parameters outcome = mySvc.buildIndexResponse(newParams(), newParams, true);
|
||||
ourLog.info("Output:\n{}", myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
// Verify
|
||||
HtmlPage narrativeHtml = extractNarrative(outcome);
|
||||
HtmlTable table = (HtmlTable) narrativeHtml.getElementById("NonUniqueIndexesTable");
|
||||
assertEquals("ADD", getBodyCellValue(table, 0, 0));
|
||||
assertEquals("ComboTokenNonUnique", getBodyCellValue(table, 0, 1));
|
||||
assertEquals("Patient?identifier=123", getBodyCellValue(table, 0, 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexComboUnique() throws IOException {
|
||||
// Setup
|
||||
ResourceIndexedSearchParams newParams = newParams();
|
||||
newParams.myComboStringUniques.add(new ResourceIndexedComboStringUnique(myEntity, "Patient?identifier=123", new IdType("Parameter/foo")));
|
||||
|
||||
// Test
|
||||
Parameters outcome = mySvc.buildIndexResponse(newParams(), newParams, true);
|
||||
ourLog.info("Output:\n{}", myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
// Verify
|
||||
HtmlPage narrativeHtml = extractNarrative(outcome);
|
||||
HtmlTable table = (HtmlTable) narrativeHtml.getElementById("UniqueIndexesTable");
|
||||
assertEquals("ADD", getBodyCellValue(table, 0, 0));
|
||||
assertEquals("ComboStringUnique", getBodyCellValue(table, 0, 1));
|
||||
assertEquals("Patient?identifier=123", getBodyCellValue(table, 0, 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexMissing() throws IOException {
|
||||
// Setup
|
||||
ResourceIndexedSearchParams newParams = newParams();
|
||||
newParams.myTokenParams.add(new ResourceIndexedSearchParamToken(myPartitionSettings, "Observation", "identifier", true));
|
||||
SearchParamPresentEntity subject = new SearchParamPresentEntity("subject", false);
|
||||
subject.setResource(new ResourceTable());
|
||||
subject.setPartitionSettings(myPartitionSettings);
|
||||
subject.calculateHashes();
|
||||
newParams.mySearchParamPresentEntities.add(subject);
|
||||
|
||||
// Test
|
||||
Parameters outcome = mySvc.buildIndexResponse(newParams(), newParams, true);
|
||||
ourLog.info("Output:\n{}", myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
// Verify
|
||||
HtmlPage narrativeHtml = extractNarrative(outcome);
|
||||
HtmlTable table = (HtmlTable) narrativeHtml.getElementById("MissingIndexesTable");
|
||||
assertEquals("identifier", getBodyCellValue(table, 0, 0));
|
||||
assertEquals("ADD", getBodyCellValue(table, 0, 1));
|
||||
assertEquals("Token", getBodyCellValue(table, 0, 2));
|
||||
assertEquals("true", getBodyCellValue(table, 0, 3));
|
||||
assertEquals("subject", getBodyCellValue(table, 1, 0));
|
||||
assertEquals("ADD", getBodyCellValue(table, 1, 1));
|
||||
assertEquals("Reference", getBodyCellValue(table, 1, 2));
|
||||
assertEquals("true", getBodyCellValue(table, 1, 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexNumber() throws IOException {
|
||||
// Setup
|
||||
ResourceIndexedSearchParams newParams = newParams();
|
||||
newParams.myNumberParams.add(new ResourceIndexedSearchParamNumber(myPartitionSettings, "Immunization", "dose", BigDecimal.ONE));
|
||||
|
||||
// Test
|
||||
Parameters outcome = mySvc.buildIndexResponse(newParams(), newParams, true);
|
||||
ourLog.info("Output:\n{}", myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
// Verify
|
||||
HtmlPage narrativeHtml = extractNarrative(outcome);
|
||||
HtmlTable table = (HtmlTable) narrativeHtml.getElementById("NumberIndexesTable");
|
||||
assertEquals("dose", getBodyCellValue(table, 0, 0));
|
||||
assertEquals("ADD", getBodyCellValue(table, 0, 1));
|
||||
assertEquals("Number", getBodyCellValue(table, 0, 2));
|
||||
assertEquals("1", getBodyCellValue(table, 0, 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexResourceLink() throws IOException {
|
||||
// Setup
|
||||
ResourceIndexedSearchParams newParams = newParams();
|
||||
newParams.myLinks.add(ResourceLink.forLocalReference("Observation.subject", myEntity, "Patient", 123L, "123", new Date(), 555L));
|
||||
|
||||
// Test
|
||||
Parameters outcome = mySvc.buildIndexResponse(newParams(), newParams, true);
|
||||
ourLog.info("Output:\n{}", myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
// Verify
|
||||
HtmlPage narrativeHtml = extractNarrative(outcome);
|
||||
HtmlTable table = (HtmlTable) narrativeHtml.getElementById("ResourceLinksTable");
|
||||
assertEquals("Observation.subject", getBodyCellValue(table, 0, 0));
|
||||
assertEquals("ADD", getBodyCellValue(table, 0, 1));
|
||||
assertEquals("Reference", getBodyCellValue(table, 0, 2));
|
||||
assertEquals("Patient/123", getBodyCellValue(table, 0, 3));
|
||||
assertEquals("555", getBodyCellValue(table, 0, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexResourceLinkLogical() throws IOException {
|
||||
// Setup
|
||||
ResourceIndexedSearchParams newParams = newParams();
|
||||
newParams.myLinks.add(ResourceLink.forLogicalReference("Observation.subject", myEntity, "http://foo/base/Patient/456", new Date()));
|
||||
|
||||
// Test
|
||||
Parameters outcome = mySvc.buildIndexResponse(newParams(), newParams, true);
|
||||
ourLog.info("Output:\n{}", myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
// Verify
|
||||
HtmlPage narrativeHtml = extractNarrative(outcome);
|
||||
HtmlTable table = (HtmlTable) narrativeHtml.getElementById("ResourceLinksTable");
|
||||
assertEquals("Observation.subject", getBodyCellValue(table, 0, 0));
|
||||
assertEquals("ADD", getBodyCellValue(table, 0, 1));
|
||||
assertEquals("Reference", getBodyCellValue(table, 0, 2));
|
||||
assertEquals("", getBodyCellValue(table, 0, 3));
|
||||
assertEquals("", getBodyCellValue(table, 0, 4));
|
||||
assertEquals("http://foo/base/Patient/456", getBodyCellValue(table, 0, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexResourceLinkAbsolute() throws IOException {
|
||||
// Setup
|
||||
ResourceIndexedSearchParams newParams = newParams();
|
||||
newParams.myLinks.add(ResourceLink.forAbsoluteReference("Observation.subject", myEntity, new IdType("http://foo/base/Patient/123"), new Date()));
|
||||
|
||||
// Test
|
||||
Parameters outcome = mySvc.buildIndexResponse(newParams(), newParams, true);
|
||||
ourLog.info("Output:\n{}", myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
// Verify
|
||||
HtmlPage narrativeHtml = extractNarrative(outcome);
|
||||
HtmlTable table = (HtmlTable) narrativeHtml.getElementById("ResourceLinksTable");
|
||||
assertEquals("Observation.subject", getBodyCellValue(table, 0, 0));
|
||||
assertEquals("ADD", getBodyCellValue(table, 0, 1));
|
||||
assertEquals("Reference", getBodyCellValue(table, 0, 2));
|
||||
assertEquals("", getBodyCellValue(table, 0, 3));
|
||||
assertEquals("", getBodyCellValue(table, 0, 4));
|
||||
assertEquals("http://foo/base/Patient/123", getBodyCellValue(table, 0, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexQuantity() throws IOException {
|
||||
// Setup
|
||||
ResourceIndexedSearchParams newParams = newParams();
|
||||
newParams.myQuantityParams.add(new ResourceIndexedSearchParamQuantity(myPartitionSettings, "Observation", "value-quantity", BigDecimal.valueOf(123), "http://unitsofmeasure.org", "kg"));
|
||||
|
||||
// Test
|
||||
Parameters outcome = mySvc.buildIndexResponse(newParams(), newParams, true);
|
||||
ourLog.info("Output:\n{}", myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
// Verify
|
||||
HtmlPage narrativeHtml = extractNarrative(outcome);
|
||||
HtmlTable table = (HtmlTable) narrativeHtml.getElementById("QuantityIndexesTable");
|
||||
assertEquals("value-quantity", getBodyCellValue(table, 0, 0));
|
||||
assertEquals("ADD", getBodyCellValue(table, 0, 1));
|
||||
assertEquals("Quantity", getBodyCellValue(table, 0, 2));
|
||||
assertEquals("123", getBodyCellValue(table, 0, 3));
|
||||
assertEquals("http://unitsofmeasure.org", getBodyCellValue(table, 0, 4));
|
||||
assertEquals("kg", getBodyCellValue(table, 0, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexQuantityNormalized() throws IOException {
|
||||
// Setup
|
||||
ResourceIndexedSearchParams newParams = newParams();
|
||||
newParams.myQuantityNormalizedParams.add(new ResourceIndexedSearchParamQuantityNormalized(myPartitionSettings, "Observation", "value-quantity", 123.0, "http://unitsofmeasure.org", "kg"));
|
||||
|
||||
// Test
|
||||
Parameters outcome = mySvc.buildIndexResponse(newParams(), newParams, true);
|
||||
ourLog.info("Output:\n{}", myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
// Verify
|
||||
HtmlPage narrativeHtml = extractNarrative(outcome);
|
||||
HtmlTable table = (HtmlTable) narrativeHtml.getElementById("QuantityIndexesTable");
|
||||
assertEquals("value-quantity", getBodyCellValue(table, 0, 0));
|
||||
assertEquals("ADD", getBodyCellValue(table, 0, 1));
|
||||
assertEquals("QuantityNormalized", getBodyCellValue(table, 0, 2));
|
||||
assertEquals("123.0", getBodyCellValue(table, 0, 3));
|
||||
assertEquals("http://unitsofmeasure.org", getBodyCellValue(table, 0, 4));
|
||||
assertEquals("kg", getBodyCellValue(table, 0, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexString() throws IOException {
|
||||
// Setup
|
||||
ResourceIndexedSearchParams newParams = newParams();
|
||||
newParams.myStringParams.add(new ResourceIndexedSearchParamString(myPartitionSettings, myStorageSettings, "Patient", "family", "Simpson", "SIMPSON"));
|
||||
|
||||
// Test
|
||||
Parameters outcome = mySvc.buildIndexResponse(newParams(), newParams, true);
|
||||
ourLog.info("Output:\n{}", myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
// Verify
|
||||
HtmlPage narrativeHtml = extractNarrative(outcome);
|
||||
HtmlTable table = (HtmlTable) narrativeHtml.getElementById("StringIndexesTable");
|
||||
assertEquals("family", getBodyCellValue(table, 0, 0));
|
||||
assertEquals("ADD", getBodyCellValue(table, 0, 1));
|
||||
assertEquals("String", getBodyCellValue(table, 0, 2));
|
||||
assertEquals("Simpson", getBodyCellValue(table, 0, 3));
|
||||
assertEquals("SIMPSON", getBodyCellValue(table, 0, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexToken() throws IOException {
|
||||
// Setup
|
||||
ResourceIndexedSearchParams newParams = newParams();
|
||||
newParams.myTokenParams.add(new ResourceIndexedSearchParamToken(myPartitionSettings, "Observation", "identifier", "http://id-system", "id-value"));
|
||||
|
||||
// Test
|
||||
Parameters outcome = mySvc.buildIndexResponse(newParams(), newParams, true);
|
||||
ourLog.info("Output:\n{}", myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
// Verify
|
||||
HtmlPage narrativeHtml = extractNarrative(outcome);
|
||||
HtmlTable table = (HtmlTable) narrativeHtml.getElementById("TokenIndexesTable");
|
||||
assertEquals("identifier", getBodyCellValue(table, 0, 0));
|
||||
assertEquals("ADD", getBodyCellValue(table, 0, 1));
|
||||
assertEquals("Token", getBodyCellValue(table, 0, 2));
|
||||
assertEquals("http://id-system", getBodyCellValue(table, 0, 3));
|
||||
assertEquals("id-value", getBodyCellValue(table, 0, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexUrl() throws IOException {
|
||||
// Setup
|
||||
ResourceIndexedSearchParams newParams = newParams();
|
||||
newParams.myUriParams.add(new ResourceIndexedSearchParamUri(myPartitionSettings, "CodeSystem", "uri", "http://some-codesystem"));
|
||||
|
||||
// Test
|
||||
Parameters outcome = mySvc.buildIndexResponse(newParams(), newParams, true);
|
||||
ourLog.info("Output:\n{}", myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
// Verify
|
||||
HtmlPage narrativeHtml = extractNarrative(outcome);
|
||||
HtmlTable table = (HtmlTable) narrativeHtml.getElementById("UriIndexesTable");
|
||||
assertEquals("uri", getBodyCellValue(table, 0, 0));
|
||||
assertEquals("ADD", getBodyCellValue(table, 0, 1));
|
||||
assertEquals("Uri", getBodyCellValue(table, 0, 2));
|
||||
assertEquals("http://some-codesystem", getBodyCellValue(table, 0, 3));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static HtmlPage extractNarrative(Parameters outcome) throws IOException {
|
||||
StringType narrative = (StringType) outcome.getParameter().get(0).getValue();
|
||||
HtmlPage narrativeHtml = HtmlUtil.parseAsHtml(narrative.getValueAsString());
|
||||
ourLog.info("Narrative:\n{}", narrativeHtml.asXml());
|
||||
return narrativeHtml;
|
||||
}
|
||||
|
||||
private static String getBodyCellValue(HtmlTable table, int theRow, int theCol) {
|
||||
return table.getBodies().get(0).getRows().get(theRow).getCell(theCol).asNormalizedText();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static ResourceIndexedSearchParams newParams() {
|
||||
return new ResourceIndexedSearchParams();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,398 @@
|
|||
package ca.uhn.fhir.jpa.search.reindex;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||
import ca.uhn.fhir.jpa.dao.r5.BaseJpaR5Test;
|
||||
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.util.HapiExtensions;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r5.model.BooleanType;
|
||||
import org.hl7.fhir.r5.model.Enumerations;
|
||||
import org.hl7.fhir.r5.model.Parameters;
|
||||
import org.hl7.fhir.r5.model.Patient;
|
||||
import org.hl7.fhir.r5.model.ResearchStudy;
|
||||
import org.hl7.fhir.r5.model.SearchParameter;
|
||||
import org.hl7.fhir.r5.model.StringType;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@SuppressWarnings({"unchecked", "SqlDialectInspection"})
|
||||
public class InstanceReindexServiceImplR5Test extends BaseJpaR5Test {
|
||||
|
||||
@Autowired
|
||||
private IInstanceReindexService mySvc;
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
public void beforeResetConfig() {
|
||||
super.beforeResetConfig();
|
||||
|
||||
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCleanupDao() {
|
||||
super.afterCleanupDao();
|
||||
|
||||
JpaStorageSettings defaults = new JpaStorageSettings();
|
||||
myStorageSettings.setIndexMissingFields(defaults.getIndexMissingFields());
|
||||
myStorageSettings.setNormalizedQuantitySearchLevel(defaults.getNormalizedQuantitySearchLevel());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDryRunMissing() {
|
||||
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.ENABLED);
|
||||
|
||||
IIdType id = createPatient(withFamily("Simpson"), withGiven("Homer"));
|
||||
|
||||
Parameters outcome = (Parameters) mySvc.reindexDryRun(new SystemRequestDetails(), id, null);
|
||||
ourLog.info("Output:{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
List<Parameters.ParametersParameterComponent> sections = outcome.getParameters("MissingIndexes");
|
||||
assertEquals(1, sections.size());
|
||||
|
||||
List<String> indexInstances = sections
|
||||
.get(0)
|
||||
.getPart()
|
||||
.stream()
|
||||
.map(t -> t.getName() + " " + getPartValue("Action", t) + " " + getPartValue("Type", t) + " " + getPartValue("Missing", t))
|
||||
.sorted()
|
||||
.toList();
|
||||
assertThat(indexInstances.toString(), indexInstances, contains(
|
||||
"_profile NO_CHANGE Reference true",
|
||||
"active NO_CHANGE Token true",
|
||||
"address NO_CHANGE String true",
|
||||
"address-city NO_CHANGE String true",
|
||||
"address-country NO_CHANGE String true",
|
||||
"address-postalcode NO_CHANGE String true",
|
||||
"address-state NO_CHANGE String true",
|
||||
"address-use NO_CHANGE Token true",
|
||||
"age NO_CHANGE Number true",
|
||||
"birthOrderBoolean NO_CHANGE Token true",
|
||||
"birthdate NO_CHANGE Date true",
|
||||
"death-date NO_CHANGE Date true",
|
||||
"email NO_CHANGE Token true",
|
||||
"gender NO_CHANGE Token true",
|
||||
"general-practitioner NO_CHANGE Reference true",
|
||||
"identifier NO_CHANGE Token true",
|
||||
"language NO_CHANGE Token true",
|
||||
"link NO_CHANGE Reference true",
|
||||
"mothersMaidenName NO_CHANGE String true",
|
||||
"organization NO_CHANGE Reference true",
|
||||
"part-agree NO_CHANGE Reference true",
|
||||
"phone NO_CHANGE Token true",
|
||||
"telecom NO_CHANGE Token true"
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDryRunTypes_ComboNonUniqueSearchParam() {
|
||||
createNamesAndGenderSp(false);
|
||||
|
||||
IIdType id = createPatient(withFamily("Simpson"), withGiven("Homer"));
|
||||
|
||||
runInTransaction(this::logAllNonUniqueIndexes);
|
||||
|
||||
Parameters outcome = (Parameters) mySvc.reindexDryRun(new SystemRequestDetails(), id, null);
|
||||
ourLog.info("Output:{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
Parameters.ParametersParameterComponent index = findSingleIndex(outcome, "Patient?family=SIMPSON%5C%7C&given=HOMER", "NonUniqueIndexes");
|
||||
assertEquals("NO_CHANGE", getPartValue("Action", index));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDryRunTypes_ComboUniqueSearchParam() {
|
||||
createNamesAndGenderSp(true);
|
||||
|
||||
IIdType id = createPatient(withFamily("Simpson"), withGiven("Homer"));
|
||||
|
||||
Parameters outcome = (Parameters) mySvc.reindexDryRun(new SystemRequestDetails(), id, null);
|
||||
ourLog.info("Output:{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
Parameters.ParametersParameterComponent index = findIndexes(outcome, "Patient?family=Simpson%5C%7C&given=Homer", 1, "UniqueIndexes").get(0);
|
||||
assertEquals("NO_CHANGE", getPartValue("Action", index));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDryRunTypes_Number() {
|
||||
IIdType id = createResource("ResearchStudy", withPrimitiveAttribute("recruitment.targetNumber", "3"));
|
||||
|
||||
logAllNumberIndexes();
|
||||
|
||||
Parameters outcome = (Parameters) mySvc.reindexDryRun(new SystemRequestDetails(), id, null);
|
||||
ourLog.info("Output:{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
Parameters.ParametersParameterComponent index = findSingleIndex(outcome, ResearchStudy.SP_RECRUITMENTTARGET, "NumberIndexes");
|
||||
assertEquals("NO_CHANGE", getPartValue("Action", index));
|
||||
assertEquals("Number", getPartValue("Type", index));
|
||||
assertEquals("3", getPartValue("Value", index));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDryRunTypes_Quantity() {
|
||||
myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
|
||||
|
||||
IIdType id = createObservation(withQuantityAtPath("valueQuantity", 1.2, "http://unitsofmeasure.org", "kg"));
|
||||
|
||||
Parameters outcome = (Parameters) mySvc.reindexDryRun(new SystemRequestDetails(), id, null);
|
||||
ourLog.info("Output:{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
Parameters.ParametersParameterComponent index = findSingleIndex(outcome, "value-quantity", "QuantityIndexes");
|
||||
assertEquals("NO_CHANGE", getPartValue("Action", index));
|
||||
assertEquals("Quantity", getPartValue("Type", index));
|
||||
assertEquals("http://unitsofmeasure.org", getPartValue("System", index));
|
||||
assertEquals("kg", getPartValue("Units", index));
|
||||
assertEquals(1.2d, getPartValueDecimal(index), 0.001d);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDryRunTypes_QuantityNormalized() {
|
||||
myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
|
||||
|
||||
IIdType id = createObservation(withQuantityAtPath("valueQuantity", 1.2, "http://unitsofmeasure.org", "mg"));
|
||||
|
||||
Parameters outcome = (Parameters) mySvc.reindexDryRun(new SystemRequestDetails(), id, null);
|
||||
ourLog.info("Output:{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
Parameters.ParametersParameterComponent index;
|
||||
|
||||
index = findIndexes(outcome, "value-quantity", 2, "QuantityIndexes").get(0);
|
||||
assertEquals("NO_CHANGE", getPartValue("Action", index));
|
||||
assertEquals("Quantity", getPartValue("Type", index));
|
||||
assertEquals("http://unitsofmeasure.org", getPartValue("System", index));
|
||||
assertEquals("mg", getPartValue("Units", index));
|
||||
assertEquals(1.2d, getPartValueDecimal(index), 0.001d);
|
||||
|
||||
index = findIndexes(outcome, "value-quantity", 2, "QuantityIndexes").get(1);
|
||||
assertEquals("NO_CHANGE", getPartValue("Action", index));
|
||||
assertEquals("QuantityNormalized", getPartValue("Type", index));
|
||||
assertEquals("http://unitsofmeasure.org", getPartValue("System", index));
|
||||
assertEquals("g", getPartValue("Units", index));
|
||||
assertEquals(0.0012d, getPartValueDecimal(index), 0.001d);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDryRunTypes_ResourceLink() {
|
||||
createPatient(withId("A"), withActiveTrue());
|
||||
IIdType id = createObservation(withSubject("Patient/A"));
|
||||
|
||||
Parameters outcome = (Parameters) mySvc.reindexDryRun(new SystemRequestDetails(), id, null);
|
||||
ourLog.info("Output:{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
Parameters.ParametersParameterComponent index = findSingleIndex(outcome, "Observation.subject", "ResourceLinks");
|
||||
assertEquals("NO_CHANGE", getPartValue("Action", index));
|
||||
assertEquals("Reference", getPartValue("Type", index));
|
||||
assertEquals("Patient/A", getPartValue("TargetId", index));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDryRunTypes_ResourceLink_WithUrl() {
|
||||
createPatient(withId("A"), withActiveTrue());
|
||||
IIdType id = createObservation(withSubject("Patient/A"));
|
||||
|
||||
runInTransaction(() -> {
|
||||
assertEquals(2, myEntityManager.createNativeQuery("update HFJ_RES_LINK set TARGET_RESOURCE_ID = null").executeUpdate());
|
||||
assertEquals(2, myEntityManager.createNativeQuery("update HFJ_RES_LINK set TARGET_RESOURCE_URL = 'http://foo'").executeUpdate());
|
||||
assertEquals(2, myEntityManager.createNativeQuery("update HFJ_RES_LINK set TARGET_RESOURCE_VERSION = 1").executeUpdate());
|
||||
});
|
||||
|
||||
Parameters outcome = (Parameters) mySvc.reindexDryRun(new SystemRequestDetails(), id, null);
|
||||
ourLog.info("Output:{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
List<Parameters.ParametersParameterComponent> indexes = findIndexes(outcome, "Observation.subject", 2, "ResourceLinks");
|
||||
Parameters.ParametersParameterComponent index;
|
||||
index = indexes.get(0);
|
||||
assertEquals("ADD", getPartValue("Action", index));
|
||||
assertEquals("Reference", getPartValue("Type", index));
|
||||
assertEquals("Patient/A", getPartValue("TargetId", index));
|
||||
index = indexes.get(1);
|
||||
assertEquals("REMOVE", getPartValue("Action", index));
|
||||
assertEquals("Reference", getPartValue("Type", index));
|
||||
assertEquals("http://foo", getPartValue("TargetUrl", index));
|
||||
assertEquals("1", getPartValue("TargetVersion", index));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDryRunTypes_String() {
|
||||
IIdType id = createPatient(withIdentifier("http://identifiers", "123"), withFamily("Smith"));
|
||||
|
||||
Parameters outcome = (Parameters) mySvc.reindexDryRun(new SystemRequestDetails(), id, null);
|
||||
ourLog.info("Output:{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
Parameters.ParametersParameterComponent index = findSingleIndex(outcome, "family", "StringIndexes");
|
||||
assertEquals("NO_CHANGE", getPartValue("Action", index));
|
||||
assertEquals("String", getPartValue("Type", index));
|
||||
assertEquals("SMITH", getPartValue("ValueNormalized", index));
|
||||
assertEquals("Smith", getPartValue("ValueExact", index));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDryRunTypes_String_SpecificParameter() {
|
||||
IIdType id = createPatient(withIdentifier("http://identifiers", "123"), withFamily("Simpson"), withGiven("Homer"));
|
||||
|
||||
Parameters outcome = (Parameters) mySvc.reindexDryRun(new SystemRequestDetails(), id, Set.of("family"));
|
||||
ourLog.info("Output:{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
Parameters.ParametersParameterComponent index = findSingleIndex(outcome, "family", "StringIndexes");
|
||||
assertEquals("UNKNOWN", getPartValue("Action", index));
|
||||
assertEquals("String", getPartValue("Type", index));
|
||||
assertEquals("SIMPSON", getPartValue("ValueNormalized", index));
|
||||
assertEquals("Simpson", getPartValue("ValueExact", index));
|
||||
|
||||
findIndexes(outcome, "family", 1, "StringIndexes");
|
||||
findIndexes(outcome, "given", 0, "StringIndexes");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDryRunTypes_Token() {
|
||||
IIdType id = createPatient(withIdentifier("http://identifiers", "123"), withFamily("Smith"));
|
||||
|
||||
Parameters outcome = (Parameters) mySvc.reindexDryRun(new SystemRequestDetails(), id, null);
|
||||
ourLog.info("Output:{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
Parameters.ParametersParameterComponent index = findSingleIndex(outcome, "identifier", "TokenIndexes");
|
||||
assertEquals("NO_CHANGE", getPartValue("Action", index));
|
||||
assertEquals("Token", getPartValue("Type", index));
|
||||
assertEquals("http://identifiers", getPartValue("System", index));
|
||||
assertEquals("123", getPartValue("Value", index));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDryRunTypes_Uri() {
|
||||
IIdType id = createResource("CodeSystem", withPrimitiveAttribute("url", "http://foo"));
|
||||
|
||||
Parameters outcome = (Parameters) mySvc.reindexDryRun(new SystemRequestDetails(), id, null);
|
||||
ourLog.info("Output:{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
Parameters.ParametersParameterComponent index = findSingleIndex(outcome, "system", "UriIndexes");
|
||||
assertEquals("NO_CHANGE", getPartValue("Action", index));
|
||||
assertEquals("Uri", getPartValue("Type", index));
|
||||
assertEquals("http://foo", getPartValue("Value", index));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReindexInstance() {
|
||||
Patient p1 = new Patient();
|
||||
p1.setActive(true);
|
||||
p1.addExtension()
|
||||
.setUrl("http://acme.org/eyecolour")
|
||||
.setValue(new StringType("Gold"));
|
||||
IIdType p1id = myPatientDao.create(p1, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameter eyeColourSp = new SearchParameter();
|
||||
eyeColourSp.addBase("Patient");
|
||||
eyeColourSp.setCode("eyecolour");
|
||||
eyeColourSp.setType(Enumerations.SearchParamType.STRING);
|
||||
eyeColourSp.setTitle("Eye Colour");
|
||||
eyeColourSp.setExpression("Patient.extension('http://acme.org/eyecolour')");
|
||||
eyeColourSp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
mySearchParameterDao.create(eyeColourSp, mySrd);
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous("eyecolour", new StringParam("GOLD"));
|
||||
assertEquals(0, myPatientDao.search(map, mySrd).size());
|
||||
|
||||
Parameters outcome = (Parameters) mySvc.reindex(mySrd, p1id);
|
||||
ourLog.info("Output:{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||
|
||||
Parameters.ParametersParameterComponent index = findSingleIndex(outcome, "eyecolour", "StringIndexes");
|
||||
assertEquals("ADD", getPartValue("Action", index));
|
||||
assertEquals("String", getPartValue("Type", index));
|
||||
assertEquals("GOLD", getPartValue("ValueNormalized", index));
|
||||
assertEquals("Gold", getPartValue("ValueExact", index));
|
||||
|
||||
assertEquals(1, myPatientDao.search(map, mySrd).size());
|
||||
}
|
||||
|
||||
|
||||
private void createNamesAndGenderSp(boolean theUnique) {
|
||||
SearchParameter sp = new SearchParameter();
|
||||
sp.setId("SearchParameter/patient-family");
|
||||
sp.setType(Enumerations.SearchParamType.STRING);
|
||||
sp.setCode("family");
|
||||
sp.setExpression("Patient.name.family + '|'");
|
||||
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
sp.addBase("Patient");
|
||||
mySearchParameterDao.update(sp, mySrd);
|
||||
|
||||
sp = new SearchParameter();
|
||||
sp.setId("SearchParameter/patient-given");
|
||||
sp.setType(Enumerations.SearchParamType.STRING);
|
||||
sp.setCode("given");
|
||||
sp.setExpression("Patient.name.given");
|
||||
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
sp.addBase("Patient");
|
||||
mySearchParameterDao.update(sp, mySrd);
|
||||
|
||||
sp = new SearchParameter();
|
||||
sp.setId("SearchParameter/patient-names-and-gender");
|
||||
sp.setType(Enumerations.SearchParamType.COMPOSITE);
|
||||
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
sp.addBase("Patient");
|
||||
sp.addComponent()
|
||||
.setExpression("Patient")
|
||||
.setDefinition("SearchParameter/patient-family");
|
||||
sp.addComponent()
|
||||
.setExpression("Patient")
|
||||
.setDefinition("SearchParameter/patient-given");
|
||||
sp.addExtension()
|
||||
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
|
||||
.setValue(new BooleanType(theUnique));
|
||||
mySearchParameterDao.update(sp, mySrd);
|
||||
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
|
||||
}
|
||||
|
||||
private double getPartValueDecimal(Parameters.ParametersParameterComponent theParent) {
|
||||
return Double.parseDouble(getPartValue("Value", theParent));
|
||||
}
|
||||
|
||||
private static Parameters.ParametersParameterComponent findSingleIndex(Parameters theResponse, String theParamName, String theSectionName) {
|
||||
List<Parameters.ParametersParameterComponent> indexInstances = findIndexes(theResponse, theParamName, 1, theSectionName);
|
||||
return indexInstances.get(0);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static List<Parameters.ParametersParameterComponent> findIndexes(Parameters theResponse, String theParamName, int theExpectedSize, String theSectionName) {
|
||||
List<Parameters.ParametersParameterComponent> indexes = theResponse.getParameters(theSectionName);
|
||||
assertEquals(1, indexes.size());
|
||||
|
||||
List<Parameters.ParametersParameterComponent> indexInstances = indexes
|
||||
.get(0)
|
||||
.getPart()
|
||||
.stream()
|
||||
.filter(t -> t.getName().equals(theParamName))
|
||||
.toList();
|
||||
|
||||
assertEquals(theExpectedSize, indexInstances.size());
|
||||
return indexInstances;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static String getPartValue(String thePartName, Parameters.ParametersParameterComponent theParent) {
|
||||
return theParent
|
||||
.getPart()
|
||||
.stream()
|
||||
.filter(t2 -> t2.getName().equals(thePartName))
|
||||
.findFirst()
|
||||
.map(t -> (IPrimitiveType<?>) t.getValue())
|
||||
.map(IPrimitiveType::getValueAsString)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Couldn't find part with name: " + thePartName));
|
||||
}
|
||||
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -17,6 +17,7 @@ import ca.uhn.fhir.jpa.provider.DiffProvider;
|
|||
import ca.uhn.fhir.jpa.provider.JpaCapabilityStatementProvider;
|
||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
|
||||
import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
|
||||
import ca.uhn.fhir.jpa.provider.InstanceReindexProvider;
|
||||
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||
import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||
|
@ -197,6 +198,7 @@ public class TestRestfulServer extends RestfulServer {
|
|||
}
|
||||
|
||||
providers.add(myAppCtx.getBean(JpaSystemProvider.class));
|
||||
providers.add(myAppCtx.getBean(InstanceReindexProvider.class));
|
||||
|
||||
/*
|
||||
* On the DSTU2 endpoint, we want to enable ETag support
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -163,6 +163,11 @@ public class ProviderConstants {
|
|||
*/
|
||||
public static final String OPERATION_REINDEX = "$reindex";
|
||||
|
||||
/**
|
||||
* Operation name for the $reindex operation
|
||||
*/
|
||||
public static final String OPERATION_REINDEX_DRYRUN = "$reindex-dryrun";
|
||||
|
||||
/**
|
||||
* Operation name for the $invalidate-expansion operation
|
||||
*/
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
@ -21,7 +21,7 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -56,7 +56,7 @@ public class ReindexProvider {
|
|||
}
|
||||
|
||||
@Operation(name = ProviderConstants.OPERATION_REINDEX, idempotent = false)
|
||||
public IBaseParameters Reindex(
|
||||
public IBaseParameters reindex(
|
||||
@OperationParam(name = ProviderConstants.OPERATION_REINDEX_PARAM_URL, typeName = "string", min = 0, max = OperationParam.MAX_UNLIMITED) List<IPrimitiveType<String>> theUrlsToReindex,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -119,7 +119,6 @@ public class JpaStorageSettings extends StorageSettings {
|
|||
*/
|
||||
@Nullable
|
||||
private Integer myMaximumIncludesToLoadPerPage = DEFAULT_MAXIMUM_INCLUDES_TO_LOAD_PER_PAGE;
|
||||
private IndexEnabledEnum myIndexMissingFieldsEnabled = IndexEnabledEnum.DISABLED;
|
||||
/**
|
||||
* update setter javadoc if default changes
|
||||
*/
|
||||
|
@ -847,53 +846,6 @@ public class JpaStorageSettings extends StorageSettings {
|
|||
myFetchSizeDefaultMaximum = theFetchSizeDefaultMaximum;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@link IndexEnabledEnum#DISABLED} (default is {@link IndexEnabledEnum#DISABLED})
|
||||
* the server will not create search indexes for search parameters with no values in resources.
|
||||
* <p>
|
||||
* Disabling this feature means that the <code>:missing</code> search modifier will not be
|
||||
* supported on the server, but also means that storage and indexing (i.e. writes to the
|
||||
* database) may be much faster on servers which have lots of search parameters and need
|
||||
* to write quickly.
|
||||
* </p>
|
||||
* <p>
|
||||
* This feature may be enabled on servers where supporting the use of the :missing parameter is
|
||||
* of higher importance than raw write performance
|
||||
* </p>
|
||||
*/
|
||||
public IndexEnabledEnum getIndexMissingFields() {
|
||||
return myIndexMissingFieldsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@link IndexEnabledEnum#DISABLED} (default is {@link IndexEnabledEnum#DISABLED})
|
||||
* the server will not create search indexes for search parameters with no values in resources.
|
||||
* <p>
|
||||
* Disabling this feature means that the <code>:missing</code> search modifier will not be
|
||||
* supported on the server, but also means that storage and indexing (i.e. writes to the
|
||||
* database) may be much faster on servers which have lots of search parameters and need
|
||||
* to write quickly.
|
||||
* </p>
|
||||
* <p>
|
||||
* This feature may be enabled on servers where supporting the use of the :missing parameter is
|
||||
* of higher importance than raw write performance
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that this setting also has an impact on sorting (i.e. using the
|
||||
* <code>_sort</code> parameter on searches): If the server is configured
|
||||
* to not index missing field.
|
||||
* </p>
|
||||
* <p>
|
||||
* The following index may need to be added into the indexed tables such as <code>HFJ_SPIDX_TOKEN</code>
|
||||
* to improve the search performance while <code>:missing</code> is enabled.
|
||||
* <code>RES_TYPE, SP_NAME, SP_MISSING</code>
|
||||
* </p>
|
||||
*/
|
||||
public void setIndexMissingFields(IndexEnabledEnum theIndexMissingFields) {
|
||||
Validate.notNull(theIndexMissingFields, "theIndexMissingFields must not be null");
|
||||
myIndexMissingFieldsEnabled = theIndexMissingFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #setMaximumExpansionSize(int)}
|
||||
*/
|
||||
|
@ -2379,10 +2331,6 @@ public class JpaStorageSettings extends StorageSettings {
|
|||
}
|
||||
|
||||
|
||||
public enum IndexEnabledEnum {
|
||||
ENABLED,
|
||||
DISABLED
|
||||
}
|
||||
|
||||
/**
|
||||
* This enum provides allowable options for {@link #setResourceServerIdStrategy(IdStrategyEnum)}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.5.11-SNAPSHOT</version>
|
||||
<version>6.5.12-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue