Improve indexing on JPA server in anticipation of a new indexing
strategy in HAPI FHIR 3.5.0
This commit is contained in:
parent
e58779d484
commit
2f2900e837
|
@ -58,6 +58,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
|
|
|
@ -70,6 +70,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
|
||||||
return FhirServerConfigCommon.getDataSource(env);
|
return FhirServerConfigCommon.getDataSource(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
return FhirServerConfigCommon.getEntityManagerFactory(env, dataSource(), fhirContextDstu3());
|
return FhirServerConfigCommon.getEntityManagerFactory(env, dataSource(), fhirContextDstu3());
|
||||||
|
|
|
@ -73,6 +73,7 @@ public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 {
|
||||||
return FhirServerConfigCommon.getDataSource(env);
|
return FhirServerConfigCommon.getDataSource(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
return FhirServerConfigCommon.getEntityManagerFactory(env, dataSource(), fhirContextDstu2());
|
return FhirServerConfigCommon.getEntityManagerFactory(env, dataSource(), fhirContextDstu2());
|
||||||
|
|
|
@ -188,6 +188,7 @@ public class Constants {
|
||||||
public static final String PARAM_GRAPHQL_QUERY = "query";
|
public static final String PARAM_GRAPHQL_QUERY = "query";
|
||||||
public static final String HEADER_X_CACHE = "X-Cache";
|
public static final String HEADER_X_CACHE = "X-Cache";
|
||||||
public static final String HEADER_X_SECURITY_CONTEXT = "X-Security-Context";
|
public static final String HEADER_X_SECURITY_CONTEXT = "X-Security-Context";
|
||||||
|
public static final String POWERED_BY_HEADER = "X-Powered-By";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8);
|
CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8);
|
||||||
|
|
|
@ -63,6 +63,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu2 {
|
||||||
@Qualifier("jpaProperties")
|
@Qualifier("jpaProperties")
|
||||||
private Properties myJpaProperties;
|
private Properties myJpaProperties;
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
|
|
|
@ -66,6 +66,7 @@ public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
|
|
|
@ -66,6 +66,7 @@ public class FhirServerConfigR4 extends BaseJavaConfigR4 {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
|
|
|
@ -28,6 +28,8 @@ import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.subscription.email.SubscriptionEmailInterceptor;
|
import ca.uhn.fhir.jpa.subscription.email.SubscriptionEmailInterceptor;
|
||||||
import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
|
import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor;
|
||||||
import ca.uhn.fhir.jpa.subscription.websocket.SubscriptionWebsocketInterceptor;
|
import ca.uhn.fhir.jpa.subscription.websocket.SubscriptionWebsocketInterceptor;
|
||||||
|
import ca.uhn.fhir.jpa.util.IReindexController;
|
||||||
|
import ca.uhn.fhir.jpa.util.ReindexController;
|
||||||
import org.hibernate.jpa.HibernatePersistenceProvider;
|
import org.hibernate.jpa.HibernatePersistenceProvider;
|
||||||
import org.springframework.beans.factory.annotation.Autowire;
|
import org.springframework.beans.factory.annotation.Autowire;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -48,6 +50,7 @@ import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
|
||||||
import org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean;
|
import org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean;
|
||||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@ -63,14 +66,13 @@ public abstract class BaseConfig implements SchedulingConfigurer {
|
||||||
private ApplicationContext myAppCtx;
|
private ApplicationContext myAppCtx;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTasks(ScheduledTaskRegistrar theTaskRegistrar) {
|
public void configureTasks(@Nonnull ScheduledTaskRegistrar theTaskRegistrar) {
|
||||||
theTaskRegistrar.setTaskScheduler(taskScheduler());
|
theTaskRegistrar.setTaskScheduler(taskScheduler());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(autowire = Autowire.BY_TYPE)
|
@Bean(autowire = Autowire.BY_TYPE)
|
||||||
public DatabaseBackedPagingProvider databaseBackedPagingProvider() {
|
public DatabaseBackedPagingProvider databaseBackedPagingProvider() {
|
||||||
DatabaseBackedPagingProvider retVal = new DatabaseBackedPagingProvider();
|
return new DatabaseBackedPagingProvider();
|
||||||
return retVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,6 +98,11 @@ public abstract class BaseConfig implements SchedulingConfigurer {
|
||||||
return new HibernateJpaDialect();
|
return new HibernateJpaDialect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
private IReindexController reindexController() {
|
||||||
|
return new ReindexController();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean()
|
@Bean()
|
||||||
public ScheduledExecutorFactoryBean scheduledExecutorService() {
|
public ScheduledExecutorFactoryBean scheduledExecutorService() {
|
||||||
ScheduledExecutorFactoryBean b = new ScheduledExecutorFactoryBean();
|
ScheduledExecutorFactoryBean b = new ScheduledExecutorFactoryBean();
|
||||||
|
@ -167,5 +174,4 @@ public abstract class BaseConfig implements SchedulingConfigurer {
|
||||||
return new PropertySourcesPlaceholderConfigurer();
|
return new PropertySourcesPlaceholderConfigurer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||||
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
||||||
import ca.uhn.fhir.jpa.util.ExpungeOptions;
|
import ca.uhn.fhir.jpa.util.ExpungeOptions;
|
||||||
import ca.uhn.fhir.jpa.util.ExpungeOutcome;
|
import ca.uhn.fhir.jpa.util.ExpungeOutcome;
|
||||||
|
import ca.uhn.fhir.jpa.util.ReindexController;
|
||||||
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
|
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
|
||||||
import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils;
|
import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils;
|
||||||
import ca.uhn.fhir.model.api.*;
|
import ca.uhn.fhir.model.api.*;
|
||||||
|
@ -46,7 +47,6 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.method.SearchMethodBinding;
|
import ca.uhn.fhir.rest.server.method.SearchMethodBinding;
|
||||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
|
||||||
import ca.uhn.fhir.util.*;
|
import ca.uhn.fhir.util.*;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.instance.model.api.*;
|
import org.hl7.fhir.instance.model.api.*;
|
||||||
|
@ -61,6 +61,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.transaction.support.TransactionCallback;
|
import org.springframework.transaction.support.TransactionCallback;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.persistence.NoResultException;
|
import javax.persistence.NoResultException;
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
|
@ -88,6 +89,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
private String mySecondaryPrimaryKeyParamName;
|
private String mySecondaryPrimaryKeyParamName;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchParamRegistry mySearchParamRegistry;
|
private ISearchParamRegistry mySearchParamRegistry;
|
||||||
|
@Autowired
|
||||||
|
private ReindexController myReindexController;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
|
public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
|
||||||
|
@ -97,7 +100,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
throw new ResourceNotFoundException(theId);
|
throw new ResourceNotFoundException(theId);
|
||||||
}
|
}
|
||||||
|
|
||||||
//@formatter:off
|
|
||||||
for (BaseTag next : new ArrayList<>(entity.getTags())) {
|
for (BaseTag next : new ArrayList<>(entity.getTags())) {
|
||||||
if (ObjectUtil.equals(next.getTag().getTagType(), theTagType) &&
|
if (ObjectUtil.equals(next.getTag().getTagType(), theTagType) &&
|
||||||
ObjectUtil.equals(next.getTag().getSystem(), theScheme) &&
|
ObjectUtil.equals(next.getTag().getSystem(), theScheme) &&
|
||||||
|
@ -105,7 +107,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//@formatter:on
|
|
||||||
|
|
||||||
entity.setHasTags(true);
|
entity.setHasTags(true);
|
||||||
|
|
||||||
|
@ -459,7 +460,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
return outcome;
|
return outcome;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private <MT extends IBaseMetaType> void doMetaAdd(MT theMetaAdd, BaseHasResource entity) {
|
private <MT extends IBaseMetaType> void doMetaAdd(MT theMetaAdd, BaseHasResource entity) {
|
||||||
List<TagDefinition> tags = toTagList(theMetaAdd);
|
List<TagDefinition> tags = toTagList(theMetaAdd);
|
||||||
|
|
||||||
|
@ -538,7 +538,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
return doExpunge(getResourceName(), null, null, theExpungeOptions);
|
return doExpunge(getResourceName(), null, null, theExpungeOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TagList getAllResourceTags(RequestDetails theRequestDetails) {
|
public TagList getAllResourceTags(RequestDetails theRequestDetails) {
|
||||||
// Notify interceptors
|
// Notify interceptors
|
||||||
|
@ -630,7 +629,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
Integer updatedCount = txTemplate.execute(new TransactionCallback<Integer>() {
|
Integer updatedCount = txTemplate.execute(new TransactionCallback<Integer>() {
|
||||||
@Override
|
@Override
|
||||||
public @NonNull
|
public @NonNull
|
||||||
Integer doInTransaction(TransactionStatus theStatus) {
|
Integer doInTransaction(@Nonnull TransactionStatus theStatus) {
|
||||||
return myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
|
return myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -640,6 +639,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
}
|
}
|
||||||
|
|
||||||
mySearchParamRegistry.requestRefresh();
|
mySearchParamRegistry.requestRefresh();
|
||||||
|
myReindexController.requestReindex();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -52,32 +52,6 @@ public class FhirResourceDaoSearchParameterDstu2 extends FhirResourceDaoDstu2<Se
|
||||||
markResourcesMatchingExpressionAsNeedingReindexing(reindex, expression);
|
markResourcesMatchingExpressionAsNeedingReindexing(reindex, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called once per minute to perform any required re-indexing. During most passes this will
|
|
||||||
* just check and find that there are no resources requiring re-indexing. In that case the method just returns
|
|
||||||
* immediately. If the search finds that some resources require reindexing, the system will do a bunch of
|
|
||||||
* reindexing and then return.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
@Scheduled(fixedDelay = DateUtils.MILLIS_PER_MINUTE)
|
|
||||||
@Transactional(propagation = Propagation.NEVER)
|
|
||||||
public void performReindexingPass() {
|
|
||||||
if (getConfig().isSchedulingDisabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer count = mySystemDao.performReindexingPass(100);
|
|
||||||
for (int i = 0; i < 50 && count != null && count != 0; i++) {
|
|
||||||
count = mySystemDao.performReindexingPass(100);
|
|
||||||
try {
|
|
||||||
Thread.sleep(DateUtils.MILLIS_PER_SECOND);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postPersist(ResourceTable theEntity, SearchParameter theResource) {
|
protected void postPersist(ResourceTable theEntity, SearchParameter theResource) {
|
||||||
super.postPersist(theEntity, theResource);
|
super.postPersist(theEntity, theResource);
|
||||||
|
|
|
@ -24,6 +24,4 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
public interface IFhirResourceDaoSearchParameter<T extends IBaseResource> extends IFhirResourceDao<T> {
|
public interface IFhirResourceDaoSearchParameter<T extends IBaseResource> extends IFhirResourceDao<T> {
|
||||||
|
|
||||||
void performReindexingPass();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,16 @@
|
||||||
package ca.uhn.fhir.jpa.dao.dstu3;
|
package ca.uhn.fhir.jpa.dao.dstu3;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.BaseSearchParamExtractor;
|
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.dao.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4;
|
import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
|
||||||
import ca.uhn.fhir.util.ElementUtil;
|
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
|
||||||
import org.hl7.fhir.dstu3.model.*;
|
import org.hl7.fhir.dstu3.model.*;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR JPA Server
|
* HAPI FHIR JPA Server
|
||||||
|
@ -43,8 +33,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
|
public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoSearchParameterDstu3.class);
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchParamRegistry mySearchParamRegistry;
|
private ISearchParamRegistry mySearchParamRegistry;
|
||||||
|
|
||||||
|
@ -57,31 +45,6 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
|
||||||
markResourcesMatchingExpressionAsNeedingReindexing(reindex, expression);
|
markResourcesMatchingExpressionAsNeedingReindexing(reindex, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called once per minute to perform any required re-indexing. During most passes this will
|
|
||||||
* just check and find that there are no resources requiring re-indexing. In that case the method just returns
|
|
||||||
* immediately. If the search finds that some resources require reindexing, the system will do multiple
|
|
||||||
* reindexing passes and then return.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
@Scheduled(fixedDelay = DateUtils.MILLIS_PER_MINUTE)
|
|
||||||
@Transactional(propagation = Propagation.NEVER)
|
|
||||||
public void performReindexingPass() {
|
|
||||||
if (getConfig().isSchedulingDisabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer count = mySystemDao.performReindexingPass(100);
|
|
||||||
for (int i = 0; i < 50 && count != null && count != 0; i++) {
|
|
||||||
count = mySystemDao.performReindexingPass(100);
|
|
||||||
try {
|
|
||||||
Thread.sleep(DateUtils.MILLIS_PER_SECOND);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postPersist(ResourceTable theEntity, SearchParameter theResource) {
|
protected void postPersist(ResourceTable theEntity, SearchParameter theResource) {
|
||||||
|
|
|
@ -8,13 +8,9 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.ElementUtil;
|
import ca.uhn.fhir.util.ElementUtil;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.hl7.fhir.r4.model.*;
|
import org.hl7.fhir.r4.model.*;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -51,31 +47,6 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchPa
|
||||||
markResourcesMatchingExpressionAsNeedingReindexing(reindex, expression);
|
markResourcesMatchingExpressionAsNeedingReindexing(reindex, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called once per minute to perform any required re-indexing. During most passes this will
|
|
||||||
* just check and find that there are no resources requiring re-indexing. In that case the method just returns
|
|
||||||
* immediately. If the search finds that some resources require reindexing, the system will do multiple
|
|
||||||
* reindexing passes and then return.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
@Scheduled(fixedDelay = DateUtils.MILLIS_PER_MINUTE)
|
|
||||||
@Transactional(propagation = Propagation.NEVER)
|
|
||||||
public void performReindexingPass() {
|
|
||||||
if (getConfig().isSchedulingDisabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer count = mySystemDao.performReindexingPass(100);
|
|
||||||
for (int i = 0; i < 50 && count != null && count != 0; i++) {
|
|
||||||
count = mySystemDao.performReindexingPass(100);
|
|
||||||
try {
|
|
||||||
Thread.sleep(DateUtils.MILLIS_PER_SECOND);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postPersist(ResourceTable theEntity, SearchParameter theResource) {
|
protected void postPersist(ResourceTable theEntity, SearchParameter theResource) {
|
||||||
|
|
|
@ -21,6 +21,12 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.hash.HashCode;
|
||||||
|
import com.google.common.hash.HashFunction;
|
||||||
|
import com.google.common.hash.Hasher;
|
||||||
|
import com.google.common.hash.Hashing;
|
||||||
import org.hibernate.search.annotations.ContainedIn;
|
import org.hibernate.search.annotations.ContainedIn;
|
||||||
import org.hibernate.search.annotations.Field;
|
import org.hibernate.search.annotations.Field;
|
||||||
|
|
||||||
|
@ -30,6 +36,10 @@ import java.util.Date;
|
||||||
|
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
||||||
|
/** Don't change this without careful consideration. You will break existing hashes! */
|
||||||
|
private static final HashFunction HASH_FUNCTION = Hashing.murmur3_128(0);
|
||||||
|
/** Don't make this public 'cause nobody better touch it! */
|
||||||
|
private static final byte[] DELIMITER_BYTES = "|".getBytes(Charsets.UTF_8);
|
||||||
|
|
||||||
static final int MAX_SP_NAME = 100;
|
static final int MAX_SP_NAME = 100;
|
||||||
|
|
||||||
|
@ -68,14 +78,23 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamName(String theName) {
|
public void setParamName(String theName) {
|
||||||
|
clearHashes();
|
||||||
myParamName = theName;
|
myParamName = theName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses may override
|
||||||
|
*/
|
||||||
|
protected void clearHashes() {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
public ResourceTable getResource() {
|
public ResourceTable getResource() {
|
||||||
return myResource;
|
return myResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseResourceIndexedSearchParam setResource(ResourceTable theResource) {
|
public BaseResourceIndexedSearchParam setResource(ResourceTable theResource) {
|
||||||
|
clearHashes();
|
||||||
myResource = theResource;
|
myResource = theResource;
|
||||||
myResourceType = theResource.getResourceType();
|
myResourceType = theResource.getResourceType();
|
||||||
return this;
|
return this;
|
||||||
|
@ -107,4 +126,23 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract IQueryParameterType toQueryParameterType();
|
public abstract IQueryParameterType toQueryParameterType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a fast and consistent hashing algorithm to a set of strings
|
||||||
|
*/
|
||||||
|
static long hash(String... theValues) {
|
||||||
|
Hasher hasher = HASH_FUNCTION.newHasher();
|
||||||
|
|
||||||
|
for (String next : theValues) {
|
||||||
|
next = UrlUtil.escapeUrlParam(next);
|
||||||
|
byte[] bytes = next.getBytes(Charsets.UTF_8);
|
||||||
|
hasher.putBytes(bytes);
|
||||||
|
hasher.putBytes(DELIMITER_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashCode hashCode = hasher.hash();
|
||||||
|
return hashCode.asLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,6 @@ public class ResourceIndexedCompositeStringUnique implements Comparable<Resource
|
||||||
@Id
|
@Id
|
||||||
@Column(name = "PID")
|
@Column(name = "PID")
|
||||||
private Long myId;
|
private Long myId;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", foreignKey = @ForeignKey(name = "FK_IDXCMPSTRUNIQ_RES_ID"))
|
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", foreignKey = @ForeignKey(name = "FK_IDXCMPSTRUNIQ_RES_ID"))
|
||||||
private ResourceTable myResource;
|
private ResourceTable myResource;
|
||||||
|
|
|
@ -29,7 +29,6 @@ import org.hibernate.search.annotations.Field;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
|
|
||||||
//@formatter:off
|
|
||||||
@Embeddable
|
@Embeddable
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "HFJ_SPIDX_COORDS", indexes = {
|
@Table(name = "HFJ_SPIDX_COORDS", indexes = {
|
||||||
|
@ -37,7 +36,6 @@ import javax.persistence.*;
|
||||||
@Index(name = "IDX_SP_COORDS_UPDATED", columnList = "SP_UPDATED"),
|
@Index(name = "IDX_SP_COORDS_UPDATED", columnList = "SP_UPDATED"),
|
||||||
@Index(name = "IDX_SP_COORDS_RESID", columnList = "RES_ID")
|
@Index(name = "IDX_SP_COORDS_RESID", columnList = "RES_ID")
|
||||||
})
|
})
|
||||||
//@formatter:on
|
|
||||||
public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchParam {
|
public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchParam {
|
||||||
|
|
||||||
public static final int MAX_LENGTH = 100;
|
public static final int MAX_LENGTH = 100;
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.hibernate.search.annotations.NumericField;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@Embeddable
|
@Embeddable
|
||||||
|
@ -64,6 +65,16 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_QUANTITY")
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_QUANTITY")
|
||||||
@Column(name = "SP_ID")
|
@Column(name = "SP_ID")
|
||||||
private Long myId;
|
private Long myId;
|
||||||
|
/**
|
||||||
|
* @since 3.4.0 - At some point this should be made not-null
|
||||||
|
*/
|
||||||
|
@Column(name = "HASH_UNITS_AND_VALPREFIX", nullable = true)
|
||||||
|
private Long myHashUnitsAndValPrefix;
|
||||||
|
/**
|
||||||
|
* @since 3.4.0 - At some point this should be made not-null
|
||||||
|
*/
|
||||||
|
@Column(name = "HASH_VALPREFIX", nullable = true)
|
||||||
|
private Long myHashValPrefix;
|
||||||
|
|
||||||
public ResourceIndexedSearchParamQuantity() {
|
public ResourceIndexedSearchParamQuantity() {
|
||||||
// nothing
|
// nothing
|
||||||
|
@ -76,6 +87,20 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
setUnits(theUnits);
|
setUnits(theUnits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
public void calculateHashes() {
|
||||||
|
if (myHashUnitsAndValPrefix == null) {
|
||||||
|
setHashUnitsAndValPrefix(hash(getResourceType(), getParamName(), getSystem(), getUnits(), toTruncatedString(getValue())));
|
||||||
|
setHashValPrefix(hash(getResourceType(), getParamName(), toTruncatedString(getValue())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void clearHashes() {
|
||||||
|
myHashUnitsAndValPrefix = null;
|
||||||
|
myHashValPrefix = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object theObj) {
|
public boolean equals(Object theObj) {
|
||||||
if (this == theObj) {
|
if (this == theObj) {
|
||||||
|
@ -94,9 +119,29 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
b.append(getSystem(), obj.getSystem());
|
b.append(getSystem(), obj.getSystem());
|
||||||
b.append(getUnits(), obj.getUnits());
|
b.append(getUnits(), obj.getUnits());
|
||||||
b.append(getValue(), obj.getValue());
|
b.append(getValue(), obj.getValue());
|
||||||
|
b.append(getHashUnitsAndValPrefix(), obj.getHashUnitsAndValPrefix());
|
||||||
|
b.append(getHashValPrefix(), obj.getHashValPrefix());
|
||||||
return b.isEquals();
|
return b.isEquals();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getHashUnitsAndValPrefix() {
|
||||||
|
calculateHashes();
|
||||||
|
return myHashUnitsAndValPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHashUnitsAndValPrefix(Long theHashUnitsAndValPrefix) {
|
||||||
|
myHashUnitsAndValPrefix = theHashUnitsAndValPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getHashValPrefix() {
|
||||||
|
calculateHashes();
|
||||||
|
return myHashValPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHashValPrefix(Long theHashValPrefix) {
|
||||||
|
myHashValPrefix = theHashValPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Long getId() {
|
protected Long getId() {
|
||||||
return myId;
|
return myId;
|
||||||
|
@ -107,6 +152,7 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSystem(String theSystem) {
|
public void setSystem(String theSystem) {
|
||||||
|
clearHashes();
|
||||||
mySystem = theSystem;
|
mySystem = theSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +161,7 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUnits(String theUnits) {
|
public void setUnits(String theUnits) {
|
||||||
|
clearHashes();
|
||||||
myUnits = theUnits;
|
myUnits = theUnits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,6 +170,7 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(BigDecimal theValue) {
|
public void setValue(BigDecimal theValue) {
|
||||||
|
clearHashes();
|
||||||
myValue = theValue;
|
myValue = theValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,6 +182,8 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
b.append(getSystem());
|
b.append(getSystem());
|
||||||
b.append(getUnits());
|
b.append(getUnits());
|
||||||
b.append(getValue());
|
b.append(getValue());
|
||||||
|
b.append(getHashUnitsAndValPrefix());
|
||||||
|
b.append(getHashValPrefix());
|
||||||
return b.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,4 +203,8 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
return b.build();
|
return b.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String toTruncatedString(BigDecimal theValue) {
|
||||||
|
return theValue.setScale(0, RoundingMode.FLOOR).toPlainString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ import org.hibernate.search.annotations.*;
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
import javax.persistence.Index;
|
import javax.persistence.Index;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.left;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@Embeddable
|
@Embeddable
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -91,9 +93,8 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
||||||
* Note that MYSQL chokes on unique indexes for lengths > 255 so be careful here
|
* Note that MYSQL chokes on unique indexes for lengths > 255 so be careful here
|
||||||
*/
|
*/
|
||||||
public static final int MAX_LENGTH = 200;
|
public static final int MAX_LENGTH = 200;
|
||||||
|
public static final int HASH_PREFIX_LENGTH = 1;
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@SequenceGenerator(name = "SEQ_SPIDX_STRING", sequenceName = "SEQ_SPIDX_STRING")
|
@SequenceGenerator(name = "SEQ_SPIDX_STRING", sequenceName = "SEQ_SPIDX_STRING")
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_STRING")
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_STRING")
|
||||||
|
@ -116,6 +117,16 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
||||||
|
|
||||||
@Column(name = "SP_VALUE_NORMALIZED", length = MAX_LENGTH, nullable = true)
|
@Column(name = "SP_VALUE_NORMALIZED", length = MAX_LENGTH, nullable = true)
|
||||||
private String myValueNormalized;
|
private String myValueNormalized;
|
||||||
|
/**
|
||||||
|
* @since 3.4.0 - At some point this should be made not-null
|
||||||
|
*/
|
||||||
|
@Column(name = "HASH_NORM_PREFIX", nullable = true)
|
||||||
|
private Long myHashNormalizedPrefix;
|
||||||
|
/**
|
||||||
|
* @since 3.4.0 - At some point this should be made not-null
|
||||||
|
*/
|
||||||
|
@Column(name = "HASH_EXACT", nullable = true)
|
||||||
|
private Long myHashExact;
|
||||||
|
|
||||||
public ResourceIndexedSearchParamString() {
|
public ResourceIndexedSearchParamString() {
|
||||||
super();
|
super();
|
||||||
|
@ -128,6 +139,20 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
||||||
setValueExact(theValueExact);
|
setValueExact(theValueExact);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
public void calculateHashes() {
|
||||||
|
if (myHashNormalizedPrefix == null) {
|
||||||
|
setHashNormalizedPrefix(hash(getResourceType(), getParamName(), left(getValueNormalized(), HASH_PREFIX_LENGTH)));
|
||||||
|
setHashExact(hash(getResourceType(), getParamName(), getValueExact()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void clearHashes() {
|
||||||
|
myHashNormalizedPrefix = null;
|
||||||
|
myHashExact = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object theObj) {
|
public boolean equals(Object theObj) {
|
||||||
if (this == theObj) {
|
if (this == theObj) {
|
||||||
|
@ -144,9 +169,29 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
||||||
b.append(getParamName(), obj.getParamName());
|
b.append(getParamName(), obj.getParamName());
|
||||||
b.append(getResource(), obj.getResource());
|
b.append(getResource(), obj.getResource());
|
||||||
b.append(getValueExact(), obj.getValueExact());
|
b.append(getValueExact(), obj.getValueExact());
|
||||||
|
b.append(getHashNormalizedPrefix(), obj.getHashNormalizedPrefix());
|
||||||
|
b.append(getHashExact(), obj.getHashExact());
|
||||||
return b.isEquals();
|
return b.isEquals();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getHashExact() {
|
||||||
|
calculateHashes();
|
||||||
|
return myHashExact;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHashExact(Long theHashExact) {
|
||||||
|
myHashExact = theHashExact;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getHashNormalizedPrefix() {
|
||||||
|
calculateHashes();
|
||||||
|
return myHashNormalizedPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHashNormalizedPrefix(Long theHashNormalizedPrefix) {
|
||||||
|
myHashNormalizedPrefix = theHashNormalizedPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Long getId() {
|
protected Long getId() {
|
||||||
return myId;
|
return myId;
|
||||||
|
@ -180,6 +225,8 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
||||||
b.append(getParamName());
|
b.append(getParamName());
|
||||||
b.append(getResource());
|
b.append(getResource());
|
||||||
b.append(getValueExact());
|
b.append(getValueExact());
|
||||||
|
b.append(getHashNormalizedPrefix());
|
||||||
|
b.append(getHashExact());
|
||||||
return b.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ import org.hibernate.search.annotations.Field;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
|
|
||||||
//@formatter:off
|
|
||||||
@Embeddable
|
@Embeddable
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "HFJ_SPIDX_TOKEN", indexes = {
|
@Table(name = "HFJ_SPIDX_TOKEN", indexes = {
|
||||||
|
@ -40,12 +39,12 @@ import javax.persistence.*;
|
||||||
@Index(name = "IDX_SP_TOKEN_UPDATED", columnList = "SP_UPDATED"),
|
@Index(name = "IDX_SP_TOKEN_UPDATED", columnList = "SP_UPDATED"),
|
||||||
@Index(name = "IDX_SP_TOKEN_RESID", columnList = "RES_ID")
|
@Index(name = "IDX_SP_TOKEN_RESID", columnList = "RES_ID")
|
||||||
})
|
})
|
||||||
//@formatter:on
|
|
||||||
public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchParam {
|
public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchParam {
|
||||||
|
|
||||||
public static final int MAX_LENGTH = 200;
|
public static final int MAX_LENGTH = 200;
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Field()
|
@Field()
|
||||||
@Column(name = "SP_SYSTEM", nullable = true, length = MAX_LENGTH)
|
@Column(name = "SP_SYSTEM", nullable = true, length = MAX_LENGTH)
|
||||||
public String mySystem;
|
public String mySystem;
|
||||||
|
@ -57,16 +56,58 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_TOKEN")
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_TOKEN")
|
||||||
@Column(name = "SP_ID")
|
@Column(name = "SP_ID")
|
||||||
private Long myId;
|
private Long myId;
|
||||||
|
/**
|
||||||
|
* @since 3.4.0 - At some point this should be made not-null
|
||||||
|
*/
|
||||||
|
@Column(name = "HASH_SYS", nullable = true)
|
||||||
|
private Long myHashSystem;
|
||||||
|
/**
|
||||||
|
* @since 3.4.0 - At some point this should be made not-null
|
||||||
|
*/
|
||||||
|
@Column(name = "HASH_SYS_AND_VALUE", nullable = true)
|
||||||
|
private Long myHashSystemAndValue;
|
||||||
|
/**
|
||||||
|
* @since 3.4.0 - At some point this should be made not-null
|
||||||
|
*/
|
||||||
|
@Column(name = "HASH_VALUE", nullable = true)
|
||||||
|
private Long myHashValue;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public ResourceIndexedSearchParamToken() {
|
public ResourceIndexedSearchParamToken() {
|
||||||
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public ResourceIndexedSearchParamToken(String theName, String theSystem, String theValue) {
|
public ResourceIndexedSearchParamToken(String theName, String theSystem, String theValue) {
|
||||||
|
super();
|
||||||
setParamName(theName);
|
setParamName(theName);
|
||||||
setSystem(theSystem);
|
setSystem(theSystem);
|
||||||
setValue(theValue);
|
setValue(theValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
public void calculateHashes() {
|
||||||
|
if (myHashSystem == null) {
|
||||||
|
setHashSystem(hash(getResourceType(), getParamName(), getSystem()));
|
||||||
|
setHashSystemAndValue(hash(getResourceType(), getParamName(), getSystem(), getValue()));
|
||||||
|
setHashValue(hash(getResourceType(), getParamName(), getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void clearHashes() {
|
||||||
|
myHashSystem = null;
|
||||||
|
myHashSystemAndValue = null;
|
||||||
|
myHashValue = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object theObj) {
|
public boolean equals(Object theObj) {
|
||||||
if (this == theObj) {
|
if (this == theObj) {
|
||||||
|
@ -84,9 +125,40 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
b.append(getResource(), obj.getResource());
|
b.append(getResource(), obj.getResource());
|
||||||
b.append(getSystem(), obj.getSystem());
|
b.append(getSystem(), obj.getSystem());
|
||||||
b.append(getValue(), obj.getValue());
|
b.append(getValue(), obj.getValue());
|
||||||
|
b.append(getHashSystem(), obj.getHashSystem());
|
||||||
|
b.append(getHashSystemAndValue(), obj.getHashSystemAndValue());
|
||||||
|
b.append(getHashValue(), obj.getHashValue());
|
||||||
return b.isEquals();
|
return b.isEquals();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getHashSystem() {
|
||||||
|
calculateHashes();
|
||||||
|
return myHashSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHashSystem(Long theHashSystem) {
|
||||||
|
myHashSystem = theHashSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getHashSystemAndValue() {
|
||||||
|
calculateHashes();
|
||||||
|
return myHashSystemAndValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHashSystemAndValue(Long theHashSystemAndValue) {
|
||||||
|
calculateHashes();
|
||||||
|
myHashSystemAndValue = theHashSystemAndValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getHashValue() {
|
||||||
|
calculateHashes();
|
||||||
|
return myHashValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHashValue(Long theHashValue) {
|
||||||
|
myHashValue = theHashValue;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Long getId() {
|
protected Long getId() {
|
||||||
return myId;
|
return myId;
|
||||||
|
@ -97,6 +169,7 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSystem(String theSystem) {
|
public void setSystem(String theSystem) {
|
||||||
|
clearHashes();
|
||||||
mySystem = StringUtils.defaultIfBlank(theSystem, null);
|
mySystem = StringUtils.defaultIfBlank(theSystem, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,6 +178,7 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(String theValue) {
|
public void setValue(String theValue) {
|
||||||
|
clearHashes();
|
||||||
myValue = StringUtils.defaultIfBlank(theValue, null);
|
myValue = StringUtils.defaultIfBlank(theValue, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,9 +189,13 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
b.append(getResource());
|
b.append(getResource());
|
||||||
b.append(getSystem());
|
b.append(getSystem());
|
||||||
b.append(getValue());
|
b.append(getValue());
|
||||||
|
b.append(getHashSystem());
|
||||||
|
b.append(getHashSystemAndValue());
|
||||||
|
b.append(getHashValue());
|
||||||
return b.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IQueryParameterType toQueryParameterType() {
|
public IQueryParameterType toQueryParameterType() {
|
||||||
return new TokenParam(getSystem(), getValue());
|
return new TokenParam(getSystem(), getValue());
|
||||||
|
|
|
@ -30,7 +30,6 @@ import org.hibernate.search.annotations.Field;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
|
|
||||||
//@formatter:off
|
|
||||||
@Embeddable
|
@Embeddable
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "HFJ_SPIDX_URI", indexes = {
|
@Table(name = "HFJ_SPIDX_URI", indexes = {
|
||||||
|
@ -39,7 +38,6 @@ import javax.persistence.*;
|
||||||
@Index(name = "IDX_SP_URI_UPDATED", columnList = "SP_UPDATED"),
|
@Index(name = "IDX_SP_URI_UPDATED", columnList = "SP_UPDATED"),
|
||||||
@Index(name = "IDX_SP_URI_COORDS", columnList = "RES_ID")
|
@Index(name = "IDX_SP_URI_COORDS", columnList = "RES_ID")
|
||||||
})
|
})
|
||||||
//@formatter:on
|
|
||||||
public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchParam {
|
public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchParam {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -56,15 +54,38 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_URI")
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_URI")
|
||||||
@Column(name = "SP_ID")
|
@Column(name = "SP_ID")
|
||||||
private Long myId;
|
private Long myId;
|
||||||
|
/**
|
||||||
|
* @since 3.4.0 - At some point this should be made not-null
|
||||||
|
*/
|
||||||
|
@Column(name = "HASH_URI", nullable = true)
|
||||||
|
private Long myHashUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public ResourceIndexedSearchParamUri() {
|
public ResourceIndexedSearchParamUri() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public ResourceIndexedSearchParamUri(String theName, String theUri) {
|
public ResourceIndexedSearchParamUri(String theName, String theUri) {
|
||||||
setParamName(theName);
|
setParamName(theName);
|
||||||
setUri(theUri);
|
setUri(theUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PrePersist
|
||||||
|
public void calculateHashes() {
|
||||||
|
if (myHashUri == null) {
|
||||||
|
setHashUri(hash(getResourceType(), getParamName(), getUri()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void clearHashes() {
|
||||||
|
myHashUri = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object theObj) {
|
public boolean equals(Object theObj) {
|
||||||
if (this == theObj) {
|
if (this == theObj) {
|
||||||
|
@ -81,9 +102,19 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
||||||
b.append(getParamName(), obj.getParamName());
|
b.append(getParamName(), obj.getParamName());
|
||||||
b.append(getResource(), obj.getResource());
|
b.append(getResource(), obj.getResource());
|
||||||
b.append(getUri(), obj.getUri());
|
b.append(getUri(), obj.getUri());
|
||||||
|
b.append(getHashUri(), obj.getHashUri());
|
||||||
return b.isEquals();
|
return b.isEquals();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getHashUri() {
|
||||||
|
calculateHashes();
|
||||||
|
return myHashUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHashUri(Long theHashUri) {
|
||||||
|
myHashUri = theHashUri;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Long getId() {
|
protected Long getId() {
|
||||||
return myId;
|
return myId;
|
||||||
|
@ -103,6 +134,7 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
||||||
b.append(getParamName());
|
b.append(getParamName());
|
||||||
b.append(getResource());
|
b.append(getResource());
|
||||||
b.append(getUri());
|
b.append(getUri());
|
||||||
|
b.append(getHashUri());
|
||||||
return b.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -434,8 +434,9 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
return myResourceType;
|
return myResourceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResourceType(String theResourceType) {
|
public ResourceTable setResourceType(String theResourceType) {
|
||||||
myResourceType = theResourceType;
|
myResourceType = theResourceType;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package ca.uhn.fhir.jpa.util;
|
||||||
|
|
||||||
|
public interface IReindexController {
|
||||||
|
void requestReindex();
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package ca.uhn.fhir.jpa.util;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
|
||||||
|
public class ReindexController implements IReindexController {
|
||||||
|
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(ReindexController.class);
|
||||||
|
private final Semaphore myReindexingLock = new Semaphore(1);
|
||||||
|
@Autowired
|
||||||
|
private DaoConfig myDaoConfig;
|
||||||
|
@Autowired
|
||||||
|
private IFhirSystemDao<?, ?> mySystemDao;
|
||||||
|
private Long myDontReindexUntil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called once per minute to perform any required re-indexing.
|
||||||
|
* <p>
|
||||||
|
* If nothing if found that requires reindexing, the query will not fire again for
|
||||||
|
* a longer amount of time.
|
||||||
|
* <p>
|
||||||
|
* During most passes this will just check and find that there are no resources
|
||||||
|
* requiring re-indexing. In that case the method just returns immediately.
|
||||||
|
* If the search finds that some resources require reindexing, the system will
|
||||||
|
* do a bunch of reindexing and then return.
|
||||||
|
*/
|
||||||
|
@Scheduled(fixedDelay = DateUtils.MILLIS_PER_MINUTE)
|
||||||
|
@Transactional(propagation = Propagation.NEVER)
|
||||||
|
public void performReindexingPass() {
|
||||||
|
if (myDaoConfig.isSchedulingDisabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (this) {
|
||||||
|
if (myDontReindexUntil == null && myDontReindexUntil > System.currentTimeMillis()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!myReindexingLock.tryAcquire()) {
|
||||||
|
ourLog.trace("Not going to reindex in parallel threads");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Integer count;
|
||||||
|
try {
|
||||||
|
count = mySystemDao.performReindexingPass(100);
|
||||||
|
|
||||||
|
for (int i = 0; i < 50 && count != null && count != 0; i++) {
|
||||||
|
count = mySystemDao.performReindexingPass(100);
|
||||||
|
try {
|
||||||
|
Thread.sleep(DateUtils.MILLIS_PER_SECOND);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
myReindexingLock.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (this) {
|
||||||
|
if (count == null) {
|
||||||
|
myDontReindexUntil = System.currentTimeMillis() + DateUtils.MILLIS_PER_HOUR;
|
||||||
|
} else {
|
||||||
|
myDontReindexUntil = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calling this will cause a reindex loop to be triggered sooner that it would otherwise
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void requestReindex() {
|
||||||
|
synchronized (this) {
|
||||||
|
myDontReindexUntil = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -133,6 +133,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
|
|
|
@ -115,6 +115,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
|
||||||
return dataSource;
|
return dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package ca.uhn.fhir.jpa.entity;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class ResourceIndexedSearchParamQuantityTest {
|
||||||
|
|
||||||
|
private ResourceIndexedSearchParamQuantity createParam(String theParamName, String theValue, String theSystem, String theUnits) {
|
||||||
|
ResourceIndexedSearchParamQuantity token = new ResourceIndexedSearchParamQuantity(theParamName, new BigDecimal(theValue), theSystem, theUnits);
|
||||||
|
token.setResource(new ResourceTable().setResourceType("Patient"));
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashFunctions() {
|
||||||
|
ResourceIndexedSearchParamQuantity token = createParam("NAME", "123.001", "value", "VALUE");
|
||||||
|
|
||||||
|
// Make sure our hashing function gives consistent results
|
||||||
|
assertEquals(945335027461836896L, token.getHashUnitsAndValPrefix().longValue());
|
||||||
|
assertEquals(5549105497508660145L, token.getHashValPrefix().longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValueTrimming() {
|
||||||
|
assertEquals(7265149425397186226L, createParam("NAME", "401.001", "value", "VALUE").getHashUnitsAndValPrefix().longValue());
|
||||||
|
assertEquals(7265149425397186226L, createParam("NAME", "401.99999", "value", "VALUE").getHashUnitsAndValPrefix().longValue());
|
||||||
|
assertEquals(7265149425397186226L, createParam("NAME", "401", "value", "VALUE").getHashUnitsAndValPrefix().longValue());
|
||||||
|
// Should be different
|
||||||
|
assertEquals(-8387917096585386046L, createParam("NAME", "400.9999999", "value", "VALUE").getHashUnitsAndValPrefix().longValue());
|
||||||
|
// Should be different
|
||||||
|
assertEquals(8819656626732693650L, createParam("NAME", "402.000000", "value", "VALUE").getHashUnitsAndValPrefix().longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package ca.uhn.fhir.jpa.entity;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class ResourceIndexedSearchParamStringTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashFunctions() {
|
||||||
|
ResourceIndexedSearchParamString token = new ResourceIndexedSearchParamString("NAME", "value", "VALUE");
|
||||||
|
token.setResource(new ResourceTable().setResourceType("Patient"));
|
||||||
|
|
||||||
|
// Make sure our hashing function gives consistent results
|
||||||
|
assertEquals(6598082761639188617L, token.getHashNormalizedPrefix().longValue());
|
||||||
|
assertEquals(-1970227166134682431L, token.getHashExact().longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashFunctionsPrefixOnly() {
|
||||||
|
ResourceIndexedSearchParamString token = new ResourceIndexedSearchParamString("NAME", "vZZZZZZZZZZZZZZZZ", "VZZZZZZzzzZzzzZ");
|
||||||
|
token.setResource(new ResourceTable().setResourceType("Patient"));
|
||||||
|
|
||||||
|
// Should be the same as in testHashFunctions()
|
||||||
|
assertEquals(6598082761639188617L, token.getHashNormalizedPrefix().longValue());
|
||||||
|
|
||||||
|
// Should be different from testHashFunctions()
|
||||||
|
assertEquals(-1970227166134682431L, token.getHashExact().longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package ca.uhn.fhir.jpa.entity;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class ResourceIndexedSearchParamTokenTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashFunctions() {
|
||||||
|
ResourceIndexedSearchParamToken token = new ResourceIndexedSearchParamToken("NAME", "SYSTEM", "VALUE");
|
||||||
|
token.setResource(new ResourceTable().setResourceType("Patient"));
|
||||||
|
|
||||||
|
// Make sure our hashing function gives consistent results
|
||||||
|
assertEquals(-8558989679010582575L, token.getHashSystem().longValue());
|
||||||
|
assertEquals(-8644532105141886455L, token.getHashSystemAndValue().longValue());
|
||||||
|
assertEquals(-1970227166134682431L, token.getHashValue().longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashFunctionsWithOverlapNames() {
|
||||||
|
ResourceIndexedSearchParamToken token = new ResourceIndexedSearchParamToken("NAME", "SYSTEM", "VALUE");
|
||||||
|
token.setResource(new ResourceTable().setResourceType("Patient"));
|
||||||
|
|
||||||
|
// Make sure our hashing function gives consistent results
|
||||||
|
assertEquals(-8558989679010582575L, token.getHashSystem().longValue());
|
||||||
|
assertEquals(-8644532105141886455L, token.getHashSystemAndValue().longValue());
|
||||||
|
assertEquals(-1970227166134682431L, token.getHashValue().longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package ca.uhn.fhir.jpa.entity;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class ResourceIndexedSearchParamUriTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashFunctions() {
|
||||||
|
ResourceIndexedSearchParamUri token = new ResourceIndexedSearchParamUri("NAME", "http://example.com");
|
||||||
|
token.setResource(new ResourceTable().setResourceType("Patient"));
|
||||||
|
|
||||||
|
// Make sure our hashing function gives consistent results
|
||||||
|
assertEquals(-6132951326739875838L, token.getHashUri().longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -57,6 +57,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
|
|
|
@ -58,6 +58,7 @@ public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
|
|
|
@ -53,6 +53,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
|
|
|
@ -90,6 +90,7 @@ public class TdlDstu2Config extends BaseJavaConfigDstu2 {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
|
|
|
@ -70,6 +70,7 @@ public class TdlDstu3Config extends BaseJavaConfigDstu3 {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
|
|
|
@ -92,6 +92,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
|
|
|
@ -91,6 +91,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
|
|
|
@ -86,6 +86,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Bean()
|
@Bean()
|
||||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||||
|
|
|
@ -119,7 +119,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
/**
|
/**
|
||||||
* This is configurable but by default we just use HAPI version
|
* This is configurable but by default we just use HAPI version
|
||||||
*/
|
*/
|
||||||
private String myServerVersion = VersionUtil.getVersion();
|
private String myServerVersion = createPoweredByHeaderProductVersion();
|
||||||
private boolean myStarted;
|
private boolean myStarted;
|
||||||
private Map<String, IResourceProvider> myTypeToProvider = new HashMap<>();
|
private Map<String, IResourceProvider> myTypeToProvider = new HashMap<>();
|
||||||
private boolean myUncompressIncomingContents = true;
|
private boolean myUncompressIncomingContents = true;
|
||||||
|
@ -159,7 +159,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
*/
|
*/
|
||||||
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
||||||
String b = createPoweredByHeader();
|
String b = createPoweredByHeader();
|
||||||
theHttpResponse.addHeader("X-Powered-By", b);
|
theHttpResponse.addHeader(Constants.POWERED_BY_HEADER, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addLocationHeader(RequestDetails theRequest, HttpServletResponse theResponse, MethodOutcome response, String headerLocation, String resourceName) {
|
private void addLocationHeader(RequestDetails theRequest, HttpServletResponse theResponse, MethodOutcome response, String headerLocation, String resourceName) {
|
||||||
|
@ -213,12 +213,21 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
return Lists.newArrayList("FHIR Server", "FHIR " + myFhirContext.getVersion().getVersion().getFhirVersionString() + "/" + myFhirContext.getVersion().getVersion().name());
|
return Lists.newArrayList("FHIR Server", "FHIR " + myFhirContext.getVersion().getVersion().getFhirVersionString() + "/" + myFhirContext.getVersion().getVersion().name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses may override to provide their own powered by
|
||||||
|
* header. Note that if you want to be nice and still credit HAPI
|
||||||
|
* FHIR you could consider overriding
|
||||||
|
* {@link #createPoweredByAttributes()} instead and adding your own
|
||||||
|
* fragments to the list.
|
||||||
|
*/
|
||||||
protected String createPoweredByHeader() {
|
protected String createPoweredByHeader() {
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
b.append(createPoweredByHeaderProductName());
|
b.append(createPoweredByHeaderProductName());
|
||||||
b.append(" ");
|
b.append(" ");
|
||||||
b.append(VersionUtil.getVersion());
|
b.append(createPoweredByHeaderProductVersion());
|
||||||
b.append(" REST Server (");
|
b.append(" ");
|
||||||
|
b.append(createPoweredByHeaderComponentName());
|
||||||
|
b.append(" (");
|
||||||
|
|
||||||
List<String> poweredByAttributes = createPoweredByAttributes();
|
List<String> poweredByAttributes = createPoweredByAttributes();
|
||||||
for (ListIterator<String> iter = poweredByAttributes.listIterator(); iter.hasNext(); ) {
|
for (ListIterator<String> iter = poweredByAttributes.listIterator(); iter.hasNext(); ) {
|
||||||
|
@ -232,10 +241,33 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
return b.toString();
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses my override
|
||||||
|
*
|
||||||
|
* @see #createPoweredByHeader()
|
||||||
|
*/
|
||||||
|
protected String createPoweredByHeaderComponentName() {
|
||||||
|
return "REST Server";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses my override
|
||||||
|
*
|
||||||
|
* @see #createPoweredByHeader()
|
||||||
|
*/
|
||||||
protected String createPoweredByHeaderProductName() {
|
protected String createPoweredByHeaderProductName() {
|
||||||
return "HAPI FHIR";
|
return "HAPI FHIR";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses my override
|
||||||
|
*
|
||||||
|
* @see #createPoweredByHeader()
|
||||||
|
*/
|
||||||
|
protected String createPoweredByHeaderProductVersion() {
|
||||||
|
return VersionUtil.getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
if (getResourceProviders() != null) {
|
if (getResourceProviders() != null) {
|
||||||
|
@ -539,10 +571,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
*
|
*
|
||||||
* @param theList The list of interceptors (may be null)
|
* @param theList The list of interceptors (may be null)
|
||||||
*/
|
*/
|
||||||
public void setInterceptors(IServerInterceptor... theList) {
|
public void setInterceptors(List<IServerInterceptor> theList) {
|
||||||
myInterceptors.clear();
|
myInterceptors.clear();
|
||||||
if (theList != null) {
|
if (theList != null) {
|
||||||
myInterceptors.addAll(Arrays.asList(theList));
|
myInterceptors.addAll(theList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,11 +604,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
*
|
*
|
||||||
* @see #setResourceProviders(Collection)
|
* @see #setResourceProviders(Collection)
|
||||||
*/
|
*/
|
||||||
public void setPlainProviders(Collection<Object> theProviders) {
|
public void setPlainProviders(Object... theProv) {
|
||||||
myPlainProviders.clear();
|
setPlainProviders(Arrays.asList(theProv));
|
||||||
if (theProviders != null) {
|
|
||||||
myPlainProviders.addAll(theProviders);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -606,10 +635,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
/**
|
/**
|
||||||
* Sets the resource providers for this server
|
* Sets the resource providers for this server
|
||||||
*/
|
*/
|
||||||
public void setResourceProviders(Collection<IResourceProvider> theResourceProviders) {
|
public void setResourceProviders(IResourceProvider... theResourceProviders) {
|
||||||
myResourceProviders.clear();
|
myResourceProviders.clear();
|
||||||
if (theResourceProviders != null) {
|
if (theResourceProviders != null) {
|
||||||
myResourceProviders.addAll(theResourceProviders);
|
myResourceProviders.addAll(Arrays.asList(theResourceProviders));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,8 +661,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the server base URL (with no trailing '/') for a given request
|
* Returns the server base URL (with no trailing '/') for a given request
|
||||||
*
|
|
||||||
* @param theRequest
|
|
||||||
*/
|
*/
|
||||||
public String getServerBaseForRequest(ServletRequestDetails theRequest) {
|
public String getServerBaseForRequest(ServletRequestDetails theRequest) {
|
||||||
String fhirServerBase;
|
String fhirServerBase;
|
||||||
|
@ -694,9 +721,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
// passing the server into the constructor. Having that sort
|
// passing the server into the constructor. Having that sort
|
||||||
// of cross linkage causes reference cycles in Spring wiring
|
// of cross linkage causes reference cycles in Spring wiring
|
||||||
try {
|
try {
|
||||||
Method setRestfulServer = theServerConformanceProvider.getClass().getMethod("setRestfulServer", new Class[] {RestfulServer.class});
|
Method setRestfulServer = theServerConformanceProvider.getClass().getMethod("setRestfulServer", RestfulServer.class);
|
||||||
if (setRestfulServer != null) {
|
if (setRestfulServer != null) {
|
||||||
setRestfulServer.invoke(theServerConformanceProvider, new Object[] {this});
|
setRestfulServer.invoke(theServerConformanceProvider, this);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ourLog.warn("Error calling IServerConformanceProvider.setRestfulServer", e);
|
ourLog.warn("Error calling IServerConformanceProvider.setRestfulServer", e);
|
||||||
|
@ -1011,9 +1038,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
invokeInitialize(iResourceProvider);
|
invokeInitialize(iResourceProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (confProvider != null) {
|
|
||||||
invokeInitialize(confProvider);
|
invokeInitialize(confProvider);
|
||||||
}
|
|
||||||
if (getPlainProviders() != null) {
|
if (getPlainProviders() != null) {
|
||||||
for (Object next : getPlainProviders()) {
|
for (Object next : getPlainProviders()) {
|
||||||
invokeInitialize(next);
|
invokeInitialize(next);
|
||||||
|
@ -1300,7 +1326,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
*
|
*
|
||||||
* @param providers a {@code Collection} of providers. The parameter could be null or an empty {@code Collection}
|
* @param providers a {@code Collection} of providers. The parameter could be null or an empty {@code Collection}
|
||||||
*/
|
*/
|
||||||
public void registerProviders(Collection<? extends Object> providers) {
|
public void registerProviders(Collection<?> providers) {
|
||||||
myProviderRegistrationMutex.lock();
|
myProviderRegistrationMutex.lock();
|
||||||
try {
|
try {
|
||||||
if (!myStarted) {
|
if (!myStarted) {
|
||||||
|
@ -1323,9 +1349,9 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
/*
|
/*
|
||||||
* Inner method to actually register providers
|
* Inner method to actually register providers
|
||||||
*/
|
*/
|
||||||
protected void registerProviders(Collection<? extends Object> providers, boolean inInit) {
|
protected void registerProviders(Collection<?> providers, boolean inInit) {
|
||||||
List<IResourceProvider> newResourceProviders = new ArrayList<IResourceProvider>();
|
List<IResourceProvider> newResourceProviders = new ArrayList<>();
|
||||||
List<Object> newPlainProviders = new ArrayList<Object>();
|
List<Object> newPlainProviders = new ArrayList<>();
|
||||||
ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext());
|
ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext());
|
||||||
|
|
||||||
if (providers != null) {
|
if (providers != null) {
|
||||||
|
@ -1392,7 +1418,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
ourLog.info("Removing RESTful methods for: {}", theProvider.getClass());
|
ourLog.info("Removing RESTful methods for: {}", theProvider.getClass());
|
||||||
Class<?> clazz = theProvider.getClass();
|
Class<?> clazz = theProvider.getClass();
|
||||||
Class<?> supertype = clazz.getSuperclass();
|
Class<?> supertype = clazz.getSuperclass();
|
||||||
Collection<String> resourceNames = new ArrayList<String>();
|
Collection<String> resourceNames = new ArrayList<>();
|
||||||
while (!Object.class.equals(supertype)) {
|
while (!Object.class.equals(supertype)) {
|
||||||
removeResourceMethods(theProvider, supertype, resourceNames);
|
removeResourceMethods(theProvider, supertype, resourceNames);
|
||||||
supertype = supertype.getSuperclass();
|
supertype = supertype.getSuperclass();
|
||||||
|
@ -1468,12 +1494,18 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
case OPTIONS:
|
case OPTIONS:
|
||||||
doOptions(theReq, theResp);
|
doOptions(theReq, theResp);
|
||||||
break;
|
break;
|
||||||
|
case PATCH:
|
||||||
|
break;
|
||||||
case POST:
|
case POST:
|
||||||
doPost(theReq, theResp);
|
doPost(theReq, theResp);
|
||||||
break;
|
break;
|
||||||
case PUT:
|
case PUT:
|
||||||
doPut(theReq, theResp);
|
doPut(theReq, theResp);
|
||||||
break;
|
break;
|
||||||
|
case TRACE:
|
||||||
|
case TRACK:
|
||||||
|
case HEAD:
|
||||||
|
case CONNECT:
|
||||||
default:
|
default:
|
||||||
handleRequest(method, theReq, theResp);
|
handleRequest(method, theReq, theResp);
|
||||||
break;
|
break;
|
||||||
|
@ -1485,10 +1517,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
*
|
*
|
||||||
* @param theList The list of interceptors (may be null)
|
* @param theList The list of interceptors (may be null)
|
||||||
*/
|
*/
|
||||||
public void setInterceptors(List<IServerInterceptor> theList) {
|
public void setInterceptors(IServerInterceptor... theList) {
|
||||||
myInterceptors.clear();
|
myInterceptors.clear();
|
||||||
if (theList != null) {
|
if (theList != null) {
|
||||||
myInterceptors.addAll(theList);
|
myInterceptors.addAll(Arrays.asList(theList));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1497,8 +1529,11 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
*
|
*
|
||||||
* @see #setResourceProviders(Collection)
|
* @see #setResourceProviders(Collection)
|
||||||
*/
|
*/
|
||||||
public void setPlainProviders(Object... theProv) {
|
public void setPlainProviders(Collection<Object> theProviders) {
|
||||||
setPlainProviders(Arrays.asList(theProv));
|
myPlainProviders.clear();
|
||||||
|
if (theProviders != null) {
|
||||||
|
myPlainProviders.addAll(theProviders);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1516,10 +1551,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
/**
|
/**
|
||||||
* Sets the resource providers for this server
|
* Sets the resource providers for this server
|
||||||
*/
|
*/
|
||||||
public void setResourceProviders(IResourceProvider... theResourceProviders) {
|
public void setResourceProviders(Collection<IResourceProvider> theResourceProviders) {
|
||||||
myResourceProviders.clear();
|
myResourceProviders.clear();
|
||||||
if (theResourceProviders != null) {
|
if (theResourceProviders != null) {
|
||||||
myResourceProviders.addAll(Arrays.asList(theResourceProviders));
|
myResourceProviders.addAll(theResourceProviders);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1539,13 +1574,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregister one provider (either a Resource provider or a plain provider)
|
* Unregister one provider (either a Resource provider or a plain provider)
|
||||||
*
|
|
||||||
* @param provider
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public void unregisterProvider(Object provider) throws Exception {
|
public void unregisterProvider(Object provider) {
|
||||||
if (provider != null) {
|
if (provider != null) {
|
||||||
Collection<Object> providerList = new ArrayList<Object>(1);
|
Collection<Object> providerList = new ArrayList<>(1);
|
||||||
providerList.add(provider);
|
providerList.add(provider);
|
||||||
unregisterProviders(providerList);
|
unregisterProviders(providerList);
|
||||||
}
|
}
|
||||||
|
@ -1553,11 +1585,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregister a {@code Collection} of providers
|
* Unregister a {@code Collection} of providers
|
||||||
*
|
|
||||||
* @param providers
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public void unregisterProviders(Collection<? extends Object> providers) throws Exception {
|
public void unregisterProviders(Collection<?> providers) {
|
||||||
ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext());
|
ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext());
|
||||||
if (providers != null) {
|
if (providers != null) {
|
||||||
for (Object provider : providers) {
|
for (Object provider : providers) {
|
||||||
|
|
|
@ -128,9 +128,7 @@ public class SearchR4Test {
|
||||||
@Test
|
@Test
|
||||||
public void testIncludeSingleParameter() throws Exception {
|
public void testIncludeSingleParameter() throws Exception {
|
||||||
HttpGet httpGet;
|
HttpGet httpGet;
|
||||||
String linkNext;
|
|
||||||
Bundle bundle;
|
Bundle bundle;
|
||||||
String linkSelf;
|
|
||||||
|
|
||||||
// No include specified
|
// No include specified
|
||||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationRequest");
|
httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationRequest");
|
||||||
|
|
|
@ -17,6 +17,20 @@
|
||||||
</ul>
|
</ul>
|
||||||
]]>
|
]]>
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Several enhancements have been made to the JPA server index
|
||||||
|
tables. These enhancements consist of new colums that will be
|
||||||
|
used in a future version of HAPI FHIR to significantly decrease
|
||||||
|
the amount of space required for indexes on token and string index
|
||||||
|
types.
|
||||||
|
<![CDATA[<br/><br/>]]>
|
||||||
|
These new columns are not yet used in HAPI FHIR 3.4.0 but will be
|
||||||
|
enabled in HAPI FHIR 3.5.0. Anyone upgrading to HAPI FHIR 3.4.0 (or above)
|
||||||
|
is recommended to invoke the following SQL statement on their
|
||||||
|
database in order to reindex all data in a background job:
|
||||||
|
<![CDATA[<br/>]]>
|
||||||
|
<![CDATA[<code>update HFJ_RESOURCE set SP_INDEX_STATUS = null;</code>]]>
|
||||||
|
</action>
|
||||||
<action type="add">
|
<action type="add">
|
||||||
R4 structures have been updated to the latest definitions
|
R4 structures have been updated to the latest definitions
|
||||||
(SVN 13732)
|
(SVN 13732)
|
||||||
|
@ -226,6 +240,11 @@
|
||||||
An issue in the narrative generator template for the CodeableConcept
|
An issue in the narrative generator template for the CodeableConcept
|
||||||
datatype was corrected. Thanks to @RuthAlk for the pull request!
|
datatype was corrected. Thanks to @RuthAlk for the pull request!
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
The JPA server automatic reindexing process has been tweaked so that it no
|
||||||
|
longer runs once per minute (this was a heavy strain on large databases)
|
||||||
|
but will instead run once an hour unless triggered for some reason.
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="3.3.0" date="2018-03-29">
|
<release version="3.3.0" date="2018-03-29">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
Loading…
Reference in New Issue