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 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.TerminologySvcImpl;
|
||||
|
||||
|
@ -62,6 +64,11 @@ public class BaseConfig implements SchedulingConfigurer {
|
|||
theTaskRegistrar.setTaskScheduler(taskScheduler());
|
||||
}
|
||||
|
||||
@Bean(autowire = Autowire.BY_TYPE)
|
||||
public DatabaseBackedPagingProvider databaseBackedPagingProvider() {
|
||||
return new DatabaseBackedPagingProvider(10);
|
||||
}
|
||||
|
||||
@Bean(name = "myFhirContextDstu1")
|
||||
@Lazy
|
||||
public FhirContext fhirContextDstu1() {
|
||||
|
@ -99,8 +106,8 @@ public class BaseConfig implements SchedulingConfigurer {
|
|||
}
|
||||
|
||||
@Bean(autowire=Autowire.BY_TYPE)
|
||||
public ITerminologySvc terminologyService() {
|
||||
return new TerminologySvcImpl();
|
||||
public StaleSearchDeletingSvc staleSearchDeletingSvc() {
|
||||
return new StaleSearchDeletingSvc();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
@ -110,12 +117,9 @@ public class BaseConfig implements SchedulingConfigurer {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* This lets the "@Value" fields reference properties from the properties file
|
||||
*/
|
||||
@Bean
|
||||
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
@Bean(autowire = Autowire.BY_TYPE)
|
||||
public ITerminologySvc terminologyService() {
|
||||
return new TerminologySvcImpl();
|
||||
}
|
||||
|
||||
// @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 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.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||
|
||||
@Configuration
|
||||
|
@ -42,6 +42,18 @@ public class BaseDstu2Config extends BaseConfig {
|
|||
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)
|
||||
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();
|
||||
|
@ -54,17 +66,4 @@ public class BaseDstu2Config extends BaseConfig {
|
|||
retVal.setDao(systemDaoDstu2());
|
||||
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.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.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
|
||||
|
@ -50,6 +50,35 @@ public class BaseDstu3Config extends BaseConfig {
|
|||
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)
|
||||
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();
|
||||
|
@ -63,35 +92,6 @@ public class BaseDstu3Config extends BaseConfig {
|
|||
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")
|
||||
public IValidationSupport validationSupportChainDstu3() {
|
||||
return new JpaValidationSupportChainDstu3();
|
||||
|
|
|
@ -518,7 +518,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
|
||||
@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) {
|
||||
myResourceTypeToDao = new HashMap<Class<? extends IBaseResource>, IFhirResourceDao<?>>();
|
||||
for (IFhirResourceDao<?> next : myResourceDaos) {
|
||||
|
@ -959,6 +959,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType);
|
||||
|
||||
SearchParameterMap paramMap = translateMatchUrl(theMatchUrl, resourceDef);
|
||||
paramMap.setPersistResults(false);
|
||||
|
||||
if (paramMap.isEmpty()) {
|
||||
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;
|
||||
@Autowired(required = false)
|
||||
protected ISearchDao mySearchDao;
|
||||
protected IFulltextSearchSvc mySearchDao;
|
||||
@Autowired()
|
||||
protected ISearchResultDao mySearchResultDao;
|
||||
@Autowired()
|
||||
|
|
|
@ -40,6 +40,11 @@ public class DaoConfig {
|
|||
private boolean myAllowInlineMatchUrlReferences = false;
|
||||
|
||||
private boolean myAllowMultipleDelete;
|
||||
|
||||
// ***
|
||||
// update setter javadoc if default changes
|
||||
// ***
|
||||
private long myExpireSearchResultsAfterMillis = DateUtils.MILLIS_PER_HOUR;
|
||||
private int myHardSearchLimit = 1000;
|
||||
private int myHardTagListLimit = 1000;
|
||||
private int myIncludeLimit = 2000;
|
||||
|
@ -50,11 +55,22 @@ public class DaoConfig {
|
|||
private boolean myIndexContainedResources = true;
|
||||
|
||||
private List<IServerInterceptor> myInterceptors;
|
||||
|
||||
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
|
||||
|
||||
private boolean mySchedulingDisabled;
|
||||
private boolean mySubscriptionEnabled;
|
||||
private long mySubscriptionPollDelay = 1000;
|
||||
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)}
|
||||
*/
|
||||
|
@ -78,7 +94,6 @@ public class DaoConfig {
|
|||
}
|
||||
return myInterceptors;
|
||||
}
|
||||
|
||||
public ResourceEncodingEnum getResourceEncoding() {
|
||||
return myResourceEncoding;
|
||||
}
|
||||
|
@ -139,6 +154,16 @@ public class DaoConfig {
|
|||
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) {
|
||||
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.InvalidRequestException;
|
||||
|
||||
public class FhirSearchDao extends BaseHapiFhirDao<IBaseResource> implements ISearchDao {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSearchDao.class);
|
||||
public class FulltextSearchSvcImpl extends BaseHapiFhirDao<IBaseResource> implements IFulltextSearchSvc {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FulltextSearchSvcImpl.class);
|
||||
|
||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||
private EntityManager myEntityManager;
|
|
@ -23,6 +23,8 @@ package ca.uhn.fhir.jpa.dao;
|
|||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
|
@ -41,6 +43,8 @@ public interface IFhirSystemDao<T, MT> extends IDao {
|
|||
|
||||
TagList getAllTags(RequestDetails theRequestDetails);
|
||||
|
||||
public <R extends IBaseResource> IFhirResourceDao<R> getDao(Class<R> theType);
|
||||
|
||||
Map<String, Long> getResourceCounts();
|
||||
|
||||
IBundleProvider history(Date theDate, RequestDetails theRequestDetails);
|
||||
|
|
|
@ -22,9 +22,9 @@ package ca.uhn.fhir.jpa.dao;
|
|||
|
||||
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);
|
||||
|
|
@ -42,6 +42,7 @@ import java.util.Set;
|
|||
import java.util.UUID;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.Tuple;
|
||||
import javax.persistence.TypedQuery;
|
||||
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.IBaseResource;
|
||||
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.TransactionStatus;
|
||||
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.ResourceTag;
|
||||
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.TagTypeEnum;
|
||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
|
@ -129,20 +135,18 @@ public class SearchBuilder {
|
|||
private BaseHapiFhirDao<?> myCallingDao;
|
||||
private FhirContext myContext;
|
||||
private EntityManager myEntityManager;
|
||||
private SearchParameterMap myParams;
|
||||
private Collection<Long> myPids;
|
||||
private PlatformTransactionManager myPlatformTransactionManager;
|
||||
private IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
|
||||
private String myResourceName;
|
||||
private Class<? extends IBaseResource> myResourceType;
|
||||
private ISearchDao mySearchDao;
|
||||
private ISearchResultDao mySearchResultDao;
|
||||
private IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
|
||||
|
||||
private IFulltextSearchSvc mySearchDao;
|
||||
private Search mySearchEntity;
|
||||
private ISearchResultDao mySearchResultDao;
|
||||
|
||||
private boolean myHaveFlushedSearch;
|
||||
|
||||
private SearchParameterMap myParams;
|
||||
|
||||
public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager, PlatformTransactionManager thePlatformTransactionManager, ISearchDao theSearchDao, ISearchResultDao theSearchResultDao, BaseHapiFhirDao theDao, IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao) {
|
||||
public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager, PlatformTransactionManager thePlatformTransactionManager, IFulltextSearchSvc theSearchDao,
|
||||
ISearchResultDao theSearchResultDao, BaseHapiFhirDao theDao, IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao) {
|
||||
myContext = theFhirContext;
|
||||
myEntityManager = theEntityManager;
|
||||
myPlatformTransactionManager = thePlatformTransactionManager;
|
||||
|
@ -177,11 +181,11 @@ public class SearchBuilder {
|
|||
IQueryParameterType rightValue = cp.getRightValue();
|
||||
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)));
|
||||
|
||||
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>();
|
||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
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);
|
||||
|
||||
cq.where(builder.and(toArray(predicates)));
|
||||
|
||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||
doSetPids(new HashSet<Long>(q.getResultList()));
|
||||
doSetPids(q.getResultList());
|
||||
}
|
||||
|
||||
private void addPredicateId(Set<Long> thePids, DateRangeParam theLastUpdated) {
|
||||
|
@ -235,20 +239,13 @@ public class SearchBuilder {
|
|||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
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));
|
||||
|
||||
cq.where(toArray(predicates));
|
||||
|
||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||
HashSet<Long> found = new HashSet<Long>(q.getResultList());
|
||||
doSetPids(found);
|
||||
}
|
||||
|
||||
private void doCreateIdPredicate(List<Predicate> thePredicates, Expression<Long> theExpression) {
|
||||
if (myPids != null) {
|
||||
thePredicates.add(theExpression.in(myPids));
|
||||
}
|
||||
doSetPids(q.getResultList());
|
||||
}
|
||||
|
||||
private void addPredicateLanguage(List<List<? extends IQueryParameterType>> theList, DateRangeParam theLastUpdated) {
|
||||
|
@ -279,15 +276,14 @@ public class SearchBuilder {
|
|||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
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")));
|
||||
|
||||
cq.where(toArray(predicates));
|
||||
|
||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||
HashSet<Long> pids = new HashSet<Long>(q.getResultList());
|
||||
doSetPids(pids);
|
||||
doSetPids(q.getResultList());
|
||||
if (doHaveNoResults()) {
|
||||
return;
|
||||
}
|
||||
|
@ -296,7 +292,8 @@ public class SearchBuilder {
|
|||
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;
|
||||
if (nextOr.getMissing() != null) {
|
||||
if (nextOr.getMissing().booleanValue() == true) {
|
||||
|
@ -310,7 +307,8 @@ public class SearchBuilder {
|
|||
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;
|
||||
if (nextOr.getMissing() != null) {
|
||||
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("myParamName"), theParamName));
|
||||
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)));
|
||||
|
||||
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) {
|
||||
|
@ -395,17 +393,14 @@ public class SearchBuilder {
|
|||
predicates.add(builder.not(builder.in(from.get("myId")).value(subQ)));
|
||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
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)));
|
||||
|
||||
ourLog.info("Adding :missing qualifier for parameter '{}'", theParamName);
|
||||
|
||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||
List<Long> resultList = q.getResultList();
|
||||
|
||||
HashSet<Long> retVal = new HashSet<Long>(resultList);
|
||||
doSetPids(retVal);
|
||||
doSetPids(q.getResultList());
|
||||
}
|
||||
|
||||
private void addPredicateParamMissingResourceLink(String joinName, String theParamName) {
|
||||
|
@ -423,7 +418,7 @@ public class SearchBuilder {
|
|||
subQ.where(path);
|
||||
|
||||
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.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("myParamName"), theParamName));
|
||||
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)));
|
||||
|
||||
|
@ -657,7 +652,7 @@ public class SearchBuilder {
|
|||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
predicates.add(createResourceLinkPathPredicate(theParamName, builder, from));
|
||||
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)));
|
||||
|
||||
|
@ -691,7 +686,7 @@ public class SearchBuilder {
|
|||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||
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)));
|
||||
|
||||
|
@ -778,7 +773,7 @@ public class SearchBuilder {
|
|||
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));
|
||||
|
||||
cq.where(masterCodePredicate);
|
||||
|
@ -828,7 +823,7 @@ public class SearchBuilder {
|
|||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(builder.equal(from.get("myParamName"), theParamName));
|
||||
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)));
|
||||
|
||||
|
@ -868,17 +863,12 @@ public class SearchBuilder {
|
|||
if (param.getQualifier() == UriParamQualifierEnum.ABOVE) {
|
||||
|
||||
/*
|
||||
* :above is an inefficient query- It means that the user is supplying a more specific URL
|
||||
* (say http://example.com/foo/bar/baz) and that we should match on any URLs that are
|
||||
* less specific but otherwise the same. For example http://example.com and http://example.com/foo
|
||||
* would both match.
|
||||
* :above is an inefficient query- It means that the user is supplying a more specific URL (say http://example.com/foo/bar/baz) and that we should match on any URLs that are less
|
||||
* specific but otherwise the same. For example http://example.com and http://example.com/foo would both match.
|
||||
*
|
||||
* We do this by querying the DB for all candidate URIs and then manually checking
|
||||
* each one. This isn't very efficient, but this is also probably not a very common
|
||||
* type of query to do.
|
||||
* We do this by querying the DB for all candidate URIs and then manually checking each one. This isn't very efficient, but this is also probably not a very common type of query to do.
|
||||
*
|
||||
* If we ever need to make this more efficient, lucene could certainly be used
|
||||
* as an optimization.
|
||||
* If we ever need to make this more efficient, lucene could certainly be used as an optimization.
|
||||
*/
|
||||
ourLog.info("Searching for candidate URI:above parameters for Resource[{}] param[{}]", 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("myParamName"), theParamName));
|
||||
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)));
|
||||
|
||||
|
@ -953,7 +943,7 @@ public class SearchBuilder {
|
|||
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>();
|
||||
if (theLastUpdated != 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;
|
||||
switch (cmpValue) {
|
||||
case GREATERTHAN:
|
||||
|
@ -1065,7 +1056,8 @@ public class SearchBuilder {
|
|||
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;
|
||||
if (theParameter instanceof TokenParam) {
|
||||
TokenParam id = (TokenParam) theParameter;
|
||||
|
@ -1084,7 +1076,8 @@ public class SearchBuilder {
|
|||
}
|
||||
|
||||
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);
|
||||
|
@ -1098,11 +1091,8 @@ public class SearchBuilder {
|
|||
return singleCode;
|
||||
}
|
||||
|
||||
private static String createLeftMatchLikeExpression(String likeExpression) {
|
||||
return likeExpression.replace("%", "[%]") + "%";
|
||||
}
|
||||
|
||||
private Predicate createPredicateToken(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> theFrom) {
|
||||
private Predicate createPredicateToken(IQueryParameterType theParameter, String theParamName, CriteriaBuilder theBuilder,
|
||||
From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> theFrom) {
|
||||
String code;
|
||||
String system;
|
||||
if (theParameter instanceof TokenParam) {
|
||||
|
@ -1122,10 +1112,12 @@ public class SearchBuilder {
|
|||
}
|
||||
|
||||
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) {
|
||||
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>());
|
||||
|
@ -1153,11 +1145,11 @@ public class SearchBuilder {
|
|||
return type;
|
||||
}
|
||||
|
||||
private TypedQuery<Tuple> createSearchAllByTypeQuery(DateRangeParam theLastUpdated) {
|
||||
private TypedQuery<Long> createSearchAllByTypeQuery(DateRangeParam theLastUpdated) {
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Tuple> cq = builder.createTupleQuery();
|
||||
CriteriaQuery<Long> cq = builder.createQuery(Long.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>();
|
||||
predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
predicates.add(builder.isNull(from.get("myDeleted")));
|
||||
|
@ -1168,7 +1160,7 @@ public class SearchBuilder {
|
|||
|
||||
cq.where(toArray(predicates));
|
||||
|
||||
TypedQuery<Tuple> query = myEntityManager.createQuery(cq);
|
||||
TypedQuery<Long> query = myEntityManager.createQuery(cq);
|
||||
return query;
|
||||
}
|
||||
|
||||
|
@ -1267,6 +1259,112 @@ public class SearchBuilder {
|
|||
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) {
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
|
@ -1274,7 +1372,7 @@ public class SearchBuilder {
|
|||
cq.select(from.get("myId").as(Long.class));
|
||||
|
||||
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));
|
||||
TypedQuery<Long> query = myEntityManager.createQuery(cq);
|
||||
|
@ -1283,8 +1381,8 @@ public class SearchBuilder {
|
|||
doSetPids(resultList);
|
||||
}
|
||||
|
||||
private List<Long> filterResourceIdsByLastUpdated(final DateRangeParam theLastUpdated, Collection<Long> thePids) {
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
private static List<Long> filterResourceIdsByLastUpdated(EntityManager theEntityManager, final DateRangeParam theLastUpdated, Collection<Long> thePids) {
|
||||
CriteriaBuilder builder = theEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.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));
|
||||
|
||||
cq.where(SearchBuilder.toArray(lastUpdatedPredicates));
|
||||
TypedQuery<Long> query = myEntityManager.createQuery(cq);
|
||||
TypedQuery<Long> query = theEntityManager.createQuery(cq);
|
||||
|
||||
List<Long> resultList = query.getResultList();
|
||||
return resultList;
|
||||
}
|
||||
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1310,15 +1417,15 @@ public class SearchBuilder {
|
|||
theResourceListToPopulate.add(null);
|
||||
}
|
||||
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ResourceTable> cq = builder.createQuery(ResourceTable.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.where(from.get("myId").in(theIncludePids));
|
||||
TypedQuery<ResourceTable> q = myEntityManager.createQuery(cq);
|
||||
TypedQuery<ResourceTable> q = entityManager.createQuery(cq);
|
||||
|
||||
for (ResourceTable next : q.getResultList()) {
|
||||
Class<? extends IBaseResource> resourceType = myContext.getResourceDefinition(next.getResourceType()).getImplementingClass();
|
||||
IBaseResource resource = (IBaseResource) myCallingDao.toResource(resourceType, next, theForHistoryOperation);
|
||||
Class<? extends IBaseResource> resourceType = context.getResourceDefinition(next.getResourceType()).getImplementingClass();
|
||||
IBaseResource resource = (IBaseResource) theDao.toResource(resourceType, next, theForHistoryOperation);
|
||||
Integer index = position.get(next.getId());
|
||||
if (index == null) {
|
||||
ourLog.warn("Got back unexpected resource PID {}", next.getId());
|
||||
|
@ -1348,7 +1455,8 @@ public class SearchBuilder {
|
|||
*
|
||||
* @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) {
|
||||
return new HashSet<Long>();
|
||||
}
|
||||
|
@ -1382,7 +1490,7 @@ public class SearchBuilder {
|
|||
if (matchAll) {
|
||||
String sql;
|
||||
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);
|
||||
List<ResourceLink> results = q.getResultList();
|
||||
for (ResourceLink resourceLink : results) {
|
||||
|
@ -1401,14 +1509,14 @@ public class SearchBuilder {
|
|||
} else {
|
||||
|
||||
List<String> paths;
|
||||
if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
|
||||
if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
|
||||
paths = Collections.singletonList(nextInclude.getValue());
|
||||
} else {
|
||||
String resType = nextInclude.getParamType();
|
||||
if (isBlank(resType)) {
|
||||
continue;
|
||||
}
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(resType);
|
||||
RuntimeResourceDefinition def = theContext.getResourceDefinition(resType);
|
||||
if (def == null) {
|
||||
ourLog.warn("Unknown resource type in include/revinclude=" + nextInclude.getValue());
|
||||
continue;
|
||||
|
@ -1432,7 +1540,7 @@ public class SearchBuilder {
|
|||
} else {
|
||||
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("target_pids", nextRoundMatches);
|
||||
if (targetResourceType != null) {
|
||||
|
@ -1451,7 +1559,7 @@ public class SearchBuilder {
|
|||
}
|
||||
|
||||
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) {
|
||||
if (original.contains(next) == false && allAdded.contains(next) == false) {
|
||||
|
@ -1480,14 +1588,14 @@ public class SearchBuilder {
|
|||
CriteriaQuery<Tuple> cq = builder.createTupleQuery();
|
||||
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);
|
||||
|
||||
if (orders.size() > 0) {
|
||||
|
||||
// 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>();
|
||||
cq.multiselect(from.get("myId").as(Long.class));
|
||||
|
@ -1528,7 +1636,7 @@ public class SearchBuilder {
|
|||
lu = null;
|
||||
}
|
||||
|
||||
// Collection<Long> loadPids;
|
||||
// Collection<Long> loadPids;
|
||||
if (theParams.getEverythingMode() != null) {
|
||||
|
||||
Long pid = null;
|
||||
|
@ -1575,18 +1683,8 @@ public class SearchBuilder {
|
|||
|
||||
} else if (theParams.isEmpty()) {
|
||||
|
||||
TypedQuery<Tuple> query = createSearchAllByTypeQuery(lu);
|
||||
lu = null;
|
||||
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);
|
||||
TypedQuery<Long> query = createSearchAllByTypeQuery(lu);
|
||||
doSetPids(query.getResultList());
|
||||
|
||||
} else {
|
||||
|
||||
|
@ -1634,7 +1732,6 @@ public class SearchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Handle sorting if any was provided
|
||||
processSort(theParams);
|
||||
|
||||
|
@ -1642,101 +1739,14 @@ public class SearchBuilder {
|
|||
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) {
|
||||
SearchParameterMap params = theParams;
|
||||
if (params == null) {
|
||||
params = new SearchParameterMap();
|
||||
}
|
||||
myParams = theParams;
|
||||
|
||||
doInitializeSearch();
|
||||
|
||||
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) {
|
||||
myResourceType = theResourceType;
|
||||
myResourceName = theResourceName;
|
||||
|
@ -1931,9 +1937,8 @@ public class SearchBuilder {
|
|||
}
|
||||
|
||||
/**
|
||||
* 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 (+/-) 0.005</code> according to the FHIR specs.
|
||||
* 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 (+/-) 0.005</code> according to the FHIR specs.
|
||||
*/
|
||||
static BigDecimal calculateFuzzAmount(ParamPrefixEnum cmpValue, BigDecimal theValue) {
|
||||
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) {
|
||||
return thePredicates.toArray(new Predicate[thePredicates.size()]);
|
||||
}
|
||||
|
||||
public Set<Long> doGetPids() {
|
||||
return new HashSet<Long>(myPids);
|
||||
private final class BundleProviderInMemory implements IBundleProvider {
|
||||
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 Set<Include> myIncludes;
|
||||
private DateRangeParam myLastUpdated;
|
||||
private boolean myPersistResults = true;
|
||||
private RequestDetails myRequestDetails;
|
||||
private Set<Include> myRevIncludes;
|
||||
private SortSpec mySort;
|
||||
|
@ -133,6 +134,10 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
return mySort;
|
||||
}
|
||||
|
||||
public boolean isPersistResults() {
|
||||
return myPersistResults;
|
||||
}
|
||||
|
||||
public void setCount(Integer theCount) {
|
||||
myCount = theCount;
|
||||
}
|
||||
|
@ -149,6 +154,13 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
myLastUpdated = theLastUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should results be persisted into a table for paging
|
||||
*/
|
||||
public void setPersistResults(boolean thePersistResults) {
|
||||
myPersistResults = thePersistResults;
|
||||
}
|
||||
|
||||
public void setRequestDetails(RequestDetails theRequestDetails) {
|
||||
myRequestDetails = theRequestDetails;
|
||||
}
|
||||
|
@ -174,12 +186,13 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
}
|
||||
|
||||
public enum EverythingModeEnum {
|
||||
/*
|
||||
* Don't reorder! We rely on the ordinals
|
||||
*/
|
||||
ENCOUNTER_INSTANCE(false, true, true),
|
||||
ENCOUNTER_TYPE(false, true, false),
|
||||
PATIENT_INSTANCE(true, false, true),
|
||||
//@formatter:off
|
||||
PATIENT_TYPE(true, false, false);
|
||||
//@formatter:on
|
||||
|
||||
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;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* 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.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;
|
||||
|
||||
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.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
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)
|
||||
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) {
|
||||
for (ResourceHistoryTag next : getTags()) {
|
||||
if (next.getTag().equals(theTag)) {
|
||||
|
|
|
@ -35,7 +35,9 @@ import javax.persistence.GeneratedValue;
|
|||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
|
@ -150,6 +152,10 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
@Column(name = "SP_HAS_LINKS")
|
||||
private boolean myHasLinks;
|
||||
|
||||
@OneToOne(fetch=FetchType.LAZY, optional=true)
|
||||
@JoinColumn(name="HISTORY_VERSION_PID", referencedColumnName="PID", nullable=true)
|
||||
private ResourceHistoryTable myHistory;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@Column(name = "RES_ID")
|
||||
|
|
|
@ -21,20 +21,32 @@ package ca.uhn.fhir.jpa.entity;
|
|||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
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
|
||||
@Entity
|
||||
@Table(name = "HFJ_SEARCH", uniqueConstraints= {
|
||||
|
@ -48,25 +60,73 @@ public class Search implements Serializable {
|
|||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name="CREATED", nullable=false)
|
||||
@Column(name="CREATED", nullable=false, updatable=false)
|
||||
private Date myCreated;
|
||||
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Column(name="EVERYTHING_MODE", nullable=true)
|
||||
private EverythingModeEnum myEverythingMode;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator="SEQ_SEARCH")
|
||||
@SequenceGenerator(name="SEQ_SEARCH", sequenceName="SEQ_SEARCH")
|
||||
@Column(name = "PID")
|
||||
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")
|
||||
private int myTotalCount;
|
||||
|
||||
@Id
|
||||
@Column(name="SEARCH_UUID", length=40, nullable=false)
|
||||
@Column(name="SEARCH_UUID", length=40, nullable=false, updatable=false)
|
||||
private String myUuid;
|
||||
|
||||
public Date getCreated() {
|
||||
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() {
|
||||
return myTotalCount;
|
||||
}
|
||||
|
@ -79,6 +139,24 @@ public class Search implements Serializable {
|
|||
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) {
|
||||
myTotalCount = theTotalCount;
|
||||
}
|
||||
|
@ -87,4 +165,22 @@ public class Search implements Serializable {
|
|||
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.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
//@formatter:off
|
||||
@Entity
|
||||
@Table(name = "HFJ_SEARCH_RESULT", uniqueConstraints= {
|
||||
@UniqueConstraint(name="IDX_SEARCHRES_ORDER", columnNames= {"SEARCH_PID", "SEARCH_ORDER"})
|
||||
})
|
||||
//@formatter:on
|
||||
public class SearchResult implements Serializable {
|
||||
|
@ -48,6 +50,9 @@ public class SearchResult implements Serializable {
|
|||
@Column(name = "PID")
|
||||
private Long myId;
|
||||
|
||||
@Column(name="SEARCH_ORDER", nullable=false)
|
||||
private int myOrder;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name="RESOURCE_PID", referencedColumnName="RES_ID", foreignKey=@ForeignKey(name="FK_SEARCHRES_RES"), insertable=false, updatable=false, nullable=false)
|
||||
private ResourceTable myResource;
|
||||
|
@ -59,6 +64,9 @@ public class SearchResult implements Serializable {
|
|||
@JoinColumn(name="SEARCH_PID", referencedColumnName="PID", foreignKey=@ForeignKey(name="FK_SEARCHRES_SEARCH"))
|
||||
private Search mySearch;
|
||||
|
||||
@Column(name="SEARCH_PID", insertable=false, updatable=false, nullable=false)
|
||||
private Long mySearchPid;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -73,6 +81,31 @@ public class SearchResult implements Serializable {
|
|||
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) {
|
||||
myResourcePid = theResourcePid;
|
||||
}
|
||||
|
|
|
@ -31,9 +31,9 @@ import java.util.TreeMap;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.ISearchDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
|
@ -57,7 +57,7 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProvider<Bundle, MetaDt
|
|||
private IFhirSystemDao<Bundle, MetaDt> mySystemDao;
|
||||
|
||||
@Autowired
|
||||
private ISearchDao mySearchDao;
|
||||
private IFulltextSearchSvc mySearchDao;
|
||||
|
||||
//@formatter:off
|
||||
// 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.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.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.primitive.IntegerDt;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
|
@ -59,7 +59,7 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProvider<Bundle, Meta>
|
|||
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
||||
|
||||
@Autowired
|
||||
private ISearchDao mySearchDao;
|
||||
private IFulltextSearchSvc mySearchDao;
|
||||
|
||||
//@formatter:off
|
||||
// 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.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
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.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.AfterClass;
|
||||
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.model.dstu2.resource.Bundle;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
||||
|
@ -114,4 +142,59 @@ public class BaseJpaTest {
|
|||
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.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
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.IFhirResourceDaoValueSet;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.dao.ISearchDao;
|
||||
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.dao.IFulltextSearchSvc;
|
||||
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.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.model.dstu2.composite.CodeableConceptDt;
|
||||
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
||||
|
@ -89,7 +73,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
|||
@Autowired
|
||||
protected ApplicationContext myAppCtx;
|
||||
@Autowired
|
||||
protected ISearchDao mySearchDao;
|
||||
protected IFulltextSearchSvc mySearchDao;
|
||||
@Autowired
|
||||
@Qualifier("myConceptMapDaoDstu2")
|
||||
protected IFhirResourceDao<ConceptMap> myConceptMapDao;
|
||||
|
@ -221,45 +205,4 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
|||
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.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.model.dstu2.resource.Device;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Media;
|
||||
|
|
|
@ -9,7 +9,7 @@ import java.util.List;
|
|||
import org.junit.Test;
|
||||
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.model.dstu2.resource.Organization;
|
||||
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 {
|
||||
|
||||
@Autowired
|
||||
private ISearchDao mySearchDao;
|
||||
private IFulltextSearchSvc mySearchDao;
|
||||
|
||||
@Test
|
||||
public void testContentSearch() {
|
||||
|
|
|
@ -48,9 +48,7 @@ import org.springframework.test.context.ContextConfiguration;
|
|||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
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.IFhirResourceDaoValueSet;
|
||||
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.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.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.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.search.StaleSearchDeletingSvc;
|
||||
import ca.uhn.fhir.jpa.term.ITerminologySvc;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.method.MethodUtil;
|
||||
|
@ -96,29 +77,14 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
|||
@ContextConfiguration(classes= {TestDstu3Config.class})
|
||||
//@formatter:on
|
||||
public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
||||
@Autowired
|
||||
protected IResourceTableDao myResourceTableDao;
|
||||
@Autowired
|
||||
protected ITerminologySvc myTermSvc;
|
||||
@Autowired
|
||||
@Qualifier("myJpaValidationSupportChainDstu3")
|
||||
protected IValidationSupport myValidationSupport;
|
||||
@Autowired
|
||||
protected ApplicationContext myAppCtx;
|
||||
@Autowired
|
||||
protected ISearchDao mySearchDao;
|
||||
@Autowired
|
||||
@Qualifier("myConceptMapDaoDstu3")
|
||||
protected IFhirResourceDao<ConceptMap> myConceptMapDao;
|
||||
@Autowired
|
||||
@Qualifier("myCodeSystemDaoDstu3")
|
||||
protected IFhirResourceDao<CodeSystem> myCodeSystemDao;
|
||||
@Autowired
|
||||
@Qualifier("myMedicationDaoDstu3")
|
||||
protected IFhirResourceDao<Medication> myMedicationDao;
|
||||
@Autowired
|
||||
@Qualifier("myMedicationOrderDaoDstu3")
|
||||
protected IFhirResourceDao<MedicationOrder> myMedicationOrderDao;
|
||||
@Qualifier("myConceptMapDaoDstu3")
|
||||
protected IFhirResourceDao<ConceptMap> myConceptMapDao;
|
||||
@Autowired
|
||||
protected DaoConfig myDaoConfig;
|
||||
@Autowired
|
||||
|
@ -133,11 +99,9 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
@Autowired
|
||||
@Qualifier("myEncounterDaoDstu3")
|
||||
protected IFhirResourceDao<Encounter> myEncounterDao;
|
||||
|
||||
// @PersistenceContext()
|
||||
// @PersistenceContext()
|
||||
@Autowired
|
||||
protected EntityManager myEntityManager;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("myFhirContextDstu3")
|
||||
protected FhirContext myFhirCtx;
|
||||
|
@ -149,6 +113,20 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
@Qualifier("myLocationDaoDstu3")
|
||||
protected IFhirResourceDao<Location> myLocationDao;
|
||||
@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")
|
||||
protected IFhirResourceDao<Observation> myObservationDao;
|
||||
@Autowired
|
||||
|
@ -158,12 +136,6 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
@Qualifier("myPatientDaoDstu3")
|
||||
protected IFhirResourceDaoPatient<Patient> myPatientDao;
|
||||
@Autowired
|
||||
@Qualifier("myNamingSystemDaoDstu3")
|
||||
protected IFhirResourceDao<NamingSystem> myNamingSystemDao;
|
||||
@Autowired
|
||||
@Qualifier("myMediaDaoDstu3")
|
||||
protected IFhirResourceDao<Media> myMediaDao;
|
||||
@Autowired
|
||||
@Qualifier("myPractitionerDaoDstu3")
|
||||
protected IFhirResourceDao<Practitioner> myPractitionerDao;
|
||||
@Autowired
|
||||
|
@ -176,6 +148,12 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
@Qualifier("myResourceProvidersDstu3")
|
||||
protected Object myResourceProviders;
|
||||
@Autowired
|
||||
protected IResourceTableDao myResourceTableDao;
|
||||
@Autowired
|
||||
protected IFulltextSearchSvc mySearchDao;
|
||||
@Autowired
|
||||
protected StaleSearchDeletingSvc myStaleSearchDeletingSvc;
|
||||
@Autowired
|
||||
@Qualifier("myStructureDefinitionDaoDstu3")
|
||||
protected IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
|
||||
@Autowired
|
||||
|
@ -191,8 +169,13 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
@Qualifier("mySystemProviderDstu3")
|
||||
protected JpaSystemProviderDstu3 mySystemProvider;
|
||||
@Autowired
|
||||
protected ITerminologySvc myTermSvc;
|
||||
@Autowired
|
||||
protected PlatformTransactionManager myTxManager;
|
||||
@Autowired
|
||||
@Qualifier("myJpaValidationSupportChainDstu3")
|
||||
protected IValidationSupport myValidationSupport;
|
||||
@Autowired
|
||||
@Qualifier("myValueSetDaoDstu3")
|
||||
protected IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> myValueSetDao;
|
||||
|
||||
|
@ -213,14 +196,6 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
myDaoConfig.setSchedulingDisabled(true);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void beforeResetConfig() {
|
||||
myDaoConfig.setHardSearchLimit(1000);
|
||||
myDaoConfig.setHardTagListLimit(1000);
|
||||
myDaoConfig.setIncludeLimit(2000);
|
||||
}
|
||||
|
||||
|
||||
@Before
|
||||
@Transactional()
|
||||
public void beforePurgeDatabase() {
|
||||
|
@ -228,6 +203,14 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
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 {
|
||||
InputStream stream = FhirResourceDaoDstu2SearchNoFtTest.class.getResourceAsStream(resourceName);
|
||||
if (stream == null) {
|
||||
|
@ -245,55 +228,4 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
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.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.rest.param.StringAndListParam;
|
||||
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||
|
|
|
@ -11,7 +11,7 @@ import org.hl7.fhir.dstu3.model.Patient;
|
|||
import org.junit.Test;
|
||||
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.rest.param.StringAndListParam;
|
||||
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||
|
@ -22,7 +22,7 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
|||
public class FhirSearchDaoDstu3Test extends BaseJpaDstu3Test {
|
||||
|
||||
@Autowired
|
||||
private ISearchDao mySearchDao;
|
||||
private IFulltextSearchSvc mySearchDao;
|
||||
|
||||
@Test
|
||||
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.dao.dstu3.BaseJpaDstu3Test;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
|
@ -78,7 +79,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
public void after() throws Exception {
|
||||
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
|
||||
}
|
||||
|
||||
|
@ -105,7 +106,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
confProvider.setImplementationDescription("THIS IS THE DESC");
|
||||
ourRestServer.setServerConformanceProvider(confProvider);
|
||||
|
||||
ourRestServer.setPagingProvider(new FifoMemoryPagingProvider(10));
|
||||
ourRestServer.setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
|
||||
|
||||
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.dstu3.JpaConformanceProviderDstu3;
|
||||
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.dstu2.composite.MetaDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
|
@ -127,15 +128,17 @@ public class JpaServerDemo extends RestfulServer {
|
|||
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
|
||||
/*
|
||||
* Default to XML and pretty printing
|
||||
* Default to JSON and pretty printing
|
||||
*/
|
||||
setDefaultPrettyPrint(true);
|
||||
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)
|
||||
|
|
|
@ -183,6 +183,14 @@
|
|||
longer includes _revinclude matches in the Bundle.total count, or the
|
||||
page size limit.
|
||||
</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 version="1.4" date="2016-02-04">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue