Page JPA search results to the database
This commit is contained in:
parent
8de41ca8b4
commit
3bfcb57783
|
@ -38,6 +38,8 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
|
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvc;
|
||||||
import ca.uhn.fhir.jpa.term.ITerminologySvc;
|
import ca.uhn.fhir.jpa.term.ITerminologySvc;
|
||||||
import ca.uhn.fhir.jpa.term.TerminologySvcImpl;
|
import ca.uhn.fhir.jpa.term.TerminologySvcImpl;
|
||||||
|
|
||||||
|
@ -62,6 +64,11 @@ public class BaseConfig implements SchedulingConfigurer {
|
||||||
theTaskRegistrar.setTaskScheduler(taskScheduler());
|
theTaskRegistrar.setTaskScheduler(taskScheduler());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean(autowire = Autowire.BY_TYPE)
|
||||||
|
public DatabaseBackedPagingProvider databaseBackedPagingProvider() {
|
||||||
|
return new DatabaseBackedPagingProvider(10);
|
||||||
|
}
|
||||||
|
|
||||||
@Bean(name = "myFhirContextDstu1")
|
@Bean(name = "myFhirContextDstu1")
|
||||||
@Lazy
|
@Lazy
|
||||||
public FhirContext fhirContextDstu1() {
|
public FhirContext fhirContextDstu1() {
|
||||||
|
@ -99,8 +106,8 @@ public class BaseConfig implements SchedulingConfigurer {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(autowire=Autowire.BY_TYPE)
|
@Bean(autowire=Autowire.BY_TYPE)
|
||||||
public ITerminologySvc terminologyService() {
|
public StaleSearchDeletingSvc staleSearchDeletingSvc() {
|
||||||
return new TerminologySvcImpl();
|
return new StaleSearchDeletingSvc();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -110,12 +117,9 @@ public class BaseConfig implements SchedulingConfigurer {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Bean(autowire = Autowire.BY_TYPE)
|
||||||
* This lets the "@Value" fields reference properties from the properties file
|
public ITerminologySvc terminologyService() {
|
||||||
*/
|
return new TerminologySvcImpl();
|
||||||
@Bean
|
|
||||||
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
|
|
||||||
return new PropertySourcesPlaceholderConfigurer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @PostConstruct
|
// @PostConstruct
|
||||||
|
@ -127,4 +131,12 @@ public class BaseConfig implements SchedulingConfigurer {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This lets the "@Value" fields reference properties from the properties file
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
|
||||||
|
return new PropertySourcesPlaceholderConfigurer();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,9 @@ import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.FhirSearchDao;
|
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchDao;
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@ -42,6 +42,18 @@ public class BaseDstu2Config extends BaseConfig {
|
||||||
return fhirContextDstu2();
|
return fhirContextDstu2();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean(name = "myJpaValidationSupportDstu2", autowire = Autowire.BY_NAME)
|
||||||
|
public ca.uhn.fhir.jpa.dao.IJpaValidationSupportDstu2 jpaValidationSupportDstu2() {
|
||||||
|
ca.uhn.fhir.jpa.dao.JpaValidationSupportDstu2 retVal = new ca.uhn.fhir.jpa.dao.JpaValidationSupportDstu2();
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean(autowire = Autowire.BY_TYPE)
|
||||||
|
public IFulltextSearchSvc searchDao() {
|
||||||
|
FulltextSearchSvcImpl searchDao = new FulltextSearchSvcImpl();
|
||||||
|
return searchDao;
|
||||||
|
}
|
||||||
|
|
||||||
@Bean(name = "mySystemDaoDstu2", autowire = Autowire.BY_NAME)
|
@Bean(name = "mySystemDaoDstu2", autowire = Autowire.BY_NAME)
|
||||||
public IFhirSystemDao<ca.uhn.fhir.model.dstu2.resource.Bundle, MetaDt> systemDaoDstu2() {
|
public IFhirSystemDao<ca.uhn.fhir.model.dstu2.resource.Bundle, MetaDt> systemDaoDstu2() {
|
||||||
ca.uhn.fhir.jpa.dao.FhirSystemDaoDstu2 retVal = new ca.uhn.fhir.jpa.dao.FhirSystemDaoDstu2();
|
ca.uhn.fhir.jpa.dao.FhirSystemDaoDstu2 retVal = new ca.uhn.fhir.jpa.dao.FhirSystemDaoDstu2();
|
||||||
|
@ -54,17 +66,4 @@ public class BaseDstu2Config extends BaseConfig {
|
||||||
retVal.setDao(systemDaoDstu2());
|
retVal.setDao(systemDaoDstu2());
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "myJpaValidationSupportDstu2", autowire = Autowire.BY_NAME)
|
|
||||||
public ca.uhn.fhir.jpa.dao.IJpaValidationSupportDstu2 jpaValidationSupportDstu2() {
|
|
||||||
ca.uhn.fhir.jpa.dao.JpaValidationSupportDstu2 retVal = new ca.uhn.fhir.jpa.dao.JpaValidationSupportDstu2();
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(autowire = Autowire.BY_TYPE)
|
|
||||||
public ISearchDao searchDao() {
|
|
||||||
FhirSearchDao searchDao = new FhirSearchDao();
|
|
||||||
return searchDao;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,9 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.config.BaseConfig;
|
import ca.uhn.fhir.jpa.config.BaseConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.FhirSearchDao;
|
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchDao;
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
|
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
|
||||||
import ca.uhn.fhir.validation.IValidatorModule;
|
import ca.uhn.fhir.validation.IValidatorModule;
|
||||||
|
|
||||||
|
@ -50,6 +50,35 @@ public class BaseDstu3Config extends BaseConfig {
|
||||||
return fhirContextDstu3();
|
return fhirContextDstu3();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean(name="myInstanceValidatorDstu3")
|
||||||
|
@Lazy
|
||||||
|
public IValidatorModule instanceValidatorDstu3() {
|
||||||
|
FhirInstanceValidator val = new FhirInstanceValidator();
|
||||||
|
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
|
||||||
|
val.setValidationSupport(validationSupportChainDstu3());
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean(name = "myJpaValidationSupportDstu3", autowire = Autowire.BY_NAME)
|
||||||
|
public ca.uhn.fhir.jpa.dao.dstu3.IJpaValidationSupportDstu3 jpaValidationSupportDstu3() {
|
||||||
|
ca.uhn.fhir.jpa.dao.dstu3.JpaValidationSupportDstu3 retVal = new ca.uhn.fhir.jpa.dao.dstu3.JpaValidationSupportDstu3();
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean(name="myQuestionnaireResponseValidatorDstu3")
|
||||||
|
@Lazy
|
||||||
|
public IValidatorModule questionnaireResponseValidatorDstu3() {
|
||||||
|
FhirQuestionnaireResponseValidator module = new FhirQuestionnaireResponseValidator();
|
||||||
|
module.setValidationSupport(validationSupportChainDstu3());
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean(autowire = Autowire.BY_TYPE)
|
||||||
|
public IFulltextSearchSvc searchDaoDstu3() {
|
||||||
|
FulltextSearchSvcImpl searchDao = new FulltextSearchSvcImpl();
|
||||||
|
return searchDao;
|
||||||
|
}
|
||||||
|
|
||||||
@Bean(name = "mySystemDaoDstu3", autowire = Autowire.BY_NAME)
|
@Bean(name = "mySystemDaoDstu3", autowire = Autowire.BY_NAME)
|
||||||
public IFhirSystemDao<org.hl7.fhir.dstu3.model.Bundle, org.hl7.fhir.dstu3.model.Meta> systemDaoDstu3() {
|
public IFhirSystemDao<org.hl7.fhir.dstu3.model.Bundle, org.hl7.fhir.dstu3.model.Meta> systemDaoDstu3() {
|
||||||
ca.uhn.fhir.jpa.dao.dstu3.FhirSystemDaoDstu3 retVal = new ca.uhn.fhir.jpa.dao.dstu3.FhirSystemDaoDstu3();
|
ca.uhn.fhir.jpa.dao.dstu3.FhirSystemDaoDstu3 retVal = new ca.uhn.fhir.jpa.dao.dstu3.FhirSystemDaoDstu3();
|
||||||
|
@ -63,35 +92,6 @@ public class BaseDstu3Config extends BaseConfig {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name = "myJpaValidationSupportDstu3", autowire = Autowire.BY_NAME)
|
|
||||||
public ca.uhn.fhir.jpa.dao.dstu3.IJpaValidationSupportDstu3 jpaValidationSupportDstu3() {
|
|
||||||
ca.uhn.fhir.jpa.dao.dstu3.JpaValidationSupportDstu3 retVal = new ca.uhn.fhir.jpa.dao.dstu3.JpaValidationSupportDstu3();
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(autowire = Autowire.BY_TYPE)
|
|
||||||
public ISearchDao searchDaoDstu3() {
|
|
||||||
FhirSearchDao searchDao = new FhirSearchDao();
|
|
||||||
return searchDao;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(name="myInstanceValidatorDstu3")
|
|
||||||
@Lazy
|
|
||||||
public IValidatorModule instanceValidatorDstu3() {
|
|
||||||
FhirInstanceValidator val = new FhirInstanceValidator();
|
|
||||||
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
|
|
||||||
val.setValidationSupport(validationSupportChainDstu3());
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(name="myQuestionnaireResponseValidatorDstu3")
|
|
||||||
@Lazy
|
|
||||||
public IValidatorModule questionnaireResponseValidatorDstu3() {
|
|
||||||
FhirQuestionnaireResponseValidator module = new FhirQuestionnaireResponseValidator();
|
|
||||||
module.setValidationSupport(validationSupportChainDstu3());
|
|
||||||
return module;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean(autowire=Autowire.BY_NAME, name="myJpaValidationSupportChainDstu3")
|
@Bean(autowire=Autowire.BY_NAME, name="myJpaValidationSupportChainDstu3")
|
||||||
public IValidationSupport validationSupportChainDstu3() {
|
public IValidationSupport validationSupportChainDstu3() {
|
||||||
return new JpaValidationSupportChainDstu3();
|
return new JpaValidationSupportChainDstu3();
|
||||||
|
|
|
@ -518,7 +518,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected <R extends IBaseResource> IFhirResourceDao<R> getDao(Class<R> theType) {
|
public <R extends IBaseResource> IFhirResourceDao<R> getDao(Class<R> theType) {
|
||||||
if (myResourceTypeToDao == null) {
|
if (myResourceTypeToDao == null) {
|
||||||
myResourceTypeToDao = new HashMap<Class<? extends IBaseResource>, IFhirResourceDao<?>>();
|
myResourceTypeToDao = new HashMap<Class<? extends IBaseResource>, IFhirResourceDao<?>>();
|
||||||
for (IFhirResourceDao<?> next : myResourceDaos) {
|
for (IFhirResourceDao<?> next : myResourceDaos) {
|
||||||
|
@ -959,6 +959,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType);
|
RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType);
|
||||||
|
|
||||||
SearchParameterMap paramMap = translateMatchUrl(theMatchUrl, resourceDef);
|
SearchParameterMap paramMap = translateMatchUrl(theMatchUrl, resourceDef);
|
||||||
|
paramMap.setPersistResults(false);
|
||||||
|
|
||||||
if (paramMap.isEmpty()) {
|
if (paramMap.isEmpty()) {
|
||||||
throw new InvalidRequestException("Invalid match URL[" + theMatchUrl + "] - URL has no search parameters");
|
throw new InvalidRequestException("Invalid match URL[" + theMatchUrl + "] - URL has no search parameters");
|
||||||
|
|
|
@ -105,7 +105,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
|
|
||||||
private Class<T> myResourceType;
|
private Class<T> myResourceType;
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
protected ISearchDao mySearchDao;
|
protected IFulltextSearchSvc mySearchDao;
|
||||||
@Autowired()
|
@Autowired()
|
||||||
protected ISearchResultDao mySearchResultDao;
|
protected ISearchResultDao mySearchResultDao;
|
||||||
@Autowired()
|
@Autowired()
|
||||||
|
|
|
@ -40,6 +40,11 @@ public class DaoConfig {
|
||||||
private boolean myAllowInlineMatchUrlReferences = false;
|
private boolean myAllowInlineMatchUrlReferences = false;
|
||||||
|
|
||||||
private boolean myAllowMultipleDelete;
|
private boolean myAllowMultipleDelete;
|
||||||
|
|
||||||
|
// ***
|
||||||
|
// update setter javadoc if default changes
|
||||||
|
// ***
|
||||||
|
private long myExpireSearchResultsAfterMillis = DateUtils.MILLIS_PER_HOUR;
|
||||||
private int myHardSearchLimit = 1000;
|
private int myHardSearchLimit = 1000;
|
||||||
private int myHardTagListLimit = 1000;
|
private int myHardTagListLimit = 1000;
|
||||||
private int myIncludeLimit = 2000;
|
private int myIncludeLimit = 2000;
|
||||||
|
@ -50,11 +55,22 @@ public class DaoConfig {
|
||||||
private boolean myIndexContainedResources = true;
|
private boolean myIndexContainedResources = true;
|
||||||
|
|
||||||
private List<IServerInterceptor> myInterceptors;
|
private List<IServerInterceptor> myInterceptors;
|
||||||
|
|
||||||
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
|
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
|
||||||
|
|
||||||
private boolean mySchedulingDisabled;
|
private boolean mySchedulingDisabled;
|
||||||
private boolean mySubscriptionEnabled;
|
private boolean mySubscriptionEnabled;
|
||||||
private long mySubscriptionPollDelay = 1000;
|
private long mySubscriptionPollDelay = 1000;
|
||||||
private Long mySubscriptionPurgeInactiveAfterMillis;
|
private Long mySubscriptionPurgeInactiveAfterMillis;
|
||||||
|
/**
|
||||||
|
* Search results are stored in the database so that they can be paged through. After this
|
||||||
|
* number of milliseconds, they will be deleted from the database. Defaults to 1 hour.
|
||||||
|
*
|
||||||
|
* @since 1.5
|
||||||
|
*/
|
||||||
|
public long getExpireSearchResultsAfterMillis() {
|
||||||
|
return myExpireSearchResultsAfterMillis;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* See {@link #setIncludeLimit(int)}
|
* See {@link #setIncludeLimit(int)}
|
||||||
*/
|
*/
|
||||||
|
@ -78,7 +94,6 @@ public class DaoConfig {
|
||||||
}
|
}
|
||||||
return myInterceptors;
|
return myInterceptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceEncodingEnum getResourceEncoding() {
|
public ResourceEncodingEnum getResourceEncoding() {
|
||||||
return myResourceEncoding;
|
return myResourceEncoding;
|
||||||
}
|
}
|
||||||
|
@ -139,6 +154,16 @@ public class DaoConfig {
|
||||||
myAllowMultipleDelete = theAllowMultipleDelete;
|
myAllowMultipleDelete = theAllowMultipleDelete;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search results are stored in the database so that they can be paged through. After this
|
||||||
|
* number of milliseconds, they will be deleted from the database. Defaults to 1 hour.
|
||||||
|
*
|
||||||
|
* @since 1.5
|
||||||
|
*/
|
||||||
|
public void setExpireSearchResultsAfterMillis(long theExpireSearchResultsAfterMillis) {
|
||||||
|
myExpireSearchResultsAfterMillis = theExpireSearchResultsAfterMillis;
|
||||||
|
}
|
||||||
|
|
||||||
public void setHardSearchLimit(int theHardSearchLimit) {
|
public void setHardSearchLimit(int theHardSearchLimit) {
|
||||||
myHardSearchLimit = theHardSearchLimit;
|
myHardSearchLimit = theHardSearchLimit;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,8 +61,8 @@ import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
public class FhirSearchDao extends BaseHapiFhirDao<IBaseResource> implements ISearchDao {
|
public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implements IFulltextSearchSvc {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSearchDao.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FulltextSearchSvcImpl.class);
|
||||||
|
|
||||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||||
private EntityManager myEntityManager;
|
private EntityManager myEntityManager;
|
|
@ -23,6 +23,8 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.TagList;
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
|
@ -41,6 +43,8 @@ public interface IFhirSystemDao<T, MT> extends IDao {
|
||||||
|
|
||||||
TagList getAllTags(RequestDetails theRequestDetails);
|
TagList getAllTags(RequestDetails theRequestDetails);
|
||||||
|
|
||||||
|
public <R extends IBaseResource> IFhirResourceDao<R> getDao(Class<R> theType);
|
||||||
|
|
||||||
Map<String, Long> getResourceCounts();
|
Map<String, Long> getResourceCounts();
|
||||||
|
|
||||||
IBundleProvider history(Date theDate, RequestDetails theRequestDetails);
|
IBundleProvider history(Date theDate, RequestDetails theRequestDetails);
|
||||||
|
|
|
@ -22,9 +22,9 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.FhirSearchDao.Suggestion;
|
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion;
|
||||||
|
|
||||||
public interface ISearchDao {
|
public interface IFulltextSearchSvc {
|
||||||
|
|
||||||
List<Suggestion> suggestKeywords(String theContext, String theSearchParam, String theText);
|
List<Suggestion> suggestKeywords(String theContext, String theSearchParam, String theText);
|
||||||
|
|
|
@ -42,6 +42,7 @@ import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.NoResultException;
|
||||||
import javax.persistence.Tuple;
|
import javax.persistence.Tuple;
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
@ -61,6 +62,9 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
import org.springframework.transaction.TransactionStatus;
|
import org.springframework.transaction.TransactionStatus;
|
||||||
import org.springframework.transaction.support.TransactionCallback;
|
import org.springframework.transaction.support.TransactionCallback;
|
||||||
|
@ -87,6 +91,8 @@ import ca.uhn.fhir.jpa.entity.ResourceLink;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceTag;
|
import ca.uhn.fhir.jpa.entity.ResourceTag;
|
||||||
import ca.uhn.fhir.jpa.entity.Search;
|
import ca.uhn.fhir.jpa.entity.Search;
|
||||||
|
import ca.uhn.fhir.jpa.entity.SearchInclude;
|
||||||
|
import ca.uhn.fhir.jpa.entity.SearchResult;
|
||||||
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
||||||
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
|
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
|
||||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||||
|
@ -129,20 +135,18 @@ public class SearchBuilder {
|
||||||
private BaseHapiFhirDao<?> myCallingDao;
|
private BaseHapiFhirDao<?> myCallingDao;
|
||||||
private FhirContext myContext;
|
private FhirContext myContext;
|
||||||
private EntityManager myEntityManager;
|
private EntityManager myEntityManager;
|
||||||
|
private SearchParameterMap myParams;
|
||||||
|
private Collection<Long> myPids;
|
||||||
private PlatformTransactionManager myPlatformTransactionManager;
|
private PlatformTransactionManager myPlatformTransactionManager;
|
||||||
|
private IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
|
||||||
private String myResourceName;
|
private String myResourceName;
|
||||||
private Class<? extends IBaseResource> myResourceType;
|
private Class<? extends IBaseResource> myResourceType;
|
||||||
private ISearchDao mySearchDao;
|
private IFulltextSearchSvc mySearchDao;
|
||||||
private ISearchResultDao mySearchResultDao;
|
|
||||||
private IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
|
|
||||||
|
|
||||||
private Search mySearchEntity;
|
private Search mySearchEntity;
|
||||||
|
private ISearchResultDao mySearchResultDao;
|
||||||
|
|
||||||
private boolean myHaveFlushedSearch;
|
public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager, PlatformTransactionManager thePlatformTransactionManager, IFulltextSearchSvc theSearchDao,
|
||||||
|
ISearchResultDao theSearchResultDao, BaseHapiFhirDao theDao, IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao) {
|
||||||
private SearchParameterMap myParams;
|
|
||||||
|
|
||||||
public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager, PlatformTransactionManager thePlatformTransactionManager, ISearchDao theSearchDao, ISearchResultDao theSearchResultDao, BaseHapiFhirDao theDao, IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao) {
|
|
||||||
myContext = theFhirContext;
|
myContext = theFhirContext;
|
||||||
myEntityManager = theEntityManager;
|
myEntityManager = theEntityManager;
|
||||||
myPlatformTransactionManager = thePlatformTransactionManager;
|
myPlatformTransactionManager = thePlatformTransactionManager;
|
||||||
|
@ -177,11 +181,11 @@ public class SearchBuilder {
|
||||||
IQueryParameterType rightValue = cp.getRightValue();
|
IQueryParameterType rightValue = cp.getRightValue();
|
||||||
predicates.add(createCompositeParamPart(builder, from, right, rightValue));
|
predicates.add(createCompositeParamPart(builder, from, right, rightValue));
|
||||||
|
|
||||||
doCreateIdPredicate(predicates, from.get("myId").as(Long.class));
|
doCreateIdPredicate(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||||
cq.where(builder.and(toArray(predicates)));
|
cq.where(builder.and(toArray(predicates)));
|
||||||
|
|
||||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||||
doSetPids(new HashSet<Long>(q.getResultList()));
|
doSetPids(q.getResultList());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,13 +217,13 @@ public class SearchBuilder {
|
||||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||||
doCreateIdPredicate(predicates, from.get("myResourcePid").as(Long.class));
|
doCreateIdPredicate(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||||
predicates.add(masterCodePredicate);
|
predicates.add(masterCodePredicate);
|
||||||
|
|
||||||
cq.where(builder.and(toArray(predicates)));
|
cq.where(builder.and(toArray(predicates)));
|
||||||
|
|
||||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||||
doSetPids(new HashSet<Long>(q.getResultList()));
|
doSetPids(q.getResultList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPredicateId(Set<Long> thePids, DateRangeParam theLastUpdated) {
|
private void addPredicateId(Set<Long> thePids, DateRangeParam theLastUpdated) {
|
||||||
|
@ -235,20 +239,13 @@ public class SearchBuilder {
|
||||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||||
predicates.add(from.get("myId").in(thePids));
|
predicates.add(from.get("myId").in(thePids));
|
||||||
doCreateIdPredicate(predicates, from.get("myId").as(Long.class));
|
doCreateIdPredicate(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||||
predicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, from));
|
predicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, from));
|
||||||
|
|
||||||
cq.where(toArray(predicates));
|
cq.where(toArray(predicates));
|
||||||
|
|
||||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||||
HashSet<Long> found = new HashSet<Long>(q.getResultList());
|
doSetPids(q.getResultList());
|
||||||
doSetPids(found);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doCreateIdPredicate(List<Predicate> thePredicates, Expression<Long> theExpression) {
|
|
||||||
if (myPids != null) {
|
|
||||||
thePredicates.add(theExpression.in(myPids));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPredicateLanguage(List<List<? extends IQueryParameterType>> theList, DateRangeParam theLastUpdated) {
|
private void addPredicateLanguage(List<List<? extends IQueryParameterType>> theList, DateRangeParam theLastUpdated) {
|
||||||
|
@ -279,15 +276,14 @@ public class SearchBuilder {
|
||||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||||
predicates.add(from.get("myLanguage").as(String.class).in(values));
|
predicates.add(from.get("myLanguage").as(String.class).in(values));
|
||||||
doCreateIdPredicate(predicates, from.get("myId").as(Long.class));
|
doCreateIdPredicate(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||||
|
|
||||||
predicates.add(builder.isNull(from.get("myDeleted")));
|
predicates.add(builder.isNull(from.get("myDeleted")));
|
||||||
|
|
||||||
cq.where(toArray(predicates));
|
cq.where(toArray(predicates));
|
||||||
|
|
||||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||||
HashSet<Long> pids = new HashSet<Long>(q.getResultList());
|
doSetPids(q.getResultList());
|
||||||
doSetPids(pids);
|
|
||||||
if (doHaveNoResults()) {
|
if (doHaveNoResults()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -296,7 +292,8 @@ public class SearchBuilder {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean addPredicateMissingFalseIfPresent(CriteriaBuilder theBuilder, String theParamName, Root<? extends BaseResourceIndexedSearchParam> from, List<Predicate> codePredicates, IQueryParameterType nextOr) {
|
private boolean addPredicateMissingFalseIfPresent(CriteriaBuilder theBuilder, String theParamName, Root<? extends BaseResourceIndexedSearchParam> from, List<Predicate> codePredicates,
|
||||||
|
IQueryParameterType nextOr) {
|
||||||
boolean missingFalse = false;
|
boolean missingFalse = false;
|
||||||
if (nextOr.getMissing() != null) {
|
if (nextOr.getMissing() != null) {
|
||||||
if (nextOr.getMissing().booleanValue() == true) {
|
if (nextOr.getMissing().booleanValue() == true) {
|
||||||
|
@ -310,7 +307,8 @@ public class SearchBuilder {
|
||||||
return missingFalse;
|
return missingFalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean addPredicateMissingFalseIfPresentForResourceLink(CriteriaBuilder theBuilder, String theParamName, Root<? extends ResourceLink> from, List<Predicate> codePredicates, IQueryParameterType nextOr) {
|
private boolean addPredicateMissingFalseIfPresentForResourceLink(CriteriaBuilder theBuilder, String theParamName, Root<? extends ResourceLink> from, List<Predicate> codePredicates,
|
||||||
|
IQueryParameterType nextOr) {
|
||||||
boolean missingFalse = false;
|
boolean missingFalse = false;
|
||||||
if (nextOr.getMissing() != null) {
|
if (nextOr.getMissing() != null) {
|
||||||
if (nextOr.getMissing().booleanValue() == true) {
|
if (nextOr.getMissing().booleanValue() == true) {
|
||||||
|
@ -370,12 +368,12 @@ public class SearchBuilder {
|
||||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||||
predicates.add(builder.or(toArray(codePredicates)));
|
predicates.add(builder.or(toArray(codePredicates)));
|
||||||
doCreateIdPredicate(predicates, from.get("myResourcePid").as(Long.class));
|
doCreateIdPredicate(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||||
|
|
||||||
cq.where(builder.and(toArray(predicates)));
|
cq.where(builder.and(toArray(predicates)));
|
||||||
|
|
||||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||||
doSetPids(new HashSet<Long>(q.getResultList()));
|
doSetPids(q.getResultList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPredicateParamMissing(String joinName, String theParamName, Class<? extends BaseResourceIndexedSearchParam> theParamTable) {
|
private void addPredicateParamMissing(String joinName, String theParamName, Class<? extends BaseResourceIndexedSearchParam> theParamTable) {
|
||||||
|
@ -395,17 +393,14 @@ public class SearchBuilder {
|
||||||
predicates.add(builder.not(builder.in(from.get("myId")).value(subQ)));
|
predicates.add(builder.not(builder.in(from.get("myId")).value(subQ)));
|
||||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||||
predicates.add(builder.isNull(from.get("myDeleted")));
|
predicates.add(builder.isNull(from.get("myDeleted")));
|
||||||
doCreateIdPredicate(predicates, from.get("myId").as(Long.class));
|
doCreateIdPredicate(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||||
|
|
||||||
cq.where(builder.and(toArray(predicates)));
|
cq.where(builder.and(toArray(predicates)));
|
||||||
|
|
||||||
ourLog.info("Adding :missing qualifier for parameter '{}'", theParamName);
|
ourLog.info("Adding :missing qualifier for parameter '{}'", theParamName);
|
||||||
|
|
||||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||||
List<Long> resultList = q.getResultList();
|
doSetPids(q.getResultList());
|
||||||
|
|
||||||
HashSet<Long> retVal = new HashSet<Long>(resultList);
|
|
||||||
doSetPids(retVal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPredicateParamMissingResourceLink(String joinName, String theParamName) {
|
private void addPredicateParamMissingResourceLink(String joinName, String theParamName) {
|
||||||
|
@ -423,7 +418,7 @@ public class SearchBuilder {
|
||||||
subQ.where(path);
|
subQ.where(path);
|
||||||
|
|
||||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||||
doCreateIdPredicate(predicates, from.get("myId").as(Long.class));
|
doCreateIdPredicate(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||||
predicates.add(builder.not(builder.in(from.get("myId")).value(subQ)));
|
predicates.add(builder.not(builder.in(from.get("myId")).value(subQ)));
|
||||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||||
|
|
||||||
|
@ -511,7 +506,7 @@ public class SearchBuilder {
|
||||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||||
predicates.add(builder.or(toArray(codePredicates)));
|
predicates.add(builder.or(toArray(codePredicates)));
|
||||||
doCreateIdPredicate(predicates, from.get("myResourcePid").as(Long.class));
|
doCreateIdPredicate(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||||
|
|
||||||
cq.where(builder.and(toArray(predicates)));
|
cq.where(builder.and(toArray(predicates)));
|
||||||
|
|
||||||
|
@ -657,7 +652,7 @@ public class SearchBuilder {
|
||||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||||
predicates.add(createResourceLinkPathPredicate(theParamName, builder, from));
|
predicates.add(createResourceLinkPathPredicate(theParamName, builder, from));
|
||||||
predicates.add(builder.or(toArray(codePredicates)));
|
predicates.add(builder.or(toArray(codePredicates)));
|
||||||
doCreateIdPredicate(predicates, from.get("mySourceResourcePid").as(Long.class));
|
doCreateIdPredicate(builder, cq, predicates, from.get("mySourceResourcePid").as(Long.class));
|
||||||
|
|
||||||
cq.where(builder.and(toArray(predicates)));
|
cq.where(builder.and(toArray(predicates)));
|
||||||
|
|
||||||
|
@ -691,7 +686,7 @@ public class SearchBuilder {
|
||||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||||
predicates.add(builder.or(toArray(codePredicates)));
|
predicates.add(builder.or(toArray(codePredicates)));
|
||||||
doCreateIdPredicate(predicates, from.get("myResourcePid").as(Long.class));
|
doCreateIdPredicate(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||||
|
|
||||||
cq.where(builder.and(toArray(predicates)));
|
cq.where(builder.and(toArray(predicates)));
|
||||||
|
|
||||||
|
@ -778,7 +773,7 @@ public class SearchBuilder {
|
||||||
andPredicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, defJoin));
|
andPredicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, defJoin));
|
||||||
}
|
}
|
||||||
|
|
||||||
doCreateIdPredicate(andPredicates, from.get("myResourceId").as(Long.class));
|
doCreateIdPredicate(builder, cq, andPredicates, from.get("myResourceId").as(Long.class));
|
||||||
Predicate masterCodePredicate = builder.and(toArray(andPredicates));
|
Predicate masterCodePredicate = builder.and(toArray(andPredicates));
|
||||||
|
|
||||||
cq.where(masterCodePredicate);
|
cq.where(masterCodePredicate);
|
||||||
|
@ -828,7 +823,7 @@ public class SearchBuilder {
|
||||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||||
predicates.add(builder.or(toArray(codePredicates)));
|
predicates.add(builder.or(toArray(codePredicates)));
|
||||||
doCreateIdPredicate(predicates, from.get("myResourcePid").as(Long.class));
|
doCreateIdPredicate(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||||
|
|
||||||
cq.where(builder.and(toArray(predicates)));
|
cq.where(builder.and(toArray(predicates)));
|
||||||
|
|
||||||
|
@ -868,17 +863,12 @@ public class SearchBuilder {
|
||||||
if (param.getQualifier() == UriParamQualifierEnum.ABOVE) {
|
if (param.getQualifier() == UriParamQualifierEnum.ABOVE) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* :above is an inefficient query- It means that the user is supplying a more specific URL
|
* :above is an inefficient query- It means that the user is supplying a more specific URL (say http://example.com/foo/bar/baz) and that we should match on any URLs that are less
|
||||||
* (say http://example.com/foo/bar/baz) and that we should match on any URLs that are
|
* specific but otherwise the same. For example http://example.com and http://example.com/foo would both match.
|
||||||
* less specific but otherwise the same. For example http://example.com and http://example.com/foo
|
|
||||||
* would both match.
|
|
||||||
*
|
*
|
||||||
* We do this by querying the DB for all candidate URIs and then manually checking
|
* We do this by querying the DB for all candidate URIs and then manually checking each one. This isn't very efficient, but this is also probably not a very common type of query to do.
|
||||||
* each one. This isn't very efficient, but this is also probably not a very common
|
|
||||||
* type of query to do.
|
|
||||||
*
|
*
|
||||||
* If we ever need to make this more efficient, lucene could certainly be used
|
* If we ever need to make this more efficient, lucene could certainly be used as an optimization.
|
||||||
* as an optimization.
|
|
||||||
*/
|
*/
|
||||||
ourLog.info("Searching for candidate URI:above parameters for Resource[{}] param[{}]", myResourceName, theParamName);
|
ourLog.info("Searching for candidate URI:above parameters for Resource[{}] param[{}]", myResourceName, theParamName);
|
||||||
Collection<String> candidates = myResourceIndexedSearchParamUriDao.findAllByResourceTypeAndParamName(myResourceName, theParamName);
|
Collection<String> candidates = myResourceIndexedSearchParamUriDao.findAllByResourceTypeAndParamName(myResourceName, theParamName);
|
||||||
|
@ -918,7 +908,7 @@ public class SearchBuilder {
|
||||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||||
predicates.add(builder.or(toArray(codePredicates)));
|
predicates.add(builder.or(toArray(codePredicates)));
|
||||||
doCreateIdPredicate(predicates, from.get("myResourcePid").as(Long.class));
|
doCreateIdPredicate(builder, cq, predicates, from.get("myResourcePid").as(Long.class));
|
||||||
|
|
||||||
cq.where(builder.and(toArray(predicates)));
|
cq.where(builder.and(toArray(predicates)));
|
||||||
|
|
||||||
|
@ -953,7 +943,7 @@ public class SearchBuilder {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Predicate> createLastUpdatedPredicates(final DateRangeParam theLastUpdated, CriteriaBuilder builder, From<?, ResourceTable> from) {
|
private static List<Predicate> createLastUpdatedPredicates(final DateRangeParam theLastUpdated, CriteriaBuilder builder, From<?, ResourceTable> from) {
|
||||||
List<Predicate> lastUpdatedPredicates = new ArrayList<Predicate>();
|
List<Predicate> lastUpdatedPredicates = new ArrayList<Predicate>();
|
||||||
if (theLastUpdated != null) {
|
if (theLastUpdated != null) {
|
||||||
if (theLastUpdated.getLowerBoundAsInstant() != null) {
|
if (theLastUpdated.getLowerBoundAsInstant() != null) {
|
||||||
|
@ -1023,7 +1013,8 @@ public class SearchBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Predicate createPredicateNumeric(CriteriaBuilder builder, IQueryParameterType params, ParamPrefixEnum cmpValue, BigDecimal valueValue, final Expression<BigDecimal> path, String invalidMessageName, String theValueString) {
|
private Predicate createPredicateNumeric(CriteriaBuilder builder, IQueryParameterType params, ParamPrefixEnum cmpValue, BigDecimal valueValue, final Expression<BigDecimal> path,
|
||||||
|
String invalidMessageName, String theValueString) {
|
||||||
Predicate num;
|
Predicate num;
|
||||||
switch (cmpValue) {
|
switch (cmpValue) {
|
||||||
case GREATERTHAN:
|
case GREATERTHAN:
|
||||||
|
@ -1065,7 +1056,8 @@ public class SearchBuilder {
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Predicate createPredicateString(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> theFrom) {
|
private Predicate createPredicateString(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder,
|
||||||
|
From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> theFrom) {
|
||||||
String rawSearchTerm;
|
String rawSearchTerm;
|
||||||
if (theParameter instanceof TokenParam) {
|
if (theParameter instanceof TokenParam) {
|
||||||
TokenParam id = (TokenParam) theParameter;
|
TokenParam id = (TokenParam) theParameter;
|
||||||
|
@ -1084,7 +1076,8 @@ public class SearchBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawSearchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
if (rawSearchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||||
throw new InvalidRequestException("Parameter[" + theParamName + "] has length (" + rawSearchTerm.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm);
|
throw new InvalidRequestException("Parameter[" + theParamName + "] has length (" + rawSearchTerm.length() + ") that is longer than maximum allowed ("
|
||||||
|
+ ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
String likeExpression = BaseHapiFhirDao.normalizeString(rawSearchTerm);
|
String likeExpression = BaseHapiFhirDao.normalizeString(rawSearchTerm);
|
||||||
|
@ -1098,11 +1091,8 @@ public class SearchBuilder {
|
||||||
return singleCode;
|
return singleCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String createLeftMatchLikeExpression(String likeExpression) {
|
private Predicate createPredicateToken(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder,
|
||||||
return likeExpression.replace("%", "[%]") + "%";
|
From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> theFrom) {
|
||||||
}
|
|
||||||
|
|
||||||
private Predicate createPredicateToken(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> theFrom) {
|
|
||||||
String code;
|
String code;
|
||||||
String system;
|
String system;
|
||||||
if (theParameter instanceof TokenParam) {
|
if (theParameter instanceof TokenParam) {
|
||||||
|
@ -1122,10 +1112,12 @@ public class SearchBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||||
throw new InvalidRequestException("Parameter[" + theParamName + "] has system (" + system.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + system);
|
throw new InvalidRequestException(
|
||||||
|
"Parameter[" + theParamName + "] has system (" + system.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + system);
|
||||||
}
|
}
|
||||||
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||||
throw new InvalidRequestException("Parameter[" + theParamName + "] has code (" + code.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + code);
|
throw new InvalidRequestException(
|
||||||
|
"Parameter[" + theParamName + "] has code (" + code.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + code);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<Predicate> singleCodePredicates = (new ArrayList<Predicate>());
|
ArrayList<Predicate> singleCodePredicates = (new ArrayList<Predicate>());
|
||||||
|
@ -1153,11 +1145,11 @@ public class SearchBuilder {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypedQuery<Tuple> createSearchAllByTypeQuery(DateRangeParam theLastUpdated) {
|
private TypedQuery<Long> createSearchAllByTypeQuery(DateRangeParam theLastUpdated) {
|
||||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||||
CriteriaQuery<Tuple> cq = builder.createTupleQuery();
|
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||||
cq.multiselect(from.get("myId").as(Long.class));
|
cq.select(from.get("myId").as(Long.class));
|
||||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||||
predicates.add(builder.isNull(from.get("myDeleted")));
|
predicates.add(builder.isNull(from.get("myDeleted")));
|
||||||
|
@ -1168,7 +1160,7 @@ public class SearchBuilder {
|
||||||
|
|
||||||
cq.where(toArray(predicates));
|
cq.where(toArray(predicates));
|
||||||
|
|
||||||
TypedQuery<Tuple> query = myEntityManager.createQuery(cq);
|
TypedQuery<Long> query = myEntityManager.createQuery(cq);
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1267,6 +1259,112 @@ public class SearchBuilder {
|
||||||
createSort(theBuilder, theFrom, theSort.getChain(), theOrders, thePredicates);
|
createSort(theBuilder, theFrom, theSort.getChain(), theOrders, thePredicates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void doCreateIdPredicate(CriteriaBuilder builder, CriteriaQuery<?> cq, List<Predicate> thePredicates, Expression<Long> theExpression) {
|
||||||
|
if (myParams.isPersistResults()) {
|
||||||
|
if (mySearchEntity.getTotalCount() > -1) {
|
||||||
|
Subquery<Long> subQ = cq.subquery(Long.class);
|
||||||
|
Root<SearchResult> subQfrom = subQ.from(SearchResult.class);
|
||||||
|
subQ.select(subQfrom.get("myResourcePid").as(Long.class));
|
||||||
|
Predicate subQname = builder.equal(subQfrom.get("mySearch"), mySearchEntity);
|
||||||
|
subQ.where(subQname);
|
||||||
|
|
||||||
|
thePredicates.add(theExpression.in(subQ));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (myPids != null) {
|
||||||
|
thePredicates.add(theExpression.in(myPids));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Long> doGetPids() {
|
||||||
|
if (myParams.isPersistResults()) {
|
||||||
|
HashSet<Long> retVal = new HashSet<Long>();
|
||||||
|
|
||||||
|
for (SearchResult next : mySearchResultDao.findWithSearchUuid(mySearchEntity)) {
|
||||||
|
retVal.add(next.getResourcePid());
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return new HashSet<Long>(myPids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean doHaveNoResults() {
|
||||||
|
if (myParams.isPersistResults()) {
|
||||||
|
return mySearchEntity.getTotalCount() == 0;
|
||||||
|
} else {
|
||||||
|
return myPids != null && myPids.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doInitializeSearch() {
|
||||||
|
if (mySearchEntity == null) {
|
||||||
|
|
||||||
|
mySearchEntity = new Search();
|
||||||
|
mySearchEntity.setUuid(UUID.randomUUID().toString());
|
||||||
|
mySearchEntity.setCreated(new Date());
|
||||||
|
mySearchEntity.setTotalCount(-1);
|
||||||
|
mySearchEntity.setPreferredPageSize(myParams.getCount());
|
||||||
|
mySearchEntity.setEverythingMode(myParams.getEverythingMode());
|
||||||
|
mySearchEntity.setLastUpdated(myParams.getLastUpdated());
|
||||||
|
|
||||||
|
for (Include next : myParams.getIncludes()) {
|
||||||
|
mySearchEntity.getIncludes().add(new SearchInclude(mySearchEntity, next.getValue(), false, next.isRecurse()));
|
||||||
|
}
|
||||||
|
for (Include next : myParams.getRevIncludes()) {
|
||||||
|
mySearchEntity.getIncludes().add(new SearchInclude(mySearchEntity, next.getValue(), true, next.isRecurse()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myParams.isPersistResults()) {
|
||||||
|
myEntityManager.persist(mySearchEntity);
|
||||||
|
for (SearchInclude next : mySearchEntity.getIncludes()) {
|
||||||
|
myEntityManager.persist(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IBundleProvider doReturnProvider() {
|
||||||
|
if (myParams.isPersistResults()) {
|
||||||
|
return new BundleProviderPersisted(mySearchEntity.getUuid(), myPlatformTransactionManager, mySearchResultDao, myEntityManager, myContext, myCallingDao);
|
||||||
|
} else {
|
||||||
|
if (myPids == null) {
|
||||||
|
return new SimpleBundleProvider();
|
||||||
|
} else {
|
||||||
|
return new BundleProviderInMemory(myPids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doSetPids(Collection<Long> thePids) {
|
||||||
|
if (myParams.isPersistResults()) {
|
||||||
|
mySearchResultDao.deleteForSearch(mySearchEntity.getId());
|
||||||
|
mySearchResultDao.flush();
|
||||||
|
|
||||||
|
LinkedHashSet<SearchResult> results = new LinkedHashSet<SearchResult>();
|
||||||
|
int index = 0;
|
||||||
|
for (Long next : thePids) {
|
||||||
|
SearchResult nextResult = new SearchResult(mySearchEntity);
|
||||||
|
nextResult.setResourcePid(next);
|
||||||
|
nextResult.setOrder(index);
|
||||||
|
results.add(nextResult);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
mySearchResultDao.save(results);
|
||||||
|
|
||||||
|
mySearchEntity.setTotalCount(results.size());
|
||||||
|
mySearchEntity = myEntityManager.merge(mySearchEntity);
|
||||||
|
|
||||||
|
myEntityManager.flush();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
myPids = thePids;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void filterResourceIdsByLastUpdated(final DateRangeParam theLastUpdated) {
|
private void filterResourceIdsByLastUpdated(final DateRangeParam theLastUpdated) {
|
||||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||||
|
@ -1274,7 +1372,7 @@ public class SearchBuilder {
|
||||||
cq.select(from.get("myId").as(Long.class));
|
cq.select(from.get("myId").as(Long.class));
|
||||||
|
|
||||||
List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(theLastUpdated, builder, from);
|
List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(theLastUpdated, builder, from);
|
||||||
doCreateIdPredicate(lastUpdatedPredicates, from.get("myId").as(Long.class));
|
doCreateIdPredicate(builder, cq, lastUpdatedPredicates, from.get("myId").as(Long.class));
|
||||||
|
|
||||||
cq.where(SearchBuilder.toArray(lastUpdatedPredicates));
|
cq.where(SearchBuilder.toArray(lastUpdatedPredicates));
|
||||||
TypedQuery<Long> query = myEntityManager.createQuery(cq);
|
TypedQuery<Long> query = myEntityManager.createQuery(cq);
|
||||||
|
@ -1283,8 +1381,8 @@ public class SearchBuilder {
|
||||||
doSetPids(resultList);
|
doSetPids(resultList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Long> filterResourceIdsByLastUpdated(final DateRangeParam theLastUpdated, Collection<Long> thePids) {
|
private static List<Long> filterResourceIdsByLastUpdated(EntityManager theEntityManager, final DateRangeParam theLastUpdated, Collection<Long> thePids) {
|
||||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
CriteriaBuilder builder = theEntityManager.getCriteriaBuilder();
|
||||||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||||
cq.select(from.get("myId").as(Long.class));
|
cq.select(from.get("myId").as(Long.class));
|
||||||
|
@ -1293,13 +1391,22 @@ public class SearchBuilder {
|
||||||
lastUpdatedPredicates.add(from.get("myId").as(Long.class).in(thePids));
|
lastUpdatedPredicates.add(from.get("myId").as(Long.class).in(thePids));
|
||||||
|
|
||||||
cq.where(SearchBuilder.toArray(lastUpdatedPredicates));
|
cq.where(SearchBuilder.toArray(lastUpdatedPredicates));
|
||||||
TypedQuery<Long> query = myEntityManager.createQuery(cq);
|
TypedQuery<Long> query = theEntityManager.createQuery(cq);
|
||||||
|
|
||||||
List<Long> resultList = query.getResultList();
|
List<Long> resultList = query.getResultList();
|
||||||
return resultList;
|
return resultList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation) {
|
private void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation) {
|
||||||
|
EntityManager entityManager = myEntityManager;
|
||||||
|
FhirContext context = myContext;
|
||||||
|
BaseHapiFhirDao<?> dao = myCallingDao;
|
||||||
|
|
||||||
|
loadResourcesByPid(theIncludePids, theResourceListToPopulate, theRevIncludedPids, theForHistoryOperation, entityManager, context, dao);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation,
|
||||||
|
EntityManager entityManager, FhirContext context, IDao theDao) {
|
||||||
if (theIncludePids.isEmpty()) {
|
if (theIncludePids.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1310,15 +1417,15 @@ public class SearchBuilder {
|
||||||
theResourceListToPopulate.add(null);
|
theResourceListToPopulate.add(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||||
CriteriaQuery<ResourceTable> cq = builder.createQuery(ResourceTable.class);
|
CriteriaQuery<ResourceTable> cq = builder.createQuery(ResourceTable.class);
|
||||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||||
cq.where(from.get("myId").in(theIncludePids));
|
cq.where(from.get("myId").in(theIncludePids));
|
||||||
TypedQuery<ResourceTable> q = myEntityManager.createQuery(cq);
|
TypedQuery<ResourceTable> q = entityManager.createQuery(cq);
|
||||||
|
|
||||||
for (ResourceTable next : q.getResultList()) {
|
for (ResourceTable next : q.getResultList()) {
|
||||||
Class<? extends IBaseResource> resourceType = myContext.getResourceDefinition(next.getResourceType()).getImplementingClass();
|
Class<? extends IBaseResource> resourceType = context.getResourceDefinition(next.getResourceType()).getImplementingClass();
|
||||||
IBaseResource resource = (IBaseResource) myCallingDao.toResource(resourceType, next, theForHistoryOperation);
|
IBaseResource resource = (IBaseResource) theDao.toResource(resourceType, next, theForHistoryOperation);
|
||||||
Integer index = position.get(next.getId());
|
Integer index = position.get(next.getId());
|
||||||
if (index == null) {
|
if (index == null) {
|
||||||
ourLog.warn("Got back unexpected resource PID {}", next.getId());
|
ourLog.warn("Got back unexpected resource PID {}", next.getId());
|
||||||
|
@ -1348,7 +1455,8 @@ public class SearchBuilder {
|
||||||
*
|
*
|
||||||
* @param theLastUpdated
|
* @param theLastUpdated
|
||||||
*/
|
*/
|
||||||
private HashSet<Long> loadReverseIncludes(Collection<Long> theMatches, Set<Include> theRevIncludes, boolean theReverseMode, DateRangeParam theLastUpdated) {
|
private static HashSet<Long> loadReverseIncludes(FhirContext theContext, EntityManager theEntityManager, Collection<Long> theMatches, Set<Include> theRevIncludes, boolean theReverseMode,
|
||||||
|
DateRangeParam theLastUpdated) {
|
||||||
if (theMatches.size() == 0) {
|
if (theMatches.size() == 0) {
|
||||||
return new HashSet<Long>();
|
return new HashSet<Long>();
|
||||||
}
|
}
|
||||||
|
@ -1382,7 +1490,7 @@ public class SearchBuilder {
|
||||||
if (matchAll) {
|
if (matchAll) {
|
||||||
String sql;
|
String sql;
|
||||||
sql = "SELECT r FROM ResourceLink r WHERE r." + searchFieldName + " IN (:target_pids)";
|
sql = "SELECT r FROM ResourceLink r WHERE r." + searchFieldName + " IN (:target_pids)";
|
||||||
TypedQuery<ResourceLink> q = myEntityManager.createQuery(sql, ResourceLink.class);
|
TypedQuery<ResourceLink> q = theEntityManager.createQuery(sql, ResourceLink.class);
|
||||||
q.setParameter("target_pids", nextRoundMatches);
|
q.setParameter("target_pids", nextRoundMatches);
|
||||||
List<ResourceLink> results = q.getResultList();
|
List<ResourceLink> results = q.getResultList();
|
||||||
for (ResourceLink resourceLink : results) {
|
for (ResourceLink resourceLink : results) {
|
||||||
|
@ -1401,14 +1509,14 @@ public class SearchBuilder {
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
List<String> paths;
|
List<String> paths;
|
||||||
if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
|
if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
|
||||||
paths = Collections.singletonList(nextInclude.getValue());
|
paths = Collections.singletonList(nextInclude.getValue());
|
||||||
} else {
|
} else {
|
||||||
String resType = nextInclude.getParamType();
|
String resType = nextInclude.getParamType();
|
||||||
if (isBlank(resType)) {
|
if (isBlank(resType)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(resType);
|
RuntimeResourceDefinition def = theContext.getResourceDefinition(resType);
|
||||||
if (def == null) {
|
if (def == null) {
|
||||||
ourLog.warn("Unknown resource type in include/revinclude=" + nextInclude.getValue());
|
ourLog.warn("Unknown resource type in include/revinclude=" + nextInclude.getValue());
|
||||||
continue;
|
continue;
|
||||||
|
@ -1432,7 +1540,7 @@ public class SearchBuilder {
|
||||||
} else {
|
} else {
|
||||||
sql = "SELECT r FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r." + searchFieldName + " IN (:target_pids)";
|
sql = "SELECT r FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r." + searchFieldName + " IN (:target_pids)";
|
||||||
}
|
}
|
||||||
TypedQuery<ResourceLink> q = myEntityManager.createQuery(sql, ResourceLink.class);
|
TypedQuery<ResourceLink> q = theEntityManager.createQuery(sql, ResourceLink.class);
|
||||||
q.setParameter("src_path", nextPath);
|
q.setParameter("src_path", nextPath);
|
||||||
q.setParameter("target_pids", nextRoundMatches);
|
q.setParameter("target_pids", nextRoundMatches);
|
||||||
if (targetResourceType != null) {
|
if (targetResourceType != null) {
|
||||||
|
@ -1451,7 +1559,7 @@ public class SearchBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theLastUpdated != null && (theLastUpdated.getLowerBoundAsInstant() != null || theLastUpdated.getUpperBoundAsInstant() != null)) {
|
if (theLastUpdated != null && (theLastUpdated.getLowerBoundAsInstant() != null || theLastUpdated.getUpperBoundAsInstant() != null)) {
|
||||||
pidsToInclude = new HashSet<Long>(filterResourceIdsByLastUpdated(theLastUpdated, pidsToInclude));
|
pidsToInclude = new HashSet<Long>(filterResourceIdsByLastUpdated(theEntityManager, theLastUpdated, pidsToInclude));
|
||||||
}
|
}
|
||||||
for (Long next : pidsToInclude) {
|
for (Long next : pidsToInclude) {
|
||||||
if (original.contains(next) == false && allAdded.contains(next) == false) {
|
if (original.contains(next) == false && allAdded.contains(next) == false) {
|
||||||
|
@ -1480,14 +1588,14 @@ public class SearchBuilder {
|
||||||
CriteriaQuery<Tuple> cq = builder.createTupleQuery();
|
CriteriaQuery<Tuple> cq = builder.createTupleQuery();
|
||||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||||
|
|
||||||
doCreateIdPredicate(predicates, from.get("myId").as(Long.class));
|
doCreateIdPredicate(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||||
|
|
||||||
createSort(builder, from, theParams.getSort(), orders, predicates);
|
createSort(builder, from, theParams.getSort(), orders, predicates);
|
||||||
|
|
||||||
if (orders.size() > 0) {
|
if (orders.size() > 0) {
|
||||||
|
|
||||||
// TODO: why do we need the existing list for this join to work?
|
// TODO: why do we need the existing list for this join to work?
|
||||||
Collection<Long> originalPids = myPids;
|
Collection<Long> originalPids = doGetPids();
|
||||||
|
|
||||||
LinkedHashSet<Long> loadPids = new LinkedHashSet<Long>();
|
LinkedHashSet<Long> loadPids = new LinkedHashSet<Long>();
|
||||||
cq.multiselect(from.get("myId").as(Long.class));
|
cq.multiselect(from.get("myId").as(Long.class));
|
||||||
|
@ -1528,7 +1636,7 @@ public class SearchBuilder {
|
||||||
lu = null;
|
lu = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collection<Long> loadPids;
|
// Collection<Long> loadPids;
|
||||||
if (theParams.getEverythingMode() != null) {
|
if (theParams.getEverythingMode() != null) {
|
||||||
|
|
||||||
Long pid = null;
|
Long pid = null;
|
||||||
|
@ -1575,18 +1683,8 @@ public class SearchBuilder {
|
||||||
|
|
||||||
} else if (theParams.isEmpty()) {
|
} else if (theParams.isEmpty()) {
|
||||||
|
|
||||||
TypedQuery<Tuple> query = createSearchAllByTypeQuery(lu);
|
TypedQuery<Long> query = createSearchAllByTypeQuery(lu);
|
||||||
lu = null;
|
doSetPids(query.getResultList());
|
||||||
List<Tuple> resultList = query.getResultList();
|
|
||||||
if (resultList.isEmpty()) {
|
|
||||||
return doReturnProvider();
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Long> loadPids = new HashSet<Long>();
|
|
||||||
for (Tuple next : resultList) {
|
|
||||||
loadPids.add(next.get(0, Long.class));
|
|
||||||
}
|
|
||||||
doSetPids(loadPids);
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
@ -1634,7 +1732,6 @@ public class SearchBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Handle sorting if any was provided
|
// Handle sorting if any was provided
|
||||||
processSort(theParams);
|
processSort(theParams);
|
||||||
|
|
||||||
|
@ -1642,101 +1739,14 @@ public class SearchBuilder {
|
||||||
return doReturnProvider();
|
return doReturnProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IBundleProvider doReturnProvider() {
|
|
||||||
if (myPids == null) {
|
|
||||||
return new SimpleBundleProvider();
|
|
||||||
} else {
|
|
||||||
final ArrayList<Long> pids;
|
|
||||||
if (!(myPids instanceof List)) {
|
|
||||||
pids = new ArrayList<Long>(myPids);
|
|
||||||
} else {
|
|
||||||
pids = (ArrayList<Long>) myPids;
|
|
||||||
}
|
|
||||||
|
|
||||||
// // Load _revinclude resources
|
|
||||||
// final Set<Long> revIncludedPids;
|
|
||||||
// if (myParams.getEverythingMode() == null) {
|
|
||||||
// if (myParams.getRevIncludes() != null && myParams.getRevIncludes().isEmpty() == false) {
|
|
||||||
// revIncludedPids = loadReverseIncludes(pids, myParams.getRevIncludes(), true, myParams.getLastUpdated());
|
|
||||||
// } else {
|
|
||||||
// revIncludedPids = new HashSet<Long>();
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// revIncludedPids = new HashSet<Long>();
|
|
||||||
// }
|
|
||||||
|
|
||||||
return new IBundleProvider() {
|
|
||||||
@Override
|
|
||||||
public InstantDt getPublished() {
|
|
||||||
return mySearchStarted;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<IBaseResource> getResources(final int theFromIndex, final int theToIndex) {
|
|
||||||
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
|
|
||||||
return template.execute(new TransactionCallback<List<IBaseResource>>() {
|
|
||||||
@Override
|
|
||||||
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
|
|
||||||
List<Long> pidsSubList = pids.subList(theFromIndex, theToIndex);
|
|
||||||
|
|
||||||
// Load includes
|
|
||||||
pidsSubList = new ArrayList<Long>(pidsSubList);
|
|
||||||
|
|
||||||
Set<Long> revIncludedPids = new HashSet<Long>();
|
|
||||||
if (myParams.getEverythingMode() == null) {
|
|
||||||
revIncludedPids.addAll(loadReverseIncludes(pidsSubList, myParams.getRevIncludes(), true, myParams.getLastUpdated()));
|
|
||||||
}
|
|
||||||
revIncludedPids.addAll(loadReverseIncludes(pidsSubList, myParams.getIncludes(), false, myParams.getLastUpdated()));
|
|
||||||
|
|
||||||
// Execute the query and make sure we return distinct results
|
|
||||||
List<IBaseResource> resources = new ArrayList<IBaseResource>();
|
|
||||||
loadResourcesByPid(pidsSubList, resources, revIncludedPids, false);
|
|
||||||
|
|
||||||
return resources;
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Integer preferredPageSize() {
|
|
||||||
return myParams.getCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return pids.size();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Collection<Long> myPids;
|
|
||||||
|
|
||||||
private InstantDt mySearchStarted;
|
|
||||||
|
|
||||||
private void doSetPids(Collection<Long> thePids) {
|
|
||||||
myPids = thePids;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doInitializeSearch() {
|
|
||||||
assert mySearchEntity == null;
|
|
||||||
|
|
||||||
mySearchStarted = InstantDt.withCurrentTime();
|
|
||||||
|
|
||||||
// mySearchEntity = new Search();
|
|
||||||
// mySearchEntity.setUuid(UUID.randomUUID().toString());
|
|
||||||
// myEntityManager.persist(mySearchEntity);
|
|
||||||
//
|
|
||||||
// myHaveFlushedSearch = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void searchForIdsWithAndOr(SearchParameterMap theParams, DateRangeParam theLastUpdated) {
|
public void searchForIdsWithAndOr(SearchParameterMap theParams, DateRangeParam theLastUpdated) {
|
||||||
SearchParameterMap params = theParams;
|
SearchParameterMap params = theParams;
|
||||||
if (params == null) {
|
if (params == null) {
|
||||||
params = new SearchParameterMap();
|
params = new SearchParameterMap();
|
||||||
}
|
}
|
||||||
|
myParams = theParams;
|
||||||
|
|
||||||
|
doInitializeSearch();
|
||||||
|
|
||||||
RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(myResourceType);
|
RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(myResourceType);
|
||||||
|
|
||||||
|
@ -1868,10 +1878,6 @@ public class SearchBuilder {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean doHaveNoResults() {
|
|
||||||
return myPids != null && myPids.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setType(Class<? extends IBaseResource> theResourceType, String theResourceName) {
|
public void setType(Class<? extends IBaseResource> theResourceType, String theResourceName) {
|
||||||
myResourceType = theResourceType;
|
myResourceType = theResourceType;
|
||||||
myResourceName = theResourceName;
|
myResourceName = theResourceName;
|
||||||
|
@ -1931,9 +1937,8 @@ public class SearchBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Figures out the tolerance for a search. For example, if the user is searching for
|
* Figures out the tolerance for a search. For example, if the user is searching for <code>4.00</code>, this method returns <code>0.005</code> because we shold actually match values which are
|
||||||
* <code>4.00</code>, this method returns <code>0.005</code> because we shold actually
|
* <code>4 (+/-) 0.005</code> according to the FHIR specs.
|
||||||
* match values which are <code>4 (+/-) 0.005</code> according to the FHIR specs.
|
|
||||||
*/
|
*/
|
||||||
static BigDecimal calculateFuzzAmount(ParamPrefixEnum cmpValue, BigDecimal theValue) {
|
static BigDecimal calculateFuzzAmount(ParamPrefixEnum cmpValue, BigDecimal theValue) {
|
||||||
if (cmpValue == ParamPrefixEnum.APPROXIMATE) {
|
if (cmpValue == ParamPrefixEnum.APPROXIMATE) {
|
||||||
|
@ -1952,12 +1957,181 @@ public class SearchBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String createLeftMatchLikeExpression(String likeExpression) {
|
||||||
|
return likeExpression.replace("%", "[%]") + "%";
|
||||||
|
}
|
||||||
|
|
||||||
static Predicate[] toArray(List<Predicate> thePredicates) {
|
static Predicate[] toArray(List<Predicate> thePredicates) {
|
||||||
return thePredicates.toArray(new Predicate[thePredicates.size()]);
|
return thePredicates.toArray(new Predicate[thePredicates.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Long> doGetPids() {
|
private final class BundleProviderInMemory implements IBundleProvider {
|
||||||
return new HashSet<Long>(myPids);
|
private final ArrayList<Long> myPids;
|
||||||
|
|
||||||
|
private BundleProviderInMemory(Collection<Long> thePids) {
|
||||||
|
final ArrayList<Long> pids;
|
||||||
|
if (!(thePids instanceof List)) {
|
||||||
|
pids = new ArrayList<Long>(thePids);
|
||||||
|
} else {
|
||||||
|
pids = (ArrayList<Long>) thePids;
|
||||||
|
}
|
||||||
|
myPids = pids;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstantDt getPublished() {
|
||||||
|
return new InstantDt(mySearchEntity.getCreated());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IBaseResource> getResources(final int theFromIndex, final int theToIndex) {
|
||||||
|
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
|
||||||
|
return template.execute(new TransactionCallback<List<IBaseResource>>() {
|
||||||
|
@Override
|
||||||
|
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
|
||||||
|
List<Long> pidsSubList = myPids.subList(theFromIndex, theToIndex);
|
||||||
|
|
||||||
|
// Load includes
|
||||||
|
pidsSubList = new ArrayList<Long>(pidsSubList);
|
||||||
|
|
||||||
|
Set<Long> revIncludedPids = new HashSet<Long>();
|
||||||
|
if (myParams.getEverythingMode() == null) {
|
||||||
|
revIncludedPids.addAll(loadReverseIncludes(myContext, myEntityManager, pidsSubList, myParams.getRevIncludes(), true, myParams.getLastUpdated()));
|
||||||
|
}
|
||||||
|
revIncludedPids.addAll(loadReverseIncludes(myContext, myEntityManager, pidsSubList, myParams.getIncludes(), false, myParams.getLastUpdated()));
|
||||||
|
|
||||||
|
// Execute the query and make sure we return distinct results
|
||||||
|
List<IBaseResource> resources = new ArrayList<IBaseResource>();
|
||||||
|
loadResourcesByPid(pidsSubList, resources, revIncludedPids, false);
|
||||||
|
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer preferredPageSize() {
|
||||||
|
return myParams.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return myPids.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static class BundleProviderPersisted implements IBundleProvider {
|
||||||
|
|
||||||
|
private String myUuid;
|
||||||
|
private PlatformTransactionManager myPlatformTransactionManager;
|
||||||
|
private ISearchResultDao mySearchResultDao;
|
||||||
|
private Search mySearchEntity;
|
||||||
|
private EntityManager myEntityManager;
|
||||||
|
private FhirContext myContext;
|
||||||
|
private IDao myDao;
|
||||||
|
|
||||||
|
public BundleProviderPersisted(String theSearchUuid, PlatformTransactionManager thePlatformTransactionManager, ISearchResultDao theSearchResultDao, EntityManager theEntityManager,
|
||||||
|
FhirContext theContext, IDao theDao) {
|
||||||
|
myUuid = theSearchUuid;
|
||||||
|
myPlatformTransactionManager = thePlatformTransactionManager;
|
||||||
|
mySearchResultDao = theSearchResultDao;
|
||||||
|
myEntityManager = theEntityManager;
|
||||||
|
myContext = theContext;
|
||||||
|
myDao = theDao;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InstantDt getPublished() {
|
||||||
|
ensureSearchEntityLoaded();
|
||||||
|
return new InstantDt(mySearchEntity.getCreated());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IBaseResource> getResources(final int theFromIndex, final int theToIndex) {
|
||||||
|
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
|
||||||
|
|
||||||
|
return template.execute(new TransactionCallback<List<IBaseResource>>() {
|
||||||
|
@Override
|
||||||
|
public List<IBaseResource> doInTransaction(TransactionStatus theStatus) {
|
||||||
|
ensureSearchEntityLoaded();
|
||||||
|
|
||||||
|
int pageSize = theToIndex - theFromIndex;
|
||||||
|
if (pageSize < 1) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
int pageIndex = theFromIndex / pageSize;
|
||||||
|
|
||||||
|
Pageable page = new PageRequest(pageIndex, pageSize);
|
||||||
|
Page<SearchResult> search = mySearchResultDao.findWithSearchUuid(mySearchEntity, page);
|
||||||
|
|
||||||
|
List<Long> pidsSubList = new ArrayList<Long>();
|
||||||
|
for (SearchResult next : search) {
|
||||||
|
pidsSubList.add(next.getResourcePid());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load includes
|
||||||
|
pidsSubList = new ArrayList<Long>(pidsSubList);
|
||||||
|
|
||||||
|
Set<Long> revIncludedPids = new HashSet<Long>();
|
||||||
|
if (mySearchEntity.getEverythingMode() == null) {
|
||||||
|
revIncludedPids.addAll(loadReverseIncludes(myContext, myEntityManager, pidsSubList, mySearchEntity.toRevIncludesList(), true, mySearchEntity.getLastUpdated()));
|
||||||
|
}
|
||||||
|
revIncludedPids.addAll(loadReverseIncludes(myContext, myEntityManager, pidsSubList, mySearchEntity.toIncludesList(), false, mySearchEntity.getLastUpdated()));
|
||||||
|
|
||||||
|
// Execute the query and make sure we return distinct results
|
||||||
|
List<IBaseResource> resources = new ArrayList<IBaseResource>();
|
||||||
|
loadResourcesByPid(pidsSubList, resources, revIncludedPids, false, myEntityManager, myContext, myDao);
|
||||||
|
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer preferredPageSize() {
|
||||||
|
ensureSearchEntityLoaded();
|
||||||
|
return mySearchEntity.getPreferredPageSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
ensureSearchEntityLoaded();
|
||||||
|
return mySearchEntity.getTotalCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSearchUuid() {
|
||||||
|
return myUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns false if the entity can't be found
|
||||||
|
*/
|
||||||
|
public boolean ensureSearchEntityLoaded() {
|
||||||
|
if (mySearchEntity == null) {
|
||||||
|
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
|
||||||
|
template.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
||||||
|
return template.execute(new TransactionCallback<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public Boolean doInTransaction(TransactionStatus theStatus) {
|
||||||
|
TypedQuery<Search> q = myEntityManager.createQuery("SELECT s FROM Search s WHERE s.myUuid = :uuid", Search.class);
|
||||||
|
q.setParameter("uuid", myUuid);
|
||||||
|
try {
|
||||||
|
mySearchEntity = q.getSingleResult();
|
||||||
|
|
||||||
|
// Ensure includes are loaded
|
||||||
|
mySearchEntity.getIncludes().size();
|
||||||
|
return true;
|
||||||
|
} catch (NoResultException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
||||||
private EverythingModeEnum myEverythingMode = null;
|
private EverythingModeEnum myEverythingMode = null;
|
||||||
private Set<Include> myIncludes;
|
private Set<Include> myIncludes;
|
||||||
private DateRangeParam myLastUpdated;
|
private DateRangeParam myLastUpdated;
|
||||||
|
private boolean myPersistResults = true;
|
||||||
private RequestDetails myRequestDetails;
|
private RequestDetails myRequestDetails;
|
||||||
private Set<Include> myRevIncludes;
|
private Set<Include> myRevIncludes;
|
||||||
private SortSpec mySort;
|
private SortSpec mySort;
|
||||||
|
@ -133,6 +134,10 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
||||||
return mySort;
|
return mySort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPersistResults() {
|
||||||
|
return myPersistResults;
|
||||||
|
}
|
||||||
|
|
||||||
public void setCount(Integer theCount) {
|
public void setCount(Integer theCount) {
|
||||||
myCount = theCount;
|
myCount = theCount;
|
||||||
}
|
}
|
||||||
|
@ -149,6 +154,13 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
||||||
myLastUpdated = theLastUpdated;
|
myLastUpdated = theLastUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should results be persisted into a table for paging
|
||||||
|
*/
|
||||||
|
public void setPersistResults(boolean thePersistResults) {
|
||||||
|
myPersistResults = thePersistResults;
|
||||||
|
}
|
||||||
|
|
||||||
public void setRequestDetails(RequestDetails theRequestDetails) {
|
public void setRequestDetails(RequestDetails theRequestDetails) {
|
||||||
myRequestDetails = theRequestDetails;
|
myRequestDetails = theRequestDetails;
|
||||||
}
|
}
|
||||||
|
@ -174,12 +186,13 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum EverythingModeEnum {
|
public enum EverythingModeEnum {
|
||||||
|
/*
|
||||||
|
* Don't reorder! We rely on the ordinals
|
||||||
|
*/
|
||||||
ENCOUNTER_INSTANCE(false, true, true),
|
ENCOUNTER_INSTANCE(false, true, true),
|
||||||
ENCOUNTER_TYPE(false, true, false),
|
ENCOUNTER_TYPE(false, true, false),
|
||||||
PATIENT_INSTANCE(true, false, true),
|
PATIENT_INSTANCE(true, false, true),
|
||||||
//@formatter:off
|
|
||||||
PATIENT_TYPE(true, false, false);
|
PATIENT_TYPE(true, false, false);
|
||||||
//@formatter:on
|
|
||||||
|
|
||||||
private final boolean myEncounter;
|
private final boolean myEncounter;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.data;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2016 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.entity.Search;
|
||||||
|
|
||||||
|
public interface ISearchDao extends JpaRepository<Search, Long> {
|
||||||
|
|
||||||
|
@Query("SELECT s FROM Search s WHERE s.myCreated < :cutoff")
|
||||||
|
public Collection<Search> findWhereCreatedBefore(@Param("cutoff") Date theCutoff);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2016 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.entity.SearchInclude;
|
||||||
|
|
||||||
|
public interface ISearchIncludeDao extends JpaRepository<SearchInclude, Long> {
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query(value="DELETE FROM SearchInclude r WHERE r.mySearchPid = :search")
|
||||||
|
void deleteForSearch(@Param("search") Long theSearchPid);
|
||||||
|
}
|
|
@ -1,5 +1,10 @@
|
||||||
package ca.uhn.fhir.jpa.dao.data;
|
package ca.uhn.fhir.jpa.dao.data;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR JPA Server
|
* HAPI FHIR JPA Server
|
||||||
|
@ -21,9 +26,22 @@ package ca.uhn.fhir.jpa.dao.data;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.entity.Search;
|
||||||
import ca.uhn.fhir.jpa.entity.SearchResult;
|
import ca.uhn.fhir.jpa.entity.SearchResult;
|
||||||
|
|
||||||
public interface ISearchResultDao extends JpaRepository<SearchResult, Long> {
|
public interface ISearchResultDao extends JpaRepository<SearchResult, Long> {
|
||||||
// nothing
|
|
||||||
|
@Query(value="SELECT r FROM SearchResult r WHERE r.mySearch = :search")
|
||||||
|
Collection<SearchResult> findWithSearchUuid(@Param("search") Search theSearch);
|
||||||
|
|
||||||
|
@Query(value="SELECT r FROM SearchResult r WHERE r.mySearch = :search ORDER BY r.myOrder ASC")
|
||||||
|
Page<SearchResult> findWithSearchUuid(@Param("search") Search theSearch, Pageable thePage);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query(value="DELETE FROM SearchResult r WHERE r.mySearchPid = :search")
|
||||||
|
void deleteForSearch(@Param("search") Long theSearchPid);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,9 @@ import javax.persistence.FetchType;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.GenerationType;
|
import javax.persistence.GenerationType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.OneToOne;
|
||||||
import javax.persistence.SequenceGenerator;
|
import javax.persistence.SequenceGenerator;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.UniqueConstraint;
|
import javax.persistence.UniqueConstraint;
|
||||||
|
@ -66,6 +68,13 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
|
||||||
@OneToMany(mappedBy = "myResourceHistory", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
|
@OneToMany(mappedBy = "myResourceHistory", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
|
||||||
private Collection<ResourceHistoryTag> myTags;
|
private Collection<ResourceHistoryTag> myTags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field is only populated if this specific histor entry corresponds to
|
||||||
|
* the most recent version of a resource
|
||||||
|
*/
|
||||||
|
@OneToOne(fetch=FetchType.LAZY, optional=true, mappedBy="myHistory")
|
||||||
|
private ResourceTable myCorrespondsToVersion;
|
||||||
|
|
||||||
public void addTag(ResourceHistoryTag theTag) {
|
public void addTag(ResourceHistoryTag theTag) {
|
||||||
for (ResourceHistoryTag next : getTags()) {
|
for (ResourceHistoryTag next : getTags()) {
|
||||||
if (next.getTag().equals(theTag)) {
|
if (next.getTag().equals(theTag)) {
|
||||||
|
|
|
@ -35,7 +35,9 @@ import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.GenerationType;
|
import javax.persistence.GenerationType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.Index;
|
import javax.persistence.Index;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.OneToOne;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.Transient;
|
import javax.persistence.Transient;
|
||||||
|
|
||||||
|
@ -150,6 +152,10 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
@Column(name = "SP_HAS_LINKS")
|
@Column(name = "SP_HAS_LINKS")
|
||||||
private boolean myHasLinks;
|
private boolean myHasLinks;
|
||||||
|
|
||||||
|
@OneToOne(fetch=FetchType.LAZY, optional=true)
|
||||||
|
@JoinColumn(name="HISTORY_VERSION_PID", referencedColumnName="PID", nullable=true)
|
||||||
|
private ResourceHistoryTable myHistory;
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
@Column(name = "RES_ID")
|
@Column(name = "RES_ID")
|
||||||
|
|
|
@ -21,20 +21,32 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.GenerationType;
|
import javax.persistence.GenerationType;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.Index;
|
import javax.persistence.Index;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
import javax.persistence.SequenceGenerator;
|
import javax.persistence.SequenceGenerator;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.Temporal;
|
import javax.persistence.Temporal;
|
||||||
import javax.persistence.TemporalType;
|
import javax.persistence.TemporalType;
|
||||||
import javax.persistence.UniqueConstraint;
|
import javax.persistence.UniqueConstraint;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum;
|
||||||
|
import ca.uhn.fhir.model.api.Include;
|
||||||
|
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "HFJ_SEARCH", uniqueConstraints= {
|
@Table(name = "HFJ_SEARCH", uniqueConstraints= {
|
||||||
|
@ -48,25 +60,73 @@ public class Search implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Temporal(TemporalType.TIMESTAMP)
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
@Column(name="CREATED", nullable=false)
|
@Column(name="CREATED", nullable=false, updatable=false)
|
||||||
private Date myCreated;
|
private Date myCreated;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.ORDINAL)
|
||||||
|
@Column(name="EVERYTHING_MODE", nullable=true)
|
||||||
|
private EverythingModeEnum myEverythingMode;
|
||||||
|
|
||||||
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO, generator="SEQ_SEARCH")
|
@GeneratedValue(strategy = GenerationType.AUTO, generator="SEQ_SEARCH")
|
||||||
@SequenceGenerator(name="SEQ_SEARCH", sequenceName="SEQ_SEARCH")
|
@SequenceGenerator(name="SEQ_SEARCH", sequenceName="SEQ_SEARCH")
|
||||||
@Column(name = "PID")
|
@Column(name = "PID")
|
||||||
private Long myId;
|
private Long myId;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy="mySearch")
|
||||||
|
private Collection<SearchInclude> myIncludes;
|
||||||
|
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
@Column(name="LAST_UPDATED_HIGH", nullable=true, insertable=true, updatable=false)
|
||||||
|
private Date myLastUpdatedHigh;
|
||||||
|
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
@Column(name="LAST_UPDATED_LOW", nullable=true, insertable=true, updatable=false)
|
||||||
|
private Date myLastUpdatedLow;
|
||||||
|
|
||||||
|
@Column(name="PREFERRED_PAGE_SIZE", nullable=true)
|
||||||
|
private Integer myPreferredPageSize;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy="mySearch")
|
||||||
|
private Collection<SearchResult> myResults;
|
||||||
|
|
||||||
@Column(name="TOTAL_COUNT")
|
@Column(name="TOTAL_COUNT")
|
||||||
private int myTotalCount;
|
private int myTotalCount;
|
||||||
|
|
||||||
@Id
|
@Column(name="SEARCH_UUID", length=40, nullable=false, updatable=false)
|
||||||
@Column(name="SEARCH_UUID", length=40, nullable=false)
|
|
||||||
private String myUuid;
|
private String myUuid;
|
||||||
|
|
||||||
public Date getCreated() {
|
public Date getCreated() {
|
||||||
return myCreated;
|
return myCreated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EverythingModeEnum getEverythingMode() {
|
||||||
|
return myEverythingMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return myId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<SearchInclude> getIncludes() {
|
||||||
|
if (myIncludes == null) {
|
||||||
|
myIncludes = new ArrayList<SearchInclude>();
|
||||||
|
}
|
||||||
|
return myIncludes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateRangeParam getLastUpdated() {
|
||||||
|
if (myLastUpdatedLow == null && myLastUpdatedHigh == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return new DateRangeParam(myLastUpdatedLow, myLastUpdatedHigh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPreferredPageSize() {
|
||||||
|
return myPreferredPageSize;
|
||||||
|
}
|
||||||
|
|
||||||
public int getTotalCount() {
|
public int getTotalCount() {
|
||||||
return myTotalCount;
|
return myTotalCount;
|
||||||
}
|
}
|
||||||
|
@ -79,6 +139,24 @@ public class Search implements Serializable {
|
||||||
myCreated = theCreated;
|
myCreated = theCreated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEverythingMode(EverythingModeEnum theEverythingMode) {
|
||||||
|
myEverythingMode = theEverythingMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastUpdated(DateRangeParam theLastUpdated) {
|
||||||
|
if (theLastUpdated == null) {
|
||||||
|
myLastUpdatedLow = null;
|
||||||
|
myLastUpdatedHigh = null;
|
||||||
|
} else {
|
||||||
|
myLastUpdatedLow = theLastUpdated.getLowerBoundAsInstant();
|
||||||
|
myLastUpdatedHigh = theLastUpdated.getUpperBoundAsInstant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreferredPageSize(Integer thePreferredPageSize) {
|
||||||
|
myPreferredPageSize = thePreferredPageSize;
|
||||||
|
}
|
||||||
|
|
||||||
public void setTotalCount(int theTotalCount) {
|
public void setTotalCount(int theTotalCount) {
|
||||||
myTotalCount = theTotalCount;
|
myTotalCount = theTotalCount;
|
||||||
}
|
}
|
||||||
|
@ -87,4 +165,22 @@ public class Search implements Serializable {
|
||||||
myUuid = theUuid;
|
myUuid = theUuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Set<Include> toIncList(boolean theWantReverse) {
|
||||||
|
HashSet<Include> retVal = new HashSet<Include>();
|
||||||
|
for (SearchInclude next : getIncludes()) {
|
||||||
|
if (theWantReverse == next.isReverse()) {
|
||||||
|
retVal.add(new Include(next.getInclude(), next.isRecurse()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableSet(retVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Include> toIncludesList() {
|
||||||
|
return toIncList(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Include> toRevIncludesList() {
|
||||||
|
return toIncList(true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
package ca.uhn.fhir.jpa.entity;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2016 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.ForeignKey;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.SequenceGenerator;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
@Entity
|
||||||
|
@Table(name = "HFJ_SEARCH_INCLUDE")
|
||||||
|
//@formatter:on
|
||||||
|
public class SearchInclude implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SEARCH_INC")
|
||||||
|
@SequenceGenerator(name = "SEQ_SEARCH_INC", sequenceName = "SEQ_SEARCH_INC")
|
||||||
|
@Id
|
||||||
|
@Column(name = "PID")
|
||||||
|
private Long myId;
|
||||||
|
|
||||||
|
@Column(name = "REVINCLUDE", insertable = true, updatable = false, nullable = false)
|
||||||
|
private boolean myReverse;
|
||||||
|
|
||||||
|
public boolean isReverse() {
|
||||||
|
return myReverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Column(name = "SEARCH_INCLUDE", length = 200, insertable = true, updatable = false, nullable = false)
|
||||||
|
private String myInclude;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "SEARCH_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_SEARCHINC_SEARCH"), insertable = true, updatable = false, nullable = false)
|
||||||
|
private Search mySearch;
|
||||||
|
|
||||||
|
@Column(name="SEARCH_PID", insertable=false, updatable=false, nullable=false)
|
||||||
|
private Long mySearchPid;
|
||||||
|
|
||||||
|
@Column(name = "INC_RECURSE", insertable = true, updatable = false, nullable = false)
|
||||||
|
private boolean myRecurse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public SearchInclude() {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public SearchInclude(Search theSearch, String theInclude, boolean theReverse, boolean theRecurse) {
|
||||||
|
mySearch = theSearch;
|
||||||
|
myInclude = theInclude;
|
||||||
|
myReverse = theReverse;
|
||||||
|
myRecurse = theRecurse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object theObj) {
|
||||||
|
if (!(theObj instanceof SearchInclude)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (myId == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return myId.equals(((SearchInclude) theObj).myId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInclude() {
|
||||||
|
return myInclude;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return myId == null ? 0 : myId.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRecurse() {
|
||||||
|
return myRecurse;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -32,10 +32,12 @@ import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.ManyToOne;
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.SequenceGenerator;
|
import javax.persistence.SequenceGenerator;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.UniqueConstraint;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "HFJ_SEARCH_RESULT", uniqueConstraints= {
|
@Table(name = "HFJ_SEARCH_RESULT", uniqueConstraints= {
|
||||||
|
@UniqueConstraint(name="IDX_SEARCHRES_ORDER", columnNames= {"SEARCH_PID", "SEARCH_ORDER"})
|
||||||
})
|
})
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
public class SearchResult implements Serializable {
|
public class SearchResult implements Serializable {
|
||||||
|
@ -48,6 +50,9 @@ public class SearchResult implements Serializable {
|
||||||
@Column(name = "PID")
|
@Column(name = "PID")
|
||||||
private Long myId;
|
private Long myId;
|
||||||
|
|
||||||
|
@Column(name="SEARCH_ORDER", nullable=false)
|
||||||
|
private int myOrder;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name="RESOURCE_PID", referencedColumnName="RES_ID", foreignKey=@ForeignKey(name="FK_SEARCHRES_RES"), insertable=false, updatable=false, nullable=false)
|
@JoinColumn(name="RESOURCE_PID", referencedColumnName="RES_ID", foreignKey=@ForeignKey(name="FK_SEARCHRES_RES"), insertable=false, updatable=false, nullable=false)
|
||||||
private ResourceTable myResource;
|
private ResourceTable myResource;
|
||||||
|
@ -59,6 +64,9 @@ public class SearchResult implements Serializable {
|
||||||
@JoinColumn(name="SEARCH_PID", referencedColumnName="PID", foreignKey=@ForeignKey(name="FK_SEARCHRES_SEARCH"))
|
@JoinColumn(name="SEARCH_PID", referencedColumnName="PID", foreignKey=@ForeignKey(name="FK_SEARCHRES_SEARCH"))
|
||||||
private Search mySearch;
|
private Search mySearch;
|
||||||
|
|
||||||
|
@Column(name="SEARCH_PID", insertable=false, updatable=false, nullable=false)
|
||||||
|
private Long mySearchPid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
|
@ -73,6 +81,31 @@ public class SearchResult implements Serializable {
|
||||||
mySearch = theSearch;
|
mySearch = theSearch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object theObj) {
|
||||||
|
if (!(theObj instanceof SearchResult)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return myResourcePid.equals(((SearchResult)theObj).myResourcePid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrder() {
|
||||||
|
return myOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getResourcePid() {
|
||||||
|
return myResourcePid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return myResourcePid.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrder(int theOrder) {
|
||||||
|
myOrder = theOrder;
|
||||||
|
}
|
||||||
|
|
||||||
public void setResourcePid(Long theResourcePid) {
|
public void setResourcePid(Long theResourcePid) {
|
||||||
myResourcePid = theResourcePid;
|
myResourcePid = theResourcePid;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,9 @@ import java.util.TreeMap;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.FhirSearchDao.Suggestion;
|
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchDao;
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
import ca.uhn.fhir.model.api.annotation.Description;
|
import ca.uhn.fhir.model.api.annotation.Description;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
|
@ -57,7 +57,7 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProvider<Bundle, MetaDt
|
||||||
private IFhirSystemDao<Bundle, MetaDt> mySystemDao;
|
private IFhirSystemDao<Bundle, MetaDt> mySystemDao;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchDao mySearchDao;
|
private IFulltextSearchSvc mySearchDao;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
// This is generated by hand:
|
// This is generated by hand:
|
||||||
|
|
|
@ -38,10 +38,10 @@ import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.FhirSearchDao.Suggestion;
|
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion;
|
||||||
import ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider;
|
import ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchDao;
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
import ca.uhn.fhir.model.api.annotation.Description;
|
import ca.uhn.fhir.model.api.annotation.Description;
|
||||||
import ca.uhn.fhir.model.primitive.IntegerDt;
|
import ca.uhn.fhir.model.primitive.IntegerDt;
|
||||||
import ca.uhn.fhir.rest.annotation.Operation;
|
import ca.uhn.fhir.rest.annotation.Operation;
|
||||||
|
@ -59,7 +59,7 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProvider<Bundle, Meta>
|
||||||
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchDao mySearchDao;
|
private IFulltextSearchSvc mySearchDao;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
// This is generated by hand:
|
// This is generated by hand:
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package ca.uhn.fhir.jpa.search;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||||
|
import ca.uhn.fhir.jpa.dao.SearchBuilder.BundleProviderPersisted;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||||
|
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
|
|
||||||
|
public class DatabaseBackedPagingProvider extends FifoMemoryPagingProvider {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PlatformTransactionManager thePlatformTransactionManager;
|
||||||
|
@Autowired
|
||||||
|
private ISearchResultDao theSearchResultDao;
|
||||||
|
@Autowired
|
||||||
|
private EntityManager theEntityManager;
|
||||||
|
@Autowired
|
||||||
|
private FhirContext theContext;
|
||||||
|
@Autowired
|
||||||
|
private IFhirSystemDao<?, ?> theDao;
|
||||||
|
|
||||||
|
public DatabaseBackedPagingProvider(int theSize) {
|
||||||
|
super(theSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized IBundleProvider retrieveResultList(String theId) {
|
||||||
|
IBundleProvider retVal = super.retrieveResultList(theId);
|
||||||
|
if (retVal == null) {
|
||||||
|
BundleProviderPersisted provider = new SearchBuilder.BundleProviderPersisted(theId, thePlatformTransactionManager, theSearchResultDao, theEntityManager,
|
||||||
|
theContext, theDao);
|
||||||
|
if (!provider.ensureSearchEntityLoaded()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String storeResultList(IBundleProvider theList) {
|
||||||
|
if (theList instanceof SearchBuilder.BundleProviderPersisted) {
|
||||||
|
return ((BundleProviderPersisted)theList).getSearchUuid();
|
||||||
|
}
|
||||||
|
return super.storeResultList(theList);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package ca.uhn.fhir.jpa.search;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
import org.springframework.transaction.TransactionStatus;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||||
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||||
|
import ca.uhn.fhir.jpa.entity.Search;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes old searches
|
||||||
|
*/
|
||||||
|
public class StaleSearchDeletingSvc {
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StaleSearchDeletingSvc.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ISearchDao mySearchDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DaoConfig myDaoConfig;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ISearchResultDao mySearchResultDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ISearchIncludeDao mySearchIncludeDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PlatformTransactionManager myTransactionManager;
|
||||||
|
|
||||||
|
@Scheduled(fixedDelay = 10 * DateUtils.MILLIS_PER_SECOND)
|
||||||
|
@Transactional(propagation = Propagation.NOT_SUPPORTED)
|
||||||
|
public synchronized void pollForStaleSearches() {
|
||||||
|
Date cutoff = new Date(System.currentTimeMillis() - myDaoConfig.getExpireSearchResultsAfterMillis());
|
||||||
|
ourLog.debug("Searching for searches which are before {}", cutoff);
|
||||||
|
|
||||||
|
Collection<Search> toDelete = mySearchDao.findWhereCreatedBefore(cutoff);
|
||||||
|
if (toDelete.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransactionTemplate tt = new TransactionTemplate(myTransactionManager);
|
||||||
|
for (final Search next : toDelete) {
|
||||||
|
tt.execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@Override
|
||||||
|
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
|
||||||
|
Search searchToDelete = mySearchDao.findOne(next.getId());
|
||||||
|
ourLog.info("Expiring stale search {} / {}", searchToDelete.getId(), searchToDelete.getUuid());
|
||||||
|
mySearchIncludeDao.deleteForSearch(searchToDelete.getId());
|
||||||
|
mySearchResultDao.deleteForSearch(searchToDelete.getId());
|
||||||
|
mySearchDao.delete(searchToDelete);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -8,14 +8,42 @@ import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.hibernate.internal.SessionFactoryRegistry;
|
import org.hibernate.search.jpa.Search;
|
||||||
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
import org.springframework.transaction.TransactionStatus;
|
||||||
|
import org.springframework.transaction.support.TransactionCallback;
|
||||||
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.entity.ForcedId;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceHistoryTag;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamQuantity;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamUri;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceLink;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceTag;
|
||||||
|
import ca.uhn.fhir.jpa.entity.SearchInclude;
|
||||||
|
import ca.uhn.fhir.jpa.entity.SearchResult;
|
||||||
|
import ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource;
|
||||||
|
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
|
||||||
|
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
||||||
|
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
|
||||||
|
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||||
|
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||||
|
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
|
||||||
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
|
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
||||||
|
@ -114,4 +142,59 @@ public class BaseJpaTest {
|
||||||
return bundleStr;
|
return bundleStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void purgeDatabase(final EntityManager entityManager, PlatformTransactionManager theTxManager) {
|
||||||
|
TransactionTemplate txTemplate = new TransactionTemplate(theTxManager);
|
||||||
|
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
||||||
|
txTemplate.execute(new TransactionCallback<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void doInTransaction(TransactionStatus theStatus) {
|
||||||
|
entityManager.createQuery("UPDATE " + ResourceHistoryTable.class.getSimpleName() + " d SET d.myForcedId = null").executeUpdate();
|
||||||
|
entityManager.createQuery("UPDATE " + ResourceTable.class.getSimpleName() + " d SET d.myForcedId = null").executeUpdate();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
txTemplate.execute(new TransactionCallback<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void doInTransaction(TransactionStatus theStatus) {
|
||||||
|
entityManager.createQuery("DELETE from " + SubscriptionFlaggedResource.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + ForcedId.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamDate.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamNumber.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamQuantity.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamString.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamToken.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamUri.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamCoords.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + ResourceLink.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + SearchResult.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + SearchInclude.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + TermConceptParentChildLink.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
txTemplate.execute(new TransactionCallback<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void doInTransaction(TransactionStatus theStatus) {
|
||||||
|
entityManager.createQuery("DELETE from " + TermConcept.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + TermCodeSystemVersion.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + TermCodeSystem.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
txTemplate.execute(new TransactionCallback<Void>() {
|
||||||
|
@Override
|
||||||
|
public Void doInTransaction(TransactionStatus theStatus) {
|
||||||
|
entityManager.createQuery("DELETE from " + SubscriptionTable.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + ResourceHistoryTag.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + ResourceTag.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + TagDefinition.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + ResourceHistoryTable.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + ResourceTable.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + Search.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,7 @@ import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
import org.springframework.transaction.TransactionStatus;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.transaction.support.TransactionCallback;
|
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
@ -34,23 +32,9 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDaoPatient;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSubscription;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSubscription;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchDao;
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
import ca.uhn.fhir.jpa.entity.ForcedId;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTag;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamQuantity;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamUri;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceLink;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceTag;
|
|
||||||
import ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource;
|
|
||||||
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
|
|
||||||
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
|
||||||
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
||||||
|
@ -89,7 +73,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ApplicationContext myAppCtx;
|
protected ApplicationContext myAppCtx;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ISearchDao mySearchDao;
|
protected IFulltextSearchSvc mySearchDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("myConceptMapDaoDstu2")
|
@Qualifier("myConceptMapDaoDstu2")
|
||||||
protected IFhirResourceDao<ConceptMap> myConceptMapDao;
|
protected IFhirResourceDao<ConceptMap> myConceptMapDao;
|
||||||
|
@ -221,45 +205,4 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void purgeDatabase(final EntityManager entityManager, PlatformTransactionManager theTxManager) {
|
|
||||||
TransactionTemplate txTemplate = new TransactionTemplate(theTxManager);
|
|
||||||
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
|
||||||
txTemplate.execute(new TransactionCallback<Void>() {
|
|
||||||
@Override
|
|
||||||
public Void doInTransaction(TransactionStatus theStatus) {
|
|
||||||
entityManager.createQuery("UPDATE " + ResourceHistoryTable.class.getSimpleName() + " d SET d.myForcedId = null").executeUpdate();
|
|
||||||
entityManager.createQuery("UPDATE " + ResourceTable.class.getSimpleName() + " d SET d.myForcedId = null").executeUpdate();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
txTemplate.execute(new TransactionCallback<Void>() {
|
|
||||||
@Override
|
|
||||||
public Void doInTransaction(TransactionStatus theStatus) {
|
|
||||||
entityManager.createQuery("DELETE from " + SubscriptionFlaggedResource.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ForcedId.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamDate.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamNumber.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamQuantity.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamString.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamToken.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamUri.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamCoords.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceLink.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
txTemplate.execute(new TransactionCallback<Void>() {
|
|
||||||
@Override
|
|
||||||
public Void doInTransaction(TransactionStatus theStatus) {
|
|
||||||
entityManager.createQuery("DELETE from " + SubscriptionTable.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceHistoryTag.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceTag.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + TagDefinition.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceHistoryTable.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceTable.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.FhirSearchDao.Suggestion;
|
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Device;
|
import ca.uhn.fhir.model.dstu2.resource.Device;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Media;
|
import ca.uhn.fhir.model.dstu2.resource.Media;
|
||||||
|
|
|
@ -9,7 +9,7 @@ import java.util.List;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchDao;
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
@ -22,7 +22,7 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
public class FhirSearchDaoDstu2Test extends BaseJpaDstu2Test {
|
public class FhirSearchDaoDstu2Test extends BaseJpaDstu2Test {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchDao mySearchDao;
|
private IFulltextSearchSvc mySearchDao;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testContentSearch() {
|
public void testContentSearch() {
|
||||||
|
|
|
@ -48,9 +48,7 @@ import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
import org.springframework.transaction.TransactionDefinition;
|
import org.springframework.transaction.TransactionDefinition;
|
||||||
import org.springframework.transaction.TransactionStatus;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.transaction.support.TransactionCallback;
|
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
@ -62,30 +60,13 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDaoPatient;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSubscription;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSubscription;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchDao;
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
||||||
import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest;
|
import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest;
|
||||||
import ca.uhn.fhir.jpa.entity.ForcedId;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTag;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamQuantity;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamUri;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceLink;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceTag;
|
|
||||||
import ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource;
|
|
||||||
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
|
|
||||||
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
|
||||||
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
|
|
||||||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
|
||||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
|
||||||
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
|
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||||
|
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvc;
|
||||||
import ca.uhn.fhir.jpa.term.ITerminologySvc;
|
import ca.uhn.fhir.jpa.term.ITerminologySvc;
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.rest.method.MethodUtil;
|
import ca.uhn.fhir.rest.method.MethodUtil;
|
||||||
|
@ -96,29 +77,14 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
@ContextConfiguration(classes= {TestDstu3Config.class})
|
@ContextConfiguration(classes= {TestDstu3Config.class})
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
||||||
@Autowired
|
|
||||||
protected IResourceTableDao myResourceTableDao;
|
|
||||||
@Autowired
|
|
||||||
protected ITerminologySvc myTermSvc;
|
|
||||||
@Autowired
|
|
||||||
@Qualifier("myJpaValidationSupportChainDstu3")
|
|
||||||
protected IValidationSupport myValidationSupport;
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ApplicationContext myAppCtx;
|
protected ApplicationContext myAppCtx;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ISearchDao mySearchDao;
|
|
||||||
@Autowired
|
|
||||||
@Qualifier("myConceptMapDaoDstu3")
|
|
||||||
protected IFhirResourceDao<ConceptMap> myConceptMapDao;
|
|
||||||
@Autowired
|
|
||||||
@Qualifier("myCodeSystemDaoDstu3")
|
@Qualifier("myCodeSystemDaoDstu3")
|
||||||
protected IFhirResourceDao<CodeSystem> myCodeSystemDao;
|
protected IFhirResourceDao<CodeSystem> myCodeSystemDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("myMedicationDaoDstu3")
|
@Qualifier("myConceptMapDaoDstu3")
|
||||||
protected IFhirResourceDao<Medication> myMedicationDao;
|
protected IFhirResourceDao<ConceptMap> myConceptMapDao;
|
||||||
@Autowired
|
|
||||||
@Qualifier("myMedicationOrderDaoDstu3")
|
|
||||||
protected IFhirResourceDao<MedicationOrder> myMedicationOrderDao;
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected DaoConfig myDaoConfig;
|
protected DaoConfig myDaoConfig;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -133,11 +99,9 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("myEncounterDaoDstu3")
|
@Qualifier("myEncounterDaoDstu3")
|
||||||
protected IFhirResourceDao<Encounter> myEncounterDao;
|
protected IFhirResourceDao<Encounter> myEncounterDao;
|
||||||
|
// @PersistenceContext()
|
||||||
// @PersistenceContext()
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected EntityManager myEntityManager;
|
protected EntityManager myEntityManager;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("myFhirContextDstu3")
|
@Qualifier("myFhirContextDstu3")
|
||||||
protected FhirContext myFhirCtx;
|
protected FhirContext myFhirCtx;
|
||||||
|
@ -149,6 +113,20 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
||||||
@Qualifier("myLocationDaoDstu3")
|
@Qualifier("myLocationDaoDstu3")
|
||||||
protected IFhirResourceDao<Location> myLocationDao;
|
protected IFhirResourceDao<Location> myLocationDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Qualifier("myMediaDaoDstu3")
|
||||||
|
protected IFhirResourceDao<Media> myMediaDao;
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("myMedicationDaoDstu3")
|
||||||
|
protected IFhirResourceDao<Medication> myMedicationDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("myMedicationOrderDaoDstu3")
|
||||||
|
protected IFhirResourceDao<MedicationOrder> myMedicationOrderDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("myNamingSystemDaoDstu3")
|
||||||
|
protected IFhirResourceDao<NamingSystem> myNamingSystemDao;
|
||||||
|
@Autowired
|
||||||
@Qualifier("myObservationDaoDstu3")
|
@Qualifier("myObservationDaoDstu3")
|
||||||
protected IFhirResourceDao<Observation> myObservationDao;
|
protected IFhirResourceDao<Observation> myObservationDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -158,12 +136,6 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
||||||
@Qualifier("myPatientDaoDstu3")
|
@Qualifier("myPatientDaoDstu3")
|
||||||
protected IFhirResourceDaoPatient<Patient> myPatientDao;
|
protected IFhirResourceDaoPatient<Patient> myPatientDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("myNamingSystemDaoDstu3")
|
|
||||||
protected IFhirResourceDao<NamingSystem> myNamingSystemDao;
|
|
||||||
@Autowired
|
|
||||||
@Qualifier("myMediaDaoDstu3")
|
|
||||||
protected IFhirResourceDao<Media> myMediaDao;
|
|
||||||
@Autowired
|
|
||||||
@Qualifier("myPractitionerDaoDstu3")
|
@Qualifier("myPractitionerDaoDstu3")
|
||||||
protected IFhirResourceDao<Practitioner> myPractitionerDao;
|
protected IFhirResourceDao<Practitioner> myPractitionerDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -176,6 +148,12 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
||||||
@Qualifier("myResourceProvidersDstu3")
|
@Qualifier("myResourceProvidersDstu3")
|
||||||
protected Object myResourceProviders;
|
protected Object myResourceProviders;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
protected IResourceTableDao myResourceTableDao;
|
||||||
|
@Autowired
|
||||||
|
protected IFulltextSearchSvc mySearchDao;
|
||||||
|
@Autowired
|
||||||
|
protected StaleSearchDeletingSvc myStaleSearchDeletingSvc;
|
||||||
|
@Autowired
|
||||||
@Qualifier("myStructureDefinitionDaoDstu3")
|
@Qualifier("myStructureDefinitionDaoDstu3")
|
||||||
protected IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
|
protected IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -191,8 +169,13 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
||||||
@Qualifier("mySystemProviderDstu3")
|
@Qualifier("mySystemProviderDstu3")
|
||||||
protected JpaSystemProviderDstu3 mySystemProvider;
|
protected JpaSystemProviderDstu3 mySystemProvider;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
protected ITerminologySvc myTermSvc;
|
||||||
|
@Autowired
|
||||||
protected PlatformTransactionManager myTxManager;
|
protected PlatformTransactionManager myTxManager;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Qualifier("myJpaValidationSupportChainDstu3")
|
||||||
|
protected IValidationSupport myValidationSupport;
|
||||||
|
@Autowired
|
||||||
@Qualifier("myValueSetDaoDstu3")
|
@Qualifier("myValueSetDaoDstu3")
|
||||||
protected IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> myValueSetDao;
|
protected IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> myValueSetDao;
|
||||||
|
|
||||||
|
@ -213,14 +196,6 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
||||||
myDaoConfig.setSchedulingDisabled(true);
|
myDaoConfig.setSchedulingDisabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
|
||||||
public void beforeResetConfig() {
|
|
||||||
myDaoConfig.setHardSearchLimit(1000);
|
|
||||||
myDaoConfig.setHardTagListLimit(1000);
|
|
||||||
myDaoConfig.setIncludeLimit(2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@Transactional()
|
@Transactional()
|
||||||
public void beforePurgeDatabase() {
|
public void beforePurgeDatabase() {
|
||||||
|
@ -228,6 +203,14 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
||||||
purgeDatabase(entityManager, myTxManager);
|
purgeDatabase(entityManager, myTxManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void beforeResetConfig() {
|
||||||
|
myDaoConfig.setHardSearchLimit(1000);
|
||||||
|
myDaoConfig.setHardTagListLimit(1000);
|
||||||
|
myDaoConfig.setIncludeLimit(2000);
|
||||||
|
}
|
||||||
|
|
||||||
protected <T extends IBaseResource> T loadResourceFromClasspath(Class<T> type, String resourceName) throws IOException {
|
protected <T extends IBaseResource> T loadResourceFromClasspath(Class<T> type, String resourceName) throws IOException {
|
||||||
InputStream stream = FhirResourceDaoDstu2SearchNoFtTest.class.getResourceAsStream(resourceName);
|
InputStream stream = FhirResourceDaoDstu2SearchNoFtTest.class.getResourceAsStream(resourceName);
|
||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
|
@ -245,55 +228,4 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void purgeDatabase(final EntityManager entityManager, PlatformTransactionManager theTxManager) {
|
|
||||||
TransactionTemplate txTemplate = new TransactionTemplate(theTxManager);
|
|
||||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
|
||||||
txTemplate.execute(new TransactionCallback<Void>() {
|
|
||||||
@Override
|
|
||||||
public Void doInTransaction(TransactionStatus theStatus) {
|
|
||||||
entityManager.createQuery("UPDATE " + ResourceHistoryTable.class.getSimpleName() + " d SET d.myForcedId = null").executeUpdate();
|
|
||||||
entityManager.createQuery("UPDATE " + ResourceTable.class.getSimpleName() + " d SET d.myForcedId = null").executeUpdate();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
txTemplate.execute(new TransactionCallback<Void>() {
|
|
||||||
@Override
|
|
||||||
public Void doInTransaction(TransactionStatus theStatus) {
|
|
||||||
entityManager.createQuery("DELETE from " + TermConceptParentChildLink.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + TermConcept.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + TermCodeSystemVersion.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + TermCodeSystem.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
txTemplate.execute(new TransactionCallback<Void>() {
|
|
||||||
@Override
|
|
||||||
public Void doInTransaction(TransactionStatus theStatus) {
|
|
||||||
entityManager.createQuery("DELETE from " + SubscriptionFlaggedResource.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ForcedId.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamDate.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamNumber.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamQuantity.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamString.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamToken.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamUri.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamCoords.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceLink.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
txTemplate.execute(new TransactionCallback<Void>() {
|
|
||||||
@Override
|
|
||||||
public Void doInTransaction(TransactionStatus theStatus) {
|
|
||||||
entityManager.createQuery("DELETE from " + SubscriptionTable.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceHistoryTag.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceTag.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + TagDefinition.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceHistoryTable.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
entityManager.createQuery("DELETE from " + ResourceTable.class.getSimpleName() + " d").executeUpdate();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.FhirSearchDao.Suggestion;
|
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||||
import ca.uhn.fhir.rest.param.StringAndListParam;
|
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||||
import ca.uhn.fhir.rest.param.StringOrListParam;
|
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||||
|
|
|
@ -11,7 +11,7 @@ import org.hl7.fhir.dstu3.model.Patient;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchDao;
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||||
import ca.uhn.fhir.rest.param.StringAndListParam;
|
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||||
import ca.uhn.fhir.rest.param.StringOrListParam;
|
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||||
|
@ -22,7 +22,7 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
public class FhirSearchDaoDstu3Test extends BaseJpaDstu3Test {
|
public class FhirSearchDaoDstu3Test extends BaseJpaDstu3Test {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchDao mySearchDao;
|
private IFulltextSearchSvc mySearchDao;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testContentSearch() {
|
public void testContentSearch() {
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package ca.uhn.fhir.jpa.provider;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum;
|
||||||
|
|
||||||
|
public class SearchParameterMapTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Search} uses these ordinals so they shouldn't get out of order
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testEverythingOrdinals() {
|
||||||
|
assertEquals(0, EverythingModeEnum.ENCOUNTER_INSTANCE.ordinal());
|
||||||
|
assertEquals(1, EverythingModeEnum.ENCOUNTER_TYPE.ordinal());
|
||||||
|
assertEquals(2, EverythingModeEnum.PATIENT_INSTANCE.ordinal());
|
||||||
|
assertEquals(3, EverythingModeEnum.PATIENT_TYPE.ordinal());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.config.dstu3.WebsocketDstu3Config;
|
import ca.uhn.fhir.jpa.config.dstu3.WebsocketDstu3Config;
|
||||||
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
|
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
|
||||||
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||||
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
|
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
|
@ -78,7 +79,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void after() {
|
public void after() throws Exception {
|
||||||
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
|
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
||||||
confProvider.setImplementationDescription("THIS IS THE DESC");
|
confProvider.setImplementationDescription("THIS IS THE DESC");
|
||||||
ourRestServer.setServerConformanceProvider(confProvider);
|
ourRestServer.setServerConformanceProvider(confProvider);
|
||||||
|
|
||||||
ourRestServer.setPagingProvider(new FifoMemoryPagingProvider(10));
|
ourRestServer.setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
|
||||||
|
|
||||||
Server server = new Server(ourPort);
|
Server server = new Server(ourPort);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package ca.uhn.fhir.jpa.provider.dstu3;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.blankOrNullString;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
import org.hl7.fhir.dstu3.model.Bundle;
|
||||||
|
import org.hl7.fhir.dstu3.model.Bundle.BundleLinkComponent;
|
||||||
|
import org.hl7.fhir.dstu3.model.Patient;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.gclient.IQuery;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
|
||||||
|
public class StaleSearchDeletingSvcDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StaleSearchDeletingSvcDstu3Test.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void after() throws Exception {
|
||||||
|
super.after();
|
||||||
|
|
||||||
|
myDaoConfig.setExpireSearchResultsAfterMillis(DateUtils.MILLIS_PER_HOUR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEverythingInstanceWithContentFilter() throws Exception {
|
||||||
|
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
Patient pt1 = new Patient();
|
||||||
|
pt1.addName().addFamily("Everything").addGiven("Arthur");
|
||||||
|
myPatientDao.create(pt1, new ServletRequestDetails()).getId().toUnqualifiedVersionless();
|
||||||
|
}
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
IQuery<Bundle> search = ourClient
|
||||||
|
.search()
|
||||||
|
.forResource(Patient.class)
|
||||||
|
.where(Patient.NAME.matches().value("Everything"))
|
||||||
|
.returnBundle(Bundle.class);
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
Bundle resp1 = search.execute();
|
||||||
|
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
search.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
BundleLinkComponent nextLink = resp1.getLink("next");
|
||||||
|
assertNotNull(nextLink);
|
||||||
|
String nextLinkUrl = nextLink.getUrl();
|
||||||
|
assertThat(nextLinkUrl, not(blankOrNullString()));
|
||||||
|
|
||||||
|
Bundle resp2 = ourClient.search().byUrl(nextLinkUrl).returnBundle(Bundle.class).execute();
|
||||||
|
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp2));
|
||||||
|
|
||||||
|
myStaleSearchDeletingSvc.pollForStaleSearches();
|
||||||
|
|
||||||
|
ourClient.search().byUrl(nextLinkUrl).returnBundle(Bundle.class).execute();
|
||||||
|
|
||||||
|
Thread.sleep(20);
|
||||||
|
myDaoConfig.setExpireSearchResultsAfterMillis(10);
|
||||||
|
myStaleSearchDeletingSvc.pollForStaleSearches();
|
||||||
|
|
||||||
|
try {
|
||||||
|
ourClient.search().byUrl(nextLinkUrl).returnBundle(Bundle.class).execute();
|
||||||
|
fail();
|
||||||
|
} catch (ResourceGoneException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("does not exist and may have expired"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu1;
|
||||||
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||||
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
|
@ -127,15 +128,17 @@ public class JpaServerDemo extends RestfulServer {
|
||||||
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default to XML and pretty printing
|
* Default to JSON and pretty printing
|
||||||
*/
|
*/
|
||||||
setDefaultPrettyPrint(true);
|
setDefaultPrettyPrint(true);
|
||||||
setDefaultResponseEncoding(EncodingEnum.JSON);
|
setDefaultResponseEncoding(EncodingEnum.JSON);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a simple paging strategy that keeps the last 10 searches in memory
|
* -- New in HAPI FHIR 1.5 --
|
||||||
|
* This configures the server to page search results to and from
|
||||||
|
* the database
|
||||||
*/
|
*/
|
||||||
setPagingProvider(new FifoMemoryPagingProvider(10));
|
setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load interceptors for the server from Spring (these are defined in FhirServerConfig.java)
|
* Load interceptors for the server from Spring (these are defined in FhirServerConfig.java)
|
||||||
|
|
|
@ -183,6 +183,14 @@
|
||||||
longer includes _revinclude matches in the Bundle.total count, or the
|
longer includes _revinclude matches in the Bundle.total count, or the
|
||||||
page size limit.
|
page size limit.
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
JPA server now persists search results to the database in a new table where they
|
||||||
|
can be temporaily preserved. This makes the JPA server much more scalable, since it
|
||||||
|
no longer needs to store large lists of pages in memory between search invocations.
|
||||||
|
<![CDATA[<br/><br/>]]>
|
||||||
|
Old searches are deleted after an hour by default, but this can be changed
|
||||||
|
via a setting in the DaoConfig.
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.4" date="2016-02-04">
|
<release version="1.4" date="2016-02-04">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
Loading…
Reference in New Issue