Add ability for system to track unique constraints
This commit is contained in:
parent
c6ddf89557
commit
2aa538bd56
|
@ -1,5 +1,6 @@
|
||||||
package ca.uhn.fhir.context;
|
package ca.uhn.fhir.context;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.trim;
|
import static org.apache.commons.lang3.StringUtils.trim;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -51,6 +52,11 @@ public class RuntimeSearchParam {
|
||||||
|
|
||||||
public RuntimeSearchParam(IIdType theId, String theUri, String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, List<RuntimeSearchParam> theCompositeOf,
|
public RuntimeSearchParam(IIdType theId, String theUri, String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, List<RuntimeSearchParam> theCompositeOf,
|
||||||
Set<String> theProvidesMembershipInCompartments, Set<String> theTargets, RuntimeSearchParamStatusEnum theStatus) {
|
Set<String> theProvidesMembershipInCompartments, Set<String> theTargets, RuntimeSearchParamStatusEnum theStatus) {
|
||||||
|
this(theId, theUri, theName, theDescription, thePath, theParamType, theCompositeOf, theProvidesMembershipInCompartments, theTargets, theStatus, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RuntimeSearchParam(IIdType theId, String theUri, String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, List<RuntimeSearchParam> theCompositeOf,
|
||||||
|
Set<String> theProvidesMembershipInCompartments, Set<String> theTargets, RuntimeSearchParamStatusEnum theStatus, Collection<String> theBase) {
|
||||||
super();
|
super();
|
||||||
myId = theId;
|
myId = theId;
|
||||||
myUri = theUri;
|
myUri = theUri;
|
||||||
|
@ -71,12 +77,18 @@ public class RuntimeSearchParam {
|
||||||
myTargets = null;
|
myTargets = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
HashSet<String> base = new HashSet<String>();
|
if (theBase == null || theBase.isEmpty()) {
|
||||||
|
HashSet<String> base = new HashSet<>();
|
||||||
|
if (isNotBlank(thePath)) {
|
||||||
int indexOf = thePath.indexOf('.');
|
int indexOf = thePath.indexOf('.');
|
||||||
if (indexOf != -1) {
|
if (indexOf != -1) {
|
||||||
base.add(trim(thePath.substring(0, indexOf)));
|
base.add(trim(thePath.substring(0, indexOf)));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
myBase = Collections.unmodifiableSet(base);
|
myBase = Collections.unmodifiableSet(base);
|
||||||
|
} else {
|
||||||
|
myBase = Collections.unmodifiableSet(new HashSet<>(theBase));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getBase() {
|
public Set<String> getBase() {
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
import org.springframework.orm.hibernate5.HibernateExceptionTranslator;
|
||||||
import org.springframework.scheduling.TaskScheduler;
|
import org.springframework.scheduling.TaskScheduler;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||||
|
|
|
@ -19,19 +19,49 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import static org.apache.commons.lang3.StringUtils.*;
|
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import ca.uhn.fhir.context.*;
|
||||||
import java.text.Normalizer;
|
import ca.uhn.fhir.jpa.dao.data.*;
|
||||||
import java.util.*;
|
import ca.uhn.fhir.jpa.entity.*;
|
||||||
import java.util.Map.Entry;
|
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
||||||
|
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
|
||||||
import javax.persistence.*;
|
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||||
import javax.persistence.criteria.*;
|
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||||
import javax.xml.stream.events.Characters;
|
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
||||||
import javax.xml.stream.events.XMLEvent;
|
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
||||||
|
import ca.uhn.fhir.model.api.*;
|
||||||
import org.apache.commons.lang3.*;
|
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||||
|
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.XhtmlDt;
|
||||||
|
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
|
||||||
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
import ca.uhn.fhir.parser.LenientErrorHandler;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.rest.api.QualifiedParamList;
|
||||||
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.param.*;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.*;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
import ca.uhn.fhir.util.CoverageIgnore;
|
||||||
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
|
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
||||||
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.google.common.hash.HashFunction;
|
||||||
|
import com.google.common.hash.Hashing;
|
||||||
|
import org.apache.commons.lang3.NotImplementedException;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.http.NameValuePair;
|
import org.apache.http.NameValuePair;
|
||||||
import org.apache.http.client.utils.URLEncodedUtils;
|
import org.apache.http.client.utils.URLEncodedUtils;
|
||||||
import org.hl7.fhir.instance.model.api.*;
|
import org.hl7.fhir.instance.model.api.*;
|
||||||
|
@ -40,49 +70,30 @@ import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
import javax.persistence.*;
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import com.google.common.collect.Sets;
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
import com.google.common.hash.HashFunction;
|
import javax.persistence.criteria.Predicate;
|
||||||
import com.google.common.hash.Hashing;
|
import javax.persistence.criteria.Root;
|
||||||
|
import javax.xml.stream.events.Characters;
|
||||||
|
import javax.xml.stream.events.XMLEvent;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.text.Normalizer;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.*;
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
import ca.uhn.fhir.jpa.dao.data.*;
|
|
||||||
import ca.uhn.fhir.jpa.entity.*;
|
|
||||||
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
|
|
||||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
|
||||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
|
||||||
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
|
||||||
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
|
||||||
import ca.uhn.fhir.model.api.*;
|
|
||||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
|
||||||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
|
||||||
import ca.uhn.fhir.model.primitive.*;
|
|
||||||
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
|
|
||||||
import ca.uhn.fhir.parser.*;
|
|
||||||
import ca.uhn.fhir.rest.api.*;
|
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|
||||||
import ca.uhn.fhir.rest.param.*;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.*;
|
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
|
||||||
import ca.uhn.fhir.util.*;
|
|
||||||
|
|
||||||
public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
|
|
||||||
static final Set<String> EXCLUDE_ELEMENTS_IN_ENCODED;
|
|
||||||
|
|
||||||
public static final long INDEX_STATUS_INDEXED = Long.valueOf(1L);
|
public static final long INDEX_STATUS_INDEXED = Long.valueOf(1L);
|
||||||
public static final long INDEX_STATUS_INDEXING_FAILED = Long.valueOf(2L);
|
public static final long INDEX_STATUS_INDEXING_FAILED = Long.valueOf(2L);
|
||||||
public static final String NS_JPA_PROFILE = "https://github.com/jamesagnew/hapi-fhir/ns/jpa/profile";
|
public static final String NS_JPA_PROFILE = "https://github.com/jamesagnew/hapi-fhir/ns/jpa/profile";
|
||||||
public static final String OO_SEVERITY_ERROR = "error";
|
public static final String OO_SEVERITY_ERROR = "error";
|
||||||
public static final String OO_SEVERITY_INFO = "information";
|
public static final String OO_SEVERITY_INFO = "information";
|
||||||
public static final String OO_SEVERITY_WARN = "warning";
|
public static final String OO_SEVERITY_WARN = "warning";
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirDao.class);
|
public static final String UCUM_NS = "http://unitsofmeasure.org";
|
||||||
private static final Map<FhirVersionEnum, FhirContext> ourRetrievalContexts = new HashMap<FhirVersionEnum, FhirContext>();
|
static final Set<String> EXCLUDE_ELEMENTS_IN_ENCODED;
|
||||||
private static final String PROCESSING_SUB_REQUEST = "BaseHapiFhirDao.processingSubRequest";
|
|
||||||
/**
|
/**
|
||||||
* These are parameters which are supported by {@link BaseHapiFhirResourceDao#searchForIds(SearchParameterMap)}
|
* These are parameters which are supported by {@link BaseHapiFhirResourceDao#searchForIds(SearchParameterMap)}
|
||||||
*/
|
*/
|
||||||
|
@ -91,7 +102,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
* These are parameters which are supported by {@link BaseHapiFhirResourceDao#searchForIds(SearchParameterMap)}
|
* These are parameters which are supported by {@link BaseHapiFhirResourceDao#searchForIds(SearchParameterMap)}
|
||||||
*/
|
*/
|
||||||
static final Map<String, Class<? extends IQueryParameterType>> RESOURCE_META_PARAMS;
|
static final Map<String, Class<? extends IQueryParameterType>> RESOURCE_META_PARAMS;
|
||||||
public static final String UCUM_NS = "http://unitsofmeasure.org";
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirDao.class);
|
||||||
|
private static final Map<FhirVersionEnum, FhirContext> ourRetrievalContexts = new HashMap<FhirVersionEnum, FhirContext>();
|
||||||
|
private static final String PROCESSING_SUB_REQUEST = "BaseHapiFhirDao.processingSubRequest";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Map<String, Class<? extends IQueryParameterType>> resourceMetaParams = new HashMap<String, Class<? extends IQueryParameterType>>();
|
Map<String, Class<? extends IQueryParameterType>> resourceMetaParams = new HashMap<String, Class<? extends IQueryParameterType>>();
|
||||||
|
@ -115,52 +128,47 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
EXCLUDE_ELEMENTS_IN_ENCODED = Collections.unmodifiableSet(excludeElementsInEncoded);
|
EXCLUDE_ELEMENTS_IN_ENCODED = Collections.unmodifiableSet(excludeElementsInEncoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired(required = true)
|
|
||||||
private DaoConfig myConfig;
|
|
||||||
private FhirContext myContext;
|
|
||||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||||
protected EntityManager myEntityManager;
|
protected EntityManager myEntityManager;
|
||||||
@Autowired
|
@Autowired
|
||||||
protected IForcedIdDao myForcedIdDao;
|
protected IForcedIdDao myForcedIdDao;
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
protected IFulltextSearchSvc myFulltextSearchSvc;
|
protected IFulltextSearchSvc myFulltextSearchSvc;
|
||||||
@Autowired
|
|
||||||
private PlatformTransactionManager myPlatformTransactionManager;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private List<IFhirResourceDao<?>> myResourceDaos;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private IResourceHistoryTableDao myResourceHistoryTableDao;
|
|
||||||
|
|
||||||
@Autowired()
|
@Autowired()
|
||||||
protected IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
|
protected IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
|
||||||
|
|
||||||
private Map<Class<? extends IBaseResource>, IFhirResourceDao<?>> myResourceTypeToDao;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ISearchDao mySearchDao;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ISearchParamExtractor mySearchParamExtractor;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ISearchParamPresenceSvc mySearchParamPresenceSvc;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ISearchParamRegistry mySearchParamRegistry;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ISearchResultDao mySearchResultDao;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected ISearchParamRegistry mySerarchParamRegistry;
|
protected ISearchParamRegistry mySerarchParamRegistry;
|
||||||
|
|
||||||
@Autowired()
|
@Autowired()
|
||||||
protected IHapiTerminologySvc myTerminologySvc;
|
protected IHapiTerminologySvc myTerminologySvc;
|
||||||
|
@Autowired(required = true)
|
||||||
|
private DaoConfig myConfig;
|
||||||
|
private FhirContext myContext;
|
||||||
|
@Autowired
|
||||||
|
private PlatformTransactionManager myPlatformTransactionManager;
|
||||||
|
@Autowired
|
||||||
|
private List<IFhirResourceDao<?>> myResourceDaos;
|
||||||
|
@Autowired
|
||||||
|
private IResourceHistoryTableDao myResourceHistoryTableDao;
|
||||||
|
private Map<Class<? extends IBaseResource>, IFhirResourceDao<?>> myResourceTypeToDao;
|
||||||
|
@Autowired
|
||||||
|
private ISearchDao mySearchDao;
|
||||||
|
@Autowired
|
||||||
|
private ISearchParamExtractor mySearchParamExtractor;
|
||||||
|
@Autowired
|
||||||
|
private ISearchParamPresenceSvc mySearchParamPresenceSvc;
|
||||||
|
@Autowired
|
||||||
|
private ISearchParamRegistry mySearchParamRegistry;
|
||||||
|
@Autowired
|
||||||
|
private ISearchResultDao mySearchResultDao;
|
||||||
|
@Autowired
|
||||||
|
private IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
|
||||||
|
|
||||||
|
private <T extends IBaseResource> void autoCreateResource(T theResource) {
|
||||||
|
IFhirResourceDao<T> dao = (IFhirResourceDao<T>) getDao(theResource.getClass());
|
||||||
|
dao.create(theResource);
|
||||||
|
}
|
||||||
|
|
||||||
protected void clearRequestAsProcessingSubRequest(ServletRequestDetails theRequestDetails) {
|
protected void clearRequestAsProcessingSubRequest(ServletRequestDetails theRequestDetails) {
|
||||||
if (theRequestDetails != null) {
|
if (theRequestDetails != null) {
|
||||||
|
@ -188,13 +196,84 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
return InstantDt.withCurrentTime();
|
return InstantDt.withCurrentTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Set<ResourceIndexedCompositeStringUnique> extractCompositeStringUniques(ResourceTable theEntity, Set<ResourceIndexedSearchParamString> theStringParams, Set<ResourceIndexedSearchParamToken> theTokenParams, Set<ResourceIndexedSearchParamNumber> theNumberParams, Set<ResourceIndexedSearchParamQuantity> theQuantityParams, Set<ResourceIndexedSearchParamDate> theDateParams, Set<ResourceIndexedSearchParamUri> theUriParams, Set<ResourceLink> theLinks) {
|
||||||
|
Set<ResourceIndexedCompositeStringUnique> compositeStringUniques;
|
||||||
|
compositeStringUniques = new HashSet<>();
|
||||||
|
List<JpaRuntimeSearchParam> uniqueSearchParams = mySearchParamRegistry.getActiveUniqueSearchParams(theEntity.getResourceType());
|
||||||
|
for (JpaRuntimeSearchParam next : uniqueSearchParams) {
|
||||||
|
|
||||||
|
List<List<String>> partsChoices = new ArrayList<>();
|
||||||
|
|
||||||
|
for (RuntimeSearchParam nextCompositeOf : next.getCompositeOf()) {
|
||||||
|
Set<? extends BaseResourceIndexedSearchParam> paramsListForCompositePart = null;
|
||||||
|
Set<ResourceLink> linksForCompositePart = null;
|
||||||
|
switch (nextCompositeOf.getParamType()) {
|
||||||
|
case NUMBER:
|
||||||
|
paramsListForCompositePart = theNumberParams;
|
||||||
|
break;
|
||||||
|
case DATE:
|
||||||
|
paramsListForCompositePart = theDateParams;
|
||||||
|
break;
|
||||||
|
case STRING:
|
||||||
|
paramsListForCompositePart = theStringParams;
|
||||||
|
break;
|
||||||
|
case TOKEN:
|
||||||
|
paramsListForCompositePart = theTokenParams;
|
||||||
|
break;
|
||||||
|
case REFERENCE:
|
||||||
|
linksForCompositePart = theLinks;
|
||||||
|
break;
|
||||||
|
case QUANTITY:
|
||||||
|
paramsListForCompositePart = theQuantityParams;
|
||||||
|
break;
|
||||||
|
case URI:
|
||||||
|
paramsListForCompositePart = theUriParams;
|
||||||
|
break;
|
||||||
|
case COMPOSITE:
|
||||||
|
case HAS:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<String> nextChoicesList = new ArrayList<>();
|
||||||
|
partsChoices.add(nextChoicesList);
|
||||||
|
|
||||||
|
String key = UrlUtil.escape(nextCompositeOf.getName());
|
||||||
|
if (paramsListForCompositePart != null) {
|
||||||
|
for (BaseResourceIndexedSearchParam nextParam : paramsListForCompositePart) {
|
||||||
|
if (nextParam.getParamName().equals(nextCompositeOf.getName())) {
|
||||||
|
IQueryParameterType nextParamAsClientParam = nextParam.toQueryParameterType();
|
||||||
|
String value = nextParamAsClientParam.getValueAsQueryToken(getContext());
|
||||||
|
value = UrlUtil.escape(value);
|
||||||
|
nextChoicesList.add(key + "=" + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (linksForCompositePart != null) {
|
||||||
|
for (ResourceLink nextLink : linksForCompositePart) {
|
||||||
|
String value = nextLink.getTargetResource().getIdDt().toUnqualifiedVersionless().getValue();
|
||||||
|
value = UrlUtil.escape(value);
|
||||||
|
nextChoicesList.add(key + "=" + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> queryStringsToPopulate = extractCompositeStringUniquesValueChains(theEntity.getResourceType(), partsChoices);
|
||||||
|
|
||||||
|
for (String nextQueryString : queryStringsToPopulate) {
|
||||||
|
compositeStringUniques.add(new ResourceIndexedCompositeStringUnique(theEntity, nextQueryString));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return compositeStringUniques;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns a set containing all of the parameter names that
|
* @return Returns a set containing all of the parameter names that
|
||||||
* were found to have a value
|
* were found to have a value
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected Set<String> extractResourceLinks(ResourceTable theEntity, IBaseResource theResource, Set<ResourceLink> theLinks, Date theUpdateTime) {
|
protected Set<String> extractResourceLinks(ResourceTable theEntity, IBaseResource theResource, Set<ResourceLink> theLinks, Date theUpdateTime) {
|
||||||
HashSet<String> retVal = new HashSet<String>();
|
HashSet<String> retVal = new HashSet<>();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For now we don't try to load any of the links in a bundle if it's the actual bundle we're storing..
|
* For now we don't try to load any of the links in a bundle if it's the actual bundle we're storing..
|
||||||
|
@ -375,11 +454,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends IBaseResource> void autoCreateResource(T theResource) {
|
|
||||||
IFhirResourceDao<T> dao = (IFhirResourceDao<T>) getDao(theResource.getClass());
|
|
||||||
dao.create(theResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
protected Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
|
||||||
return mySearchParamExtractor.extractSearchParamCoords(theEntity, theResource);
|
return mySearchParamExtractor.extractSearchParamCoords(theEntity, theResource);
|
||||||
}
|
}
|
||||||
|
@ -569,11 +643,20 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
return myConfig;
|
return myConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setConfig(DaoConfig theConfig) {
|
||||||
|
myConfig = theConfig;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FhirContext getContext() {
|
public FhirContext getContext() {
|
||||||
return myContext;
|
return myContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void setContext(FhirContext theContext) {
|
||||||
|
myContext = theContext;
|
||||||
|
}
|
||||||
|
|
||||||
public FhirContext getContext(FhirVersionEnum theVersion) {
|
public FhirContext getContext(FhirVersionEnum theVersion) {
|
||||||
Validate.notNull(theVersion, "theVersion must not be null");
|
Validate.notNull(theVersion, "theVersion must not be null");
|
||||||
synchronized (ourRetrievalContexts) {
|
synchronized (ourRetrievalContexts) {
|
||||||
|
@ -606,6 +689,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
return dao;
|
return dao;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IResourceIndexedCompositeStringUniqueDao getResourceIndexedCompositeStringUniqueDao() {
|
||||||
|
return myResourceIndexedCompositeStringUniqueDao;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RuntimeSearchParam getSearchParamByName(RuntimeResourceDefinition theResourceDef, String theParamName) {
|
public RuntimeSearchParam getSearchParamByName(RuntimeResourceDefinition theResourceDef, String theParamName) {
|
||||||
Map<String, RuntimeSearchParam> params = mySearchParamRegistry.getActiveSearchParams(theResourceDef.getName());
|
Map<String, RuntimeSearchParam> params = mySearchParamRegistry.getActiveSearchParams(theResourceDef.getName());
|
||||||
|
@ -665,7 +752,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<Long> tagIds = new HashSet<Long>();
|
Set<Long> tagIds = new HashSet<>();
|
||||||
findMatchingTagIds(resourceName, theResourceId, tagIds, ResourceTag.class);
|
findMatchingTagIds(resourceName, theResourceId, tagIds, ResourceTag.class);
|
||||||
findMatchingTagIds(resourceName, theResourceId, tagIds, ResourceHistoryTag.class);
|
findMatchingTagIds(resourceName, theResourceId, tagIds, ResourceHistoryTag.class);
|
||||||
if (tagIds.isEmpty()) {
|
if (tagIds.isEmpty()) {
|
||||||
|
@ -1064,10 +1151,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
/**
|
/**
|
||||||
* Subclasses may override to provide behaviour. Called when a resource has been inserted into the database for the first time.
|
* Subclasses may override to provide behaviour. Called when a resource has been inserted into the database for the first time.
|
||||||
*
|
*
|
||||||
* @param theEntity
|
* @param theEntity The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
|
||||||
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
|
* @param theResource The resource being persisted
|
||||||
* @param theResource
|
|
||||||
* The resource being persisted
|
|
||||||
*/
|
*/
|
||||||
protected void postPersist(ResourceTable theEntity, T theResource) {
|
protected void postPersist(ResourceTable theEntity, T theResource) {
|
||||||
// nothing
|
// nothing
|
||||||
|
@ -1076,10 +1161,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
/**
|
/**
|
||||||
* Subclasses may override to provide behaviour. Called when a pre-existing resource has been updated in the database
|
* Subclasses may override to provide behaviour. Called when a pre-existing resource has been updated in the database
|
||||||
*
|
*
|
||||||
* @param theEntity
|
* @param theEntity The resource
|
||||||
* The resource
|
* @param theResource The resource being persisted
|
||||||
* @param theResource
|
|
||||||
* The resource being persisted
|
|
||||||
*/
|
*/
|
||||||
protected void postUpdate(ResourceTable theEntity, T theResource) {
|
protected void postUpdate(ResourceTable theEntity, T theResource) {
|
||||||
// nothing
|
// nothing
|
||||||
|
@ -1111,15 +1194,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
throw new NotImplementedException("");
|
throw new NotImplementedException("");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConfig(DaoConfig theConfig) {
|
|
||||||
myConfig = theConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
public void setContext(FhirContext theContext) {
|
|
||||||
myContext = theContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEntityManager(EntityManager theEntityManager) {
|
public void setEntityManager(EntityManager theEntityManager) {
|
||||||
myEntityManager = theEntityManager;
|
myEntityManager = theEntityManager;
|
||||||
}
|
}
|
||||||
|
@ -1143,10 +1217,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
* See <a href="http://hl7.org/fhir/2015Sep/resource.html#1.11.3.7">Updates to Tags, Profiles, and Security Labels</a> for a description of the logic that the default behaviour folows.
|
* See <a href="http://hl7.org/fhir/2015Sep/resource.html#1.11.3.7">Updates to Tags, Profiles, and Security Labels</a> for a description of the logic that the default behaviour folows.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param theEntity
|
* @param theEntity The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
|
||||||
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
|
* @param theTag The tag
|
||||||
* @param theTag
|
|
||||||
* The tag
|
|
||||||
* @return Retturns <code>true</code> if the tag should be removed
|
* @return Retturns <code>true</code> if the tag should be removed
|
||||||
*/
|
*/
|
||||||
protected boolean shouldDroppedTagBeRemovedOnUpdate(ResourceTable theEntity, ResourceTag theTag) {
|
protected boolean shouldDroppedTagBeRemovedOnUpdate(ResourceTable theEntity, ResourceTag theTag) {
|
||||||
|
@ -1156,6 +1228,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBaseResource toResource(BaseHasResource theEntity, boolean theForHistoryOperation) {
|
||||||
|
RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType());
|
||||||
|
Class<? extends IBaseResource> resourceType = type.getImplementingClass();
|
||||||
|
return toResource(resourceType, theEntity, theForHistoryOperation);
|
||||||
|
}
|
||||||
|
|
||||||
// protected ResourceTable toEntity(IResource theResource) {
|
// protected ResourceTable toEntity(IResource theResource) {
|
||||||
// ResourceTable retVal = new ResourceTable();
|
// ResourceTable retVal = new ResourceTable();
|
||||||
//
|
//
|
||||||
|
@ -1164,13 +1243,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
// return retVal;
|
// return retVal;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBaseResource toResource(BaseHasResource theEntity, boolean theForHistoryOperation) {
|
|
||||||
RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType());
|
|
||||||
Class<? extends IBaseResource> resourceType = type.getImplementingClass();
|
|
||||||
return toResource(resourceType, theEntity, theForHistoryOperation);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public <R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation) {
|
public <R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation) {
|
||||||
|
@ -1291,38 +1363,42 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
theEntity.setPublished(theUpdateTime);
|
theEntity.setPublished(theUpdateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection<ResourceIndexedSearchParamString> paramsString = new ArrayList<>();
|
Collection<ResourceIndexedSearchParamString> existingStringParams = new ArrayList<>();
|
||||||
if (theEntity.isParamsStringPopulated()) {
|
if (theEntity.isParamsStringPopulated()) {
|
||||||
paramsString.addAll(theEntity.getParamsString());
|
existingStringParams.addAll(theEntity.getParamsString());
|
||||||
}
|
}
|
||||||
Collection<ResourceIndexedSearchParamToken> paramsToken = new ArrayList<>();
|
Collection<ResourceIndexedSearchParamToken> existingTokenParams = new ArrayList<>();
|
||||||
if (theEntity.isParamsTokenPopulated()) {
|
if (theEntity.isParamsTokenPopulated()) {
|
||||||
paramsToken.addAll(theEntity.getParamsToken());
|
existingTokenParams.addAll(theEntity.getParamsToken());
|
||||||
}
|
}
|
||||||
Collection<ResourceIndexedSearchParamNumber> paramsNumber = new ArrayList<>();
|
Collection<ResourceIndexedSearchParamNumber> existingNumberParams = new ArrayList<>();
|
||||||
if (theEntity.isParamsNumberPopulated()) {
|
if (theEntity.isParamsNumberPopulated()) {
|
||||||
paramsNumber.addAll(theEntity.getParamsNumber());
|
existingNumberParams.addAll(theEntity.getParamsNumber());
|
||||||
}
|
}
|
||||||
Collection<ResourceIndexedSearchParamQuantity> paramsQuantity = new ArrayList<>();
|
Collection<ResourceIndexedSearchParamQuantity> existingQuantityParams = new ArrayList<>();
|
||||||
if (theEntity.isParamsQuantityPopulated()) {
|
if (theEntity.isParamsQuantityPopulated()) {
|
||||||
paramsQuantity.addAll(theEntity.getParamsQuantity());
|
existingQuantityParams.addAll(theEntity.getParamsQuantity());
|
||||||
}
|
}
|
||||||
Collection<ResourceIndexedSearchParamDate> paramsDate = new ArrayList<>();
|
Collection<ResourceIndexedSearchParamDate> existingDateParams = new ArrayList<>();
|
||||||
if (theEntity.isParamsDatePopulated()) {
|
if (theEntity.isParamsDatePopulated()) {
|
||||||
paramsDate.addAll(theEntity.getParamsDate());
|
existingDateParams.addAll(theEntity.getParamsDate());
|
||||||
}
|
}
|
||||||
Collection<ResourceIndexedSearchParamUri> paramsUri = new ArrayList<>();
|
Collection<ResourceIndexedSearchParamUri> existingUriParams = new ArrayList<>();
|
||||||
if (theEntity.isParamsUriPopulated()) {
|
if (theEntity.isParamsUriPopulated()) {
|
||||||
paramsUri.addAll(theEntity.getParamsUri());
|
existingUriParams.addAll(theEntity.getParamsUri());
|
||||||
}
|
}
|
||||||
Collection<ResourceIndexedSearchParamCoords> paramsCoords = new ArrayList<>();
|
Collection<ResourceIndexedSearchParamCoords> existingCoordsParams = new ArrayList<>();
|
||||||
if (theEntity.isParamsCoordsPopulated()) {
|
if (theEntity.isParamsCoordsPopulated()) {
|
||||||
paramsCoords.addAll(theEntity.getParamsCoords());
|
existingCoordsParams.addAll(theEntity.getParamsCoords());
|
||||||
}
|
}
|
||||||
Collection<ResourceLink> existingResourceLinks = new ArrayList<>();
|
Collection<ResourceLink> existingResourceLinks = new ArrayList<>();
|
||||||
if (theEntity.isHasLinks()) {
|
if (theEntity.isHasLinks()) {
|
||||||
existingResourceLinks.addAll(theEntity.getResourceLinks());
|
existingResourceLinks.addAll(theEntity.getResourceLinks());
|
||||||
}
|
}
|
||||||
|
Collection<ResourceIndexedCompositeStringUnique> existingCompositeStringUniques = new ArrayList<>();
|
||||||
|
if (theEntity.isParamsCompositeStringUniquePresent()) {
|
||||||
|
existingCompositeStringUniques.addAll(theEntity.getParamsCompositeStringUnique());
|
||||||
|
}
|
||||||
|
|
||||||
Set<ResourceIndexedSearchParamString> stringParams = null;
|
Set<ResourceIndexedSearchParamString> stringParams = null;
|
||||||
Set<ResourceIndexedSearchParamToken> tokenParams = null;
|
Set<ResourceIndexedSearchParamToken> tokenParams = null;
|
||||||
|
@ -1331,6 +1407,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
Set<ResourceIndexedSearchParamDate> dateParams = null;
|
Set<ResourceIndexedSearchParamDate> dateParams = null;
|
||||||
Set<ResourceIndexedSearchParamUri> uriParams = null;
|
Set<ResourceIndexedSearchParamUri> uriParams = null;
|
||||||
Set<ResourceIndexedSearchParamCoords> coordsParams = null;
|
Set<ResourceIndexedSearchParamCoords> coordsParams = null;
|
||||||
|
Set<ResourceIndexedCompositeStringUnique> compositeStringUniques = null;
|
||||||
Set<ResourceLink> links = null;
|
Set<ResourceLink> links = null;
|
||||||
|
|
||||||
Set<String> populatedResourceLinkParameters = Collections.emptySet();
|
Set<String> populatedResourceLinkParameters = Collections.emptySet();
|
||||||
|
@ -1365,10 +1442,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
uriParams = extractSearchParamUri(theEntity, theResource);
|
uriParams = extractSearchParamUri(theEntity, theResource);
|
||||||
coordsParams = extractSearchParamCoords(theEntity, theResource);
|
coordsParams = extractSearchParamCoords(theEntity, theResource);
|
||||||
|
|
||||||
// ourLog.info("Indexing resource: {}", entity.getId());
|
|
||||||
ourLog.trace("Storing date indexes: {}", dateParams);
|
ourLog.trace("Storing date indexes: {}", dateParams);
|
||||||
|
|
||||||
tokenParams = new HashSet<ResourceIndexedSearchParamToken>();
|
tokenParams = new HashSet<>();
|
||||||
for (BaseResourceIndexedSearchParam next : extractSearchParamTokens(theEntity, theResource)) {
|
for (BaseResourceIndexedSearchParam next : extractSearchParamTokens(theEntity, theResource)) {
|
||||||
if (next instanceof ResourceIndexedSearchParamToken) {
|
if (next instanceof ResourceIndexedSearchParamToken) {
|
||||||
tokenParams.add((ResourceIndexedSearchParamToken) next);
|
tokenParams.add((ResourceIndexedSearchParamToken) next);
|
||||||
|
@ -1395,8 +1471,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the
|
* Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the
|
||||||
* matching
|
* matching resource.
|
||||||
* resource.
|
|
||||||
*/
|
*/
|
||||||
if (myConfig.isAllowInlineMatchUrlReferences()) {
|
if (myConfig.isAllowInlineMatchUrlReferences()) {
|
||||||
FhirTerser terser = getContext().newTerser();
|
FhirTerser terser = getContext().newTerser();
|
||||||
|
@ -1443,7 +1518,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
links = new HashSet<ResourceLink>();
|
links = new HashSet<>();
|
||||||
populatedResourceLinkParameters = extractResourceLinks(theEntity, theResource, links, theUpdateTime);
|
populatedResourceLinkParameters = extractResourceLinks(theEntity, theResource, links, theUpdateTime);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1457,6 +1532,12 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle composites
|
||||||
|
*/
|
||||||
|
compositeStringUniques = extractCompositeStringUniques(theEntity, stringParams, tokenParams, numberParams, quantityParams, dateParams, uriParams, links);
|
||||||
|
|
||||||
|
|
||||||
changed = populateResourceIntoEntity(theResource, theEntity, true);
|
changed = populateResourceIntoEntity(theResource, theEntity, true);
|
||||||
|
|
||||||
theEntity.setUpdated(theUpdateTime);
|
theEntity.setUpdated(theUpdateTime);
|
||||||
|
@ -1479,6 +1560,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
theEntity.setParamsUriPopulated(uriParams.isEmpty() == false);
|
theEntity.setParamsUriPopulated(uriParams.isEmpty() == false);
|
||||||
theEntity.setParamsCoords(coordsParams);
|
theEntity.setParamsCoords(coordsParams);
|
||||||
theEntity.setParamsCoordsPopulated(coordsParams.isEmpty() == false);
|
theEntity.setParamsCoordsPopulated(coordsParams.isEmpty() == false);
|
||||||
|
theEntity.setParamsCompositeStringUnique(compositeStringUniques);
|
||||||
|
theEntity.setParamsCompositeStringUniquePresent(compositeStringUniques.isEmpty() == false);
|
||||||
theEntity.setResourceLinks(links);
|
theEntity.setResourceLinks(links);
|
||||||
theEntity.setHasLinks(links.isEmpty() == false);
|
theEntity.setHasLinks(links.isEmpty() == false);
|
||||||
theEntity.setIndexStatus(INDEX_STATUS_INDEXED);
|
theEntity.setIndexStatus(INDEX_STATUS_INDEXED);
|
||||||
|
@ -1567,28 +1650,28 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
*/
|
*/
|
||||||
if (thePerformIndexing) {
|
if (thePerformIndexing) {
|
||||||
|
|
||||||
for (ResourceIndexedSearchParamString next : paramsString) {
|
for (ResourceIndexedSearchParamString next : existingStringParams) {
|
||||||
myEntityManager.remove(next);
|
myEntityManager.remove(next);
|
||||||
}
|
}
|
||||||
for (ResourceIndexedSearchParamString next : stringParams) {
|
for (ResourceIndexedSearchParamString next : stringParams) {
|
||||||
myEntityManager.persist(next);
|
myEntityManager.persist(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ResourceIndexedSearchParamToken next : paramsToken) {
|
for (ResourceIndexedSearchParamToken next : existingTokenParams) {
|
||||||
myEntityManager.remove(next);
|
myEntityManager.remove(next);
|
||||||
}
|
}
|
||||||
for (ResourceIndexedSearchParamToken next : tokenParams) {
|
for (ResourceIndexedSearchParamToken next : tokenParams) {
|
||||||
myEntityManager.persist(next);
|
myEntityManager.persist(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ResourceIndexedSearchParamNumber next : paramsNumber) {
|
for (ResourceIndexedSearchParamNumber next : existingNumberParams) {
|
||||||
myEntityManager.remove(next);
|
myEntityManager.remove(next);
|
||||||
}
|
}
|
||||||
for (ResourceIndexedSearchParamNumber next : numberParams) {
|
for (ResourceIndexedSearchParamNumber next : numberParams) {
|
||||||
myEntityManager.persist(next);
|
myEntityManager.persist(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ResourceIndexedSearchParamQuantity next : paramsQuantity) {
|
for (ResourceIndexedSearchParamQuantity next : existingQuantityParams) {
|
||||||
myEntityManager.remove(next);
|
myEntityManager.remove(next);
|
||||||
}
|
}
|
||||||
for (ResourceIndexedSearchParamQuantity next : quantityParams) {
|
for (ResourceIndexedSearchParamQuantity next : quantityParams) {
|
||||||
|
@ -1596,7 +1679,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store date SP's
|
// Store date SP's
|
||||||
for (ResourceIndexedSearchParamDate next : paramsDate) {
|
for (ResourceIndexedSearchParamDate next : existingDateParams) {
|
||||||
myEntityManager.remove(next);
|
myEntityManager.remove(next);
|
||||||
}
|
}
|
||||||
for (ResourceIndexedSearchParamDate next : dateParams) {
|
for (ResourceIndexedSearchParamDate next : dateParams) {
|
||||||
|
@ -1604,7 +1687,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store URI SP's
|
// Store URI SP's
|
||||||
for (ResourceIndexedSearchParamUri next : paramsUri) {
|
for (ResourceIndexedSearchParamUri next : existingUriParams) {
|
||||||
myEntityManager.remove(next);
|
myEntityManager.remove(next);
|
||||||
}
|
}
|
||||||
for (ResourceIndexedSearchParamUri next : uriParams) {
|
for (ResourceIndexedSearchParamUri next : uriParams) {
|
||||||
|
@ -1612,7 +1695,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store Coords SP's
|
// Store Coords SP's
|
||||||
for (ResourceIndexedSearchParamCoords next : paramsCoords) {
|
for (ResourceIndexedSearchParamCoords next : existingCoordsParams) {
|
||||||
myEntityManager.remove(next);
|
myEntityManager.remove(next);
|
||||||
}
|
}
|
||||||
for (ResourceIndexedSearchParamCoords next : coordsParams) {
|
for (ResourceIndexedSearchParamCoords next : coordsParams) {
|
||||||
|
@ -1629,6 +1712,14 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
// make sure links are indexed
|
// make sure links are indexed
|
||||||
theEntity.setResourceLinks(links);
|
theEntity.setResourceLinks(links);
|
||||||
|
|
||||||
|
// Store composite string uniques
|
||||||
|
for (ResourceIndexedCompositeStringUnique next : existingCompositeStringUniques) {
|
||||||
|
myEntityManager.remove(next);
|
||||||
|
}
|
||||||
|
for (ResourceIndexedCompositeStringUnique next : compositeStringUniques) {
|
||||||
|
myEntityManager.persist(next);
|
||||||
|
}
|
||||||
|
|
||||||
theEntity.toString();
|
theEntity.toString();
|
||||||
|
|
||||||
} // if thePerformIndexing
|
} // if thePerformIndexing
|
||||||
|
@ -1741,10 +1832,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
* This method is invoked immediately before storing a new resource, or an update to an existing resource to allow the DAO to ensure that it is valid for persistence. By default, checks for the
|
* This method is invoked immediately before storing a new resource, or an update to an existing resource to allow the DAO to ensure that it is valid for persistence. By default, checks for the
|
||||||
* "subsetted" tag and rejects resources which have it. Subclasses should call the superclass implementation to preserve this check.
|
* "subsetted" tag and rejects resources which have it. Subclasses should call the superclass implementation to preserve this check.
|
||||||
*
|
*
|
||||||
* @param theResource
|
* @param theResource The resource that is about to be persisted
|
||||||
* The resource that is about to be persisted
|
* @param theEntityToSave TODO
|
||||||
* @param theEntityToSave
|
|
||||||
* TODO
|
|
||||||
*/
|
*/
|
||||||
protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) {
|
protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) {
|
||||||
Object tag = null;
|
Object tag = null;
|
||||||
|
@ -1781,6 +1870,76 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to create a set of all possible combinations of
|
||||||
|
* parameters across a set of search parameters. An example of why
|
||||||
|
* this is needed:
|
||||||
|
* <p>
|
||||||
|
* Let's say we have a unique index on (Patient:gender AND Patient:name).
|
||||||
|
* Then we pass in <code>SMITH, John</code> with a gender of <code>male</code>.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* In this case, because the name parameter matches both first and last name,
|
||||||
|
* we now need two unique indexes:
|
||||||
|
* <ul>
|
||||||
|
* <li>Patient?gender=male&name=SMITH</li>
|
||||||
|
* <li>Patient?gender=male&name=JOHN</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* So this recursive algorithm calculates those
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param theResourceType E.g. <code>Patient
|
||||||
|
* @param thePartsChoices E.g. <code>[[gender=male], [name=SMITH, name=JOHN]]</code>
|
||||||
|
*/
|
||||||
|
public static Set<String> extractCompositeStringUniquesValueChains(String theResourceType, List<List<String>> thePartsChoices) {
|
||||||
|
|
||||||
|
Collections.sort(thePartsChoices, new Comparator<List<String>>() {
|
||||||
|
@Override
|
||||||
|
public int compare(List<String> o1, List<String> o2) {
|
||||||
|
String str1=null;
|
||||||
|
String str2=null;
|
||||||
|
if (o1.size() > 0) {
|
||||||
|
str1 = o1.get(0);
|
||||||
|
}
|
||||||
|
if (o2.size() > 0) {
|
||||||
|
str2 = o2.get(0);
|
||||||
|
}
|
||||||
|
return StringUtils.compare(str1, str2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
List<String> values = new ArrayList<>();
|
||||||
|
Set<String> queryStringsToPopulate = new HashSet<>();
|
||||||
|
extractCompositeStringUniquesValueChains(theResourceType, thePartsChoices, values, queryStringsToPopulate);
|
||||||
|
return queryStringsToPopulate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void extractCompositeStringUniquesValueChains(String theResourceType, List<List<String>> thePartsChoices, List<String> theValues, Set<String> theQueryStringsToPopulate) {
|
||||||
|
if (thePartsChoices.size() > 0) {
|
||||||
|
List<String> nextList = thePartsChoices.get(0);
|
||||||
|
Collections.sort(nextList);
|
||||||
|
for (String nextChoice : nextList) {
|
||||||
|
theValues.add(nextChoice);
|
||||||
|
extractCompositeStringUniquesValueChains(theResourceType, thePartsChoices.subList(1, thePartsChoices.size()), theValues, theQueryStringsToPopulate);
|
||||||
|
theValues.remove(theValues.size() - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (theValues.size() > 0) {
|
||||||
|
StringBuilder uniqueString = new StringBuilder();
|
||||||
|
uniqueString.append(theResourceType);
|
||||||
|
|
||||||
|
for (int i = 0; i < theValues.size(); i++) {
|
||||||
|
uniqueString.append(i == 0 ? "?" : "&");
|
||||||
|
uniqueString.append(theValues.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
theQueryStringsToPopulate.add(uniqueString.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected static boolean isValidPid(IIdType theId) {
|
protected static boolean isValidPid(IIdType theId) {
|
||||||
if (theId == null || theId.getIdPart() == null) {
|
if (theId == null || theId.getIdPart() == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -34,7 +34,10 @@ import ca.uhn.fhir.jpa.util.DeleteConflict;
|
||||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||||
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
|
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
|
||||||
import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils;
|
import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils;
|
||||||
import ca.uhn.fhir.model.api.*;
|
import ca.uhn.fhir.model.api.IQueryParameterAnd;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||||
import ca.uhn.fhir.rest.api.QualifiedParamList;
|
import ca.uhn.fhir.rest.api.QualifiedParamList;
|
||||||
|
@ -384,7 +387,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
|
|
||||||
// Notify JPA interceptors
|
// Notify JPA interceptors
|
||||||
if (theRequestDetails != null) {
|
if (theRequestDetails != null) {
|
||||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), theResource);
|
|
||||||
theRequestDetails.getRequestOperationCallback().resourceCreated(theResource);
|
theRequestDetails.getRequestOperationCallback().resourceCreated(theResource);
|
||||||
}
|
}
|
||||||
for (IServerInterceptor next : getConfig().getInterceptors()) {
|
for (IServerInterceptor next : getConfig().getInterceptors()) {
|
||||||
|
@ -871,8 +873,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
throw new ResourceNotFoundException(theId);
|
throw new ResourceNotFoundException(theId);
|
||||||
}
|
}
|
||||||
|
|
||||||
//@formatter:off
|
for (BaseTag next : new ArrayList<>(entity.getTags())) {
|
||||||
for (BaseTag next : new ArrayList<BaseTag>(entity.getTags())) {
|
|
||||||
if (ObjectUtil.equals(next.getTag().getTagType(), theTagType) &&
|
if (ObjectUtil.equals(next.getTag().getTagType(), theTagType) &&
|
||||||
ObjectUtil.equals(next.getTag().getSystem(), theScheme) &&
|
ObjectUtil.equals(next.getTag().getSystem(), theScheme) &&
|
||||||
ObjectUtil.equals(next.getTag().getCode(), theTerm)) {
|
ObjectUtil.equals(next.getTag().getCode(), theTerm)) {
|
||||||
|
@ -880,7 +881,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
entity.getTags().remove(next);
|
entity.getTags().remove(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//@formatter:on
|
|
||||||
|
|
||||||
if (entity.getTags().isEmpty()) {
|
if (entity.getTags().isEmpty()) {
|
||||||
entity.setHasTags(false);
|
entity.setHasTags(false);
|
||||||
|
|
|
@ -20,27 +20,28 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
|
public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
|
||||||
|
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(BaseSearchParamRegistry.class);
|
||||||
private Map<String, Map<String, RuntimeSearchParam>> myBuiltInSearchParams;
|
private Map<String, Map<String, RuntimeSearchParam>> myBuiltInSearchParams;
|
||||||
|
private volatile Map<String, List<JpaRuntimeSearchParam>> myActiveUniqueSearchParams;
|
||||||
|
private volatile Map<String, Map<Set<String>, List<JpaRuntimeSearchParam>>> myActiveParamNamesToUniqueSearchParams;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private FhirContext myCtx;
|
private FhirContext myCtx;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Collection<IFhirResourceDao<?>> myDaos;
|
private Collection<IFhirResourceDao<?>> myDaos;
|
||||||
|
|
||||||
|
@ -75,18 +76,119 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
|
||||||
return myBuiltInSearchParams.get(theResourceName);
|
return myBuiltInSearchParams.get(theResourceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName) {
|
||||||
|
refreshCacheIfNecessary();
|
||||||
|
List<JpaRuntimeSearchParam> retVal = myActiveUniqueSearchParams.get(theResourceName);
|
||||||
|
if (retVal == null) {
|
||||||
|
retVal = Collections.emptyList();
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName, Set<String> theParamNames) {
|
||||||
|
refreshCacheIfNecessary();
|
||||||
|
|
||||||
|
Map<Set<String>, List<JpaRuntimeSearchParam>> paramNamesToParams = myActiveParamNamesToUniqueSearchParams.get(theResourceName);
|
||||||
|
if (paramNamesToParams == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<JpaRuntimeSearchParam> retVal = paramNamesToParams.get(theParamNames);
|
||||||
|
if (retVal == null) {
|
||||||
|
retVal = Collections.emptyList();
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(retVal);
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, Map<String, RuntimeSearchParam>> getBuiltInSearchParams() {
|
public Map<String, Map<String, RuntimeSearchParam>> getBuiltInSearchParams() {
|
||||||
return myBuiltInSearchParams;
|
return myBuiltInSearchParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void populateActiveSearchParams(Map<String, Map<String, RuntimeSearchParam>> theActiveSearchParams) {
|
||||||
|
Map<String, List<JpaRuntimeSearchParam>> activeUniqueSearchParams = new HashMap<>();
|
||||||
|
Map<String, Map<Set<String>, List<JpaRuntimeSearchParam>>> activeParamNamesToUniqueSearchParams = new HashMap<>();
|
||||||
|
|
||||||
|
Map<String, RuntimeSearchParam> idToRuntimeSearchParam = new HashMap<>();
|
||||||
|
List<JpaRuntimeSearchParam> jpaSearchParams = new ArrayList<>();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loop through parameters and find JPA params
|
||||||
|
*/
|
||||||
|
for (Map.Entry<String, Map<String, RuntimeSearchParam>> nextResourceNameToEntries : theActiveSearchParams.entrySet()) {
|
||||||
|
List<JpaRuntimeSearchParam> uniqueSearchParams = activeUniqueSearchParams.get(nextResourceNameToEntries.getKey());
|
||||||
|
if (uniqueSearchParams == null) {
|
||||||
|
uniqueSearchParams = new ArrayList<>();
|
||||||
|
activeUniqueSearchParams.put(nextResourceNameToEntries.getKey(), uniqueSearchParams);
|
||||||
|
}
|
||||||
|
Collection<RuntimeSearchParam> nextSearchParamsForResourceName = nextResourceNameToEntries.getValue().values();
|
||||||
|
for (RuntimeSearchParam nextCandidate : nextSearchParamsForResourceName) {
|
||||||
|
|
||||||
|
if (nextCandidate.getId() != null) {
|
||||||
|
idToRuntimeSearchParam.put(nextCandidate.getId().toUnqualifiedVersionless().getValue(), nextCandidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextCandidate instanceof JpaRuntimeSearchParam) {
|
||||||
|
JpaRuntimeSearchParam nextCandidateCasted = (JpaRuntimeSearchParam) nextCandidate;
|
||||||
|
jpaSearchParams.add(nextCandidateCasted);
|
||||||
|
if (nextCandidateCasted.isUnique()) {
|
||||||
|
uniqueSearchParams.add(nextCandidateCasted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> haveSeen = new HashSet<>();
|
||||||
|
for (JpaRuntimeSearchParam next : jpaSearchParams) {
|
||||||
|
if (!haveSeen.add(next.getId().toUnqualifiedVersionless().getValue())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> paramNames = new HashSet<>();
|
||||||
|
for (JpaRuntimeSearchParam.Component nextComponent : next.getComponents()) {
|
||||||
|
String nextRef = nextComponent.getReference().getReferenceElement().toUnqualifiedVersionless().getValue();
|
||||||
|
RuntimeSearchParam componentTarget = idToRuntimeSearchParam.get(nextRef);
|
||||||
|
if (componentTarget != null) {
|
||||||
|
next.getCompositeOf().add(componentTarget);
|
||||||
|
paramNames.add(componentTarget.getName());
|
||||||
|
} else {
|
||||||
|
ourLog.warn("Search parameter {} refers to unknown component {}", next.getId().toUnqualifiedVersionless().getValue(), nextRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next.getCompositeOf() != null) {
|
||||||
|
Collections.sort(next.getCompositeOf(), new Comparator<RuntimeSearchParam>() {
|
||||||
|
@Override
|
||||||
|
public int compare(RuntimeSearchParam theO1, RuntimeSearchParam theO2) {
|
||||||
|
return StringUtils.compare(theO1.getName(), theO2.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (String nextBase : next.getBase()) {
|
||||||
|
if (!activeParamNamesToUniqueSearchParams.containsKey(nextBase)) {
|
||||||
|
activeParamNamesToUniqueSearchParams.put(nextBase, new HashMap<Set<String>, List<JpaRuntimeSearchParam>>());
|
||||||
|
}
|
||||||
|
if (!activeParamNamesToUniqueSearchParams.get(nextBase).containsKey(paramNames)) {
|
||||||
|
activeParamNamesToUniqueSearchParams.get(nextBase).put(paramNames, new ArrayList<JpaRuntimeSearchParam>());
|
||||||
|
}
|
||||||
|
activeParamNamesToUniqueSearchParams.get(nextBase).get(paramNames).add(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
myActiveUniqueSearchParams = activeUniqueSearchParams;
|
||||||
|
myActiveParamNamesToUniqueSearchParams = activeParamNamesToUniqueSearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void postConstruct() {
|
public void postConstruct() {
|
||||||
Map<String, Map<String, RuntimeSearchParam>> resourceNameToSearchParams = new HashMap<String, Map<String, RuntimeSearchParam>>();
|
Map<String, Map<String, RuntimeSearchParam>> resourceNameToSearchParams = new HashMap<>();
|
||||||
|
|
||||||
for (IFhirResourceDao<?> nextDao : myDaos) {
|
for (IFhirResourceDao<?> nextDao : myDaos) {
|
||||||
RuntimeResourceDefinition nextResDef = myCtx.getResourceDefinition(nextDao.getResourceType());
|
RuntimeResourceDefinition nextResDef = myCtx.getResourceDefinition(nextDao.getResourceType());
|
||||||
String nextResourceName = nextResDef.getName();
|
String nextResourceName = nextResDef.getName();
|
||||||
HashMap<String, RuntimeSearchParam> nameToParam = new HashMap<String, RuntimeSearchParam>();
|
HashMap<String, RuntimeSearchParam> nameToParam = new HashMap<>();
|
||||||
resourceNameToSearchParams.put(nextResourceName, nameToParam);
|
resourceNameToSearchParams.put(nextResourceName, nameToParam);
|
||||||
|
|
||||||
for (RuntimeSearchParam nextSp : nextResDef.getSearchParams()) {
|
for (RuntimeSearchParam nextSp : nextResDef.getSearchParams()) {
|
||||||
|
@ -97,4 +199,6 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
|
||||||
myBuiltInSearchParams = Collections.unmodifiableMap(resourceNameToSearchParams);
|
myBuiltInSearchParams = Collections.unmodifiableMap(resourceNameToSearchParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract void refreshCacheIfNecessary();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,21 +20,27 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public interface ISearchParamRegistry {
|
public interface ISearchParamRegistry {
|
||||||
|
|
||||||
void forceRefresh();
|
void forceRefresh();
|
||||||
|
|
||||||
Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams();
|
|
||||||
|
|
||||||
Map<String,RuntimeSearchParam> getActiveSearchParams(String theResourceName);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns {@literal null} if no match
|
* @return Returns {@literal null} if no match
|
||||||
*/
|
*/
|
||||||
RuntimeSearchParam getActiveSearchParam(String theResourceName, String theParamName);
|
RuntimeSearchParam getActiveSearchParam(String theResourceName, String theParamName);
|
||||||
|
|
||||||
|
Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName);
|
||||||
|
|
||||||
|
Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams();
|
||||||
|
|
||||||
|
List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName);
|
||||||
|
|
||||||
|
List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName, Set<String> theParamNames);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,19 +19,40 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import static org.apache.commons.lang3.StringUtils.*;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import ca.uhn.fhir.context.*;
|
||||||
import java.math.MathContext;
|
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||||
import java.util.*;
|
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
|
||||||
import java.util.Map.Entry;
|
import ca.uhn.fhir.jpa.entity.*;
|
||||||
|
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
|
||||||
import javax.persistence.EntityManager;
|
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
||||||
import javax.persistence.TypedQuery;
|
import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
|
||||||
import javax.persistence.criteria.*;
|
import ca.uhn.fhir.jpa.util.BaseIterator;
|
||||||
import javax.persistence.criteria.CriteriaBuilder.In;
|
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||||
|
import ca.uhn.fhir.model.api.*;
|
||||||
import org.apache.commons.lang3.*;
|
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||||
|
import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
|
||||||
|
import ca.uhn.fhir.model.base.composite.BaseQuantityDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
|
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||||
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.SortSpec;
|
||||||
|
import ca.uhn.fhir.rest.param.*;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
@ -39,28 +60,20 @@ import org.hibernate.ScrollMode;
|
||||||
import org.hibernate.ScrollableResults;
|
import org.hibernate.ScrollableResults;
|
||||||
import org.hibernate.query.Query;
|
import org.hibernate.query.Query;
|
||||||
import org.hl7.fhir.dstu3.model.BaseResource;
|
import org.hl7.fhir.dstu3.model.BaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.*;
|
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 com.google.common.collect.*;
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
|
import javax.persistence.criteria.*;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder.In;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.MathContext;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.*;
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
|
|
||||||
import ca.uhn.fhir.jpa.entity.*;
|
|
||||||
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
|
||||||
import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
|
|
||||||
import ca.uhn.fhir.jpa.util.BaseIterator;
|
|
||||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
|
||||||
import ca.uhn.fhir.model.api.*;
|
|
||||||
import ca.uhn.fhir.model.base.composite.*;
|
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
|
||||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
|
||||||
import ca.uhn.fhir.rest.api.*;
|
|
||||||
import ca.uhn.fhir.rest.param.*;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.*;
|
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SearchBuilder is responsible for actually forming the SQL query that handles
|
* The SearchBuilder is responsible for actually forming the SQL query that handles
|
||||||
|
@ -69,8 +82,9 @@ import ca.uhn.fhir.util.UrlUtil;
|
||||||
public class SearchBuilder implements ISearchBuilder {
|
public class SearchBuilder implements ISearchBuilder {
|
||||||
|
|
||||||
private static final List<Long> EMPTY_LONG_LIST = Collections.unmodifiableList(new ArrayList<Long>());
|
private static final List<Long> EMPTY_LONG_LIST = Collections.unmodifiableList(new ArrayList<Long>());
|
||||||
private static Long NO_MORE = Long.valueOf(-1);
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBuilder.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBuilder.class);
|
||||||
|
private static Long NO_MORE = Long.valueOf(-1);
|
||||||
|
private static HandlerTypeEnum ourLastHandlerMechanismForUnitTest;
|
||||||
private List<Long> myAlsoIncludePids;
|
private List<Long> myAlsoIncludePids;
|
||||||
private CriteriaBuilder myBuilder;
|
private CriteriaBuilder myBuilder;
|
||||||
private BaseHapiFhirDao<?> myCallingDao;
|
private BaseHapiFhirDao<?> myCallingDao;
|
||||||
|
@ -135,7 +149,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Predicate> codePredicates = new ArrayList<Predicate>();
|
List<Predicate> codePredicates = new ArrayList<>();
|
||||||
for (IQueryParameterType nextOr : theList) {
|
for (IQueryParameterType nextOr : theList) {
|
||||||
IQueryParameterType params = nextOr;
|
IQueryParameterType params = nextOr;
|
||||||
Predicate p = createPredicateDate(params, theResourceName, theParamName, myBuilder, join);
|
Predicate p = createPredicateDate(params, theResourceName, theParamName, myBuilder, join);
|
||||||
|
@ -358,7 +372,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
if (!ref.getValue().matches("[a-zA-Z]+\\/.*")) {
|
if (!ref.getValue().matches("[a-zA-Z]+\\/.*")) {
|
||||||
|
|
||||||
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam(theResourceName, theParamName);
|
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam(theResourceName, theParamName);
|
||||||
resourceTypes = new ArrayList<Class<? extends IBaseResource>>();
|
resourceTypes = new ArrayList<>();
|
||||||
|
|
||||||
Set<String> targetTypes = param.getTargets();
|
Set<String> targetTypes = param.getTargets();
|
||||||
|
|
||||||
|
@ -785,7 +799,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
*/
|
*/
|
||||||
ourLog.info("Searching for candidate URI:above parameters for Resource[{}] param[{}]", myResourceName, theParamName);
|
ourLog.info("Searching for candidate URI:above parameters for Resource[{}] param[{}]", myResourceName, theParamName);
|
||||||
Collection<String> candidates = myResourceIndexedSearchParamUriDao.findAllByResourceTypeAndParamName(myResourceName, theParamName);
|
Collection<String> candidates = myResourceIndexedSearchParamUriDao.findAllByResourceTypeAndParamName(myResourceName, theParamName);
|
||||||
List<String> toFind = new ArrayList<String>();
|
List<String> toFind = new ArrayList<>();
|
||||||
for (String next : candidates) {
|
for (String next : candidates) {
|
||||||
if (value.length() >= next.length()) {
|
if (value.length() >= next.length()) {
|
||||||
if (value.substring(0, next.length()).equals(next)) {
|
if (value.substring(0, next.length()).equals(next)) {
|
||||||
|
@ -801,9 +815,9 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
predicate = join.<Object>get("myUri").as(String.class).in(toFind);
|
predicate = join.<Object>get("myUri").as(String.class).in(toFind);
|
||||||
|
|
||||||
} else if (param.getQualifier() == UriParamQualifierEnum.BELOW) {
|
} else if (param.getQualifier() == UriParamQualifierEnum.BELOW) {
|
||||||
predicate = myBuilder.like(join.<Object> get("myUri").as(String.class), createLeftMatchLikeExpression(value));
|
predicate = myBuilder.like(join.get("myUri").as(String.class), createLeftMatchLikeExpression(value));
|
||||||
} else {
|
} else {
|
||||||
predicate = myBuilder.equal(join.<Object> get("myUri").as(String.class), value);
|
predicate = myBuilder.equal(join.get("myUri").as(String.class), value);
|
||||||
}
|
}
|
||||||
codePredicates.add(predicate);
|
codePredicates.add(predicate);
|
||||||
} else {
|
} else {
|
||||||
|
@ -817,7 +831,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
* just add a predicate that can never match
|
* just add a predicate that can never match
|
||||||
*/
|
*/
|
||||||
if (codePredicates.isEmpty()) {
|
if (codePredicates.isEmpty()) {
|
||||||
Predicate predicate = myBuilder.isNull(join.<Object> get("myMissing").as(String.class));
|
Predicate predicate = myBuilder.isNull(join.get("myMissing").as(String.class));
|
||||||
myPredicates.add(predicate);
|
myPredicates.add(predicate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1240,6 +1254,45 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
myBuilder = myEntityManager.getCriteriaBuilder();
|
myBuilder = myEntityManager.getCriteriaBuilder();
|
||||||
mySearchUuid = theSearchUuid;
|
mySearchUuid = theSearchUuid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if there is a unique key associated with the set
|
||||||
|
* of parameters passed in
|
||||||
|
*/
|
||||||
|
if (myParams.getIncludes().isEmpty()) {
|
||||||
|
if (myParams.getRevIncludes().isEmpty()) {
|
||||||
|
if (myParams.getEverythingMode() == null) {
|
||||||
|
if (myParams.isAllParametersHaveNoModifier()) {
|
||||||
|
Set<String> paramNames = theParams.keySet();
|
||||||
|
if (paramNames.isEmpty() == false) {
|
||||||
|
List<JpaRuntimeSearchParam> searchParams = mySearchParamRegistry.getActiveUniqueSearchParams(myResourceName, paramNames);
|
||||||
|
if (searchParams.size() > 0) {
|
||||||
|
List<List<String>> params = new ArrayList<>();
|
||||||
|
for (Entry<String, List<List<? extends IQueryParameterType>>> nextParamNameToValues : theParams.entrySet()) {
|
||||||
|
String nextParamName = nextParamNameToValues.getKey();
|
||||||
|
nextParamName = UrlUtil.escape(nextParamName);
|
||||||
|
for (List<? extends IQueryParameterType> nextAnd : nextParamNameToValues.getValue()) {
|
||||||
|
ArrayList<String> nextValueList = new ArrayList<>();
|
||||||
|
params.add(nextValueList);
|
||||||
|
for (IQueryParameterType nextOr : nextAnd) {
|
||||||
|
String nextOrValue = nextOr.getValueAsQueryToken(myContext);
|
||||||
|
nextOrValue = UrlUtil.escape(nextOrValue);
|
||||||
|
nextValueList.add(nextParamName + "=" + nextOrValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> uniqueQueryStrings = BaseHapiFhirDao.extractCompositeStringUniquesValueChains(myResourceName, params);
|
||||||
|
ourLastHandlerMechanismForUnitTest = HandlerTypeEnum.UNIQUE_INDEX;
|
||||||
|
return new UniqueIndexIterator(uniqueQueryStrings);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ourLastHandlerMechanismForUnitTest = HandlerTypeEnum.STANDARD_QUERY;
|
||||||
return new QueryIterator();
|
return new QueryIterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1557,7 +1610,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
|
|
||||||
// Dupes will cause a crash later anyhow, but this is expensive so only do it
|
// Dupes will cause a crash later anyhow, but this is expensive so only do it
|
||||||
// when running asserts
|
// when running asserts
|
||||||
assert new HashSet<Long>(theIncludePids).size() == theIncludePids.size() : "PID list contains duplicates: " + theIncludePids;
|
assert new HashSet<>(theIncludePids).size() == theIncludePids.size() : "PID list contains duplicates: " + theIncludePids;
|
||||||
|
|
||||||
Map<Long, Integer> position = new HashMap<Long, Integer>();
|
Map<Long, Integer> position = new HashMap<Long, Integer>();
|
||||||
for (Long next : theIncludePids) {
|
for (Long next : theIncludePids) {
|
||||||
|
@ -1572,7 +1625,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
* but this should work too. Sigh.
|
* but this should work too. Sigh.
|
||||||
*/
|
*/
|
||||||
int maxLoad = 800;
|
int maxLoad = 800;
|
||||||
List<Long> pids = new ArrayList<Long>(theIncludePids);
|
List<Long> pids = new ArrayList<>(theIncludePids);
|
||||||
for (int i = 0; i < pids.size(); i += maxLoad) {
|
for (int i = 0; i < pids.size(); i += maxLoad) {
|
||||||
int to = i + maxLoad;
|
int to = i + maxLoad;
|
||||||
to = Math.min(to, pids.size());
|
to = Math.min(to, pids.size());
|
||||||
|
@ -1599,9 +1652,9 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
String searchFieldName = theReverseMode ? "myTargetResourcePid" : "mySourceResourcePid";
|
String searchFieldName = theReverseMode ? "myTargetResourcePid" : "mySourceResourcePid";
|
||||||
|
|
||||||
Collection<Long> nextRoundMatches = theMatches;
|
Collection<Long> nextRoundMatches = theMatches;
|
||||||
HashSet<Long> allAdded = new HashSet<Long>();
|
HashSet<Long> allAdded = new HashSet<>();
|
||||||
HashSet<Long> original = new HashSet<Long>(theMatches);
|
HashSet<Long> original = new HashSet<>(theMatches);
|
||||||
ArrayList<Include> includes = new ArrayList<Include>(theRevIncludes);
|
ArrayList<Include> includes = new ArrayList<>(theRevIncludes);
|
||||||
|
|
||||||
int roundCounts = 0;
|
int roundCounts = 0;
|
||||||
StopWatch w = new StopWatch();
|
StopWatch w = new StopWatch();
|
||||||
|
@ -1610,8 +1663,8 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
do {
|
do {
|
||||||
roundCounts++;
|
roundCounts++;
|
||||||
|
|
||||||
HashSet<Long> pidsToInclude = new HashSet<Long>();
|
HashSet<Long> pidsToInclude = new HashSet<>();
|
||||||
Set<Long> nextRoundOmit = new HashSet<Long>();
|
Set<Long> nextRoundOmit = new HashSet<>();
|
||||||
|
|
||||||
for (Iterator<Include> iter = includes.iterator(); iter.hasNext(); ) {
|
for (Iterator<Include> iter = includes.iterator(); iter.hasNext(); ) {
|
||||||
Include nextInclude = iter.next();
|
Include nextInclude = iter.next();
|
||||||
|
@ -1958,10 +2011,37 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
return resultList;
|
return resultList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public static HandlerTypeEnum getLastHandlerMechanismForUnitTest() {
|
||||||
|
ourLog.info("Retrieving last handler mechanism: {}", ourLastHandlerMechanismForUnitTest);
|
||||||
|
return ourLastHandlerMechanismForUnitTest;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public static void resetLastHandlerMechanismForUnitTest() {
|
||||||
|
ourLog.info("Clearing last handler mechanism (was {})", ourLastHandlerMechanismForUnitTest);
|
||||||
|
ourLastHandlerMechanismForUnitTest = null;
|
||||||
|
}
|
||||||
|
|
||||||
static Predicate[] toArray(List<Predicate> thePredicates) {
|
static Predicate[] toArray(List<Predicate> thePredicates) {
|
||||||
return thePredicates.toArray(new Predicate[thePredicates.size()]);
|
return thePredicates.toArray(new Predicate[thePredicates.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum HandlerTypeEnum {
|
||||||
|
UNIQUE_INDEX, STANDARD_QUERY
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum JoinEnum {
|
||||||
|
DATE,
|
||||||
|
NUMBER,
|
||||||
|
QUANTITY,
|
||||||
|
REFERENCE,
|
||||||
|
STRING,
|
||||||
|
TOKEN,
|
||||||
|
URI
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public class IncludesIterator extends BaseIterator<Long> implements Iterator<Long> {
|
public class IncludesIterator extends BaseIterator<Long> implements Iterator<Long> {
|
||||||
|
|
||||||
private Iterator<Long> myCurrentIterator;
|
private Iterator<Long> myCurrentIterator;
|
||||||
|
@ -2020,51 +2100,12 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum JoinEnum {
|
|
||||||
DATE,
|
|
||||||
NUMBER,
|
|
||||||
QUANTITY,
|
|
||||||
REFERENCE,
|
|
||||||
STRING,
|
|
||||||
TOKEN,
|
|
||||||
URI
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class JoinKey {
|
|
||||||
private final JoinEnum myJoinType;
|
|
||||||
private final String myParamName;
|
|
||||||
|
|
||||||
public JoinKey(String theParamName, JoinEnum theJoinType) {
|
|
||||||
super();
|
|
||||||
myParamName = theParamName;
|
|
||||||
myJoinType = theJoinType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object theObj) {
|
|
||||||
JoinKey obj = (JoinKey) theObj;
|
|
||||||
return new EqualsBuilder()
|
|
||||||
.append(myParamName, obj.myParamName)
|
|
||||||
.append(myJoinType, obj.myJoinType)
|
|
||||||
.isEquals();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return new HashCodeBuilder()
|
|
||||||
.append(myParamName)
|
|
||||||
.append(myJoinType)
|
|
||||||
.toHashCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class QueryIterator extends BaseIterator<Long> implements Iterator<Long> {
|
private final class QueryIterator extends BaseIterator<Long> implements Iterator<Long> {
|
||||||
|
|
||||||
|
private final Set<Long> myPidSet = new HashSet<Long>();
|
||||||
private boolean myFirst = true;
|
private boolean myFirst = true;
|
||||||
private IncludesIterator myIncludesIterator;
|
private IncludesIterator myIncludesIterator;
|
||||||
private Long myNext;
|
private Long myNext;
|
||||||
private final Set<Long> myPidSet = new HashSet<Long>();
|
|
||||||
private Iterator<Long> myPreResultsIterator;
|
private Iterator<Long> myPreResultsIterator;
|
||||||
private Iterator<Long> myResultsIterator;
|
private Iterator<Long> myResultsIterator;
|
||||||
private SortSpec mySort;
|
private SortSpec mySort;
|
||||||
|
@ -2215,4 +2256,68 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class UniqueIndexIterator implements Iterator<Long> {
|
||||||
|
private final Set<String> myUniqueQueryStrings;
|
||||||
|
private Iterator<Long> myWrap = null;
|
||||||
|
|
||||||
|
public UniqueIndexIterator(Set<String> theUniqueQueryStrings) {
|
||||||
|
myUniqueQueryStrings = theUniqueQueryStrings;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureHaveQuery() {
|
||||||
|
if (myWrap == null) {
|
||||||
|
ourLog.info("Searching for unique index matches over {} candidate query strings");
|
||||||
|
StopWatch sw = new StopWatch();
|
||||||
|
Collection<Long> resourcePids = myCallingDao.getResourceIndexedCompositeStringUniqueDao().findResourcePidsByQueryStrings(myUniqueQueryStrings);
|
||||||
|
ourLog.info("Found {} unique index matches in {}ms", resourcePids.size(), sw.getMillis());
|
||||||
|
myWrap = resourcePids.iterator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
ensureHaveQuery();
|
||||||
|
return myWrap.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long next() {
|
||||||
|
ensureHaveQuery();
|
||||||
|
return myWrap.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class JoinKey {
|
||||||
|
private final JoinEnum myJoinType;
|
||||||
|
private final String myParamName;
|
||||||
|
|
||||||
|
public JoinKey(String theParamName, JoinEnum theJoinType) {
|
||||||
|
super();
|
||||||
|
myParamName = theParamName;
|
||||||
|
myJoinType = theJoinType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object theObj) {
|
||||||
|
JoinKey obj = (JoinKey) theObj;
|
||||||
|
return new EqualsBuilder()
|
||||||
|
.append(myParamName, obj.myParamName)
|
||||||
|
.append(myJoinType, obj.myJoinType)
|
||||||
|
.isEquals();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return new HashCodeBuilder()
|
||||||
|
.append(myParamName)
|
||||||
|
.append(myJoinType)
|
||||||
|
.toHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,13 +116,13 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
|
||||||
if (nextValue.isEmpty()) {
|
if (nextValue.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getValue(), nextValue.getValue());
|
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getValue(), nextValue.getValue(), nextValue.getValueAsString());
|
||||||
} else if (nextObject instanceof PeriodDt) {
|
} else if (nextObject instanceof PeriodDt) {
|
||||||
PeriodDt nextValue = (PeriodDt) nextObject;
|
PeriodDt nextValue = (PeriodDt) nextObject;
|
||||||
if (nextValue.isEmpty()) {
|
if (nextValue.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getStart(), nextValue.getEnd());
|
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getStart(), nextValue.getEnd(), nextValue.getStartElement().getValueAsString());
|
||||||
} else {
|
} else {
|
||||||
if (!multiType) {
|
if (!multiType) {
|
||||||
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
|
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
|
||||||
|
|
|
@ -21,6 +21,8 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class SearchParamRegistryDstu2 extends BaseSearchParamRegistry {
|
public class SearchParamRegistryDstu2 extends BaseSearchParamRegistry {
|
||||||
|
@Override
|
||||||
|
protected void refreshCacheIfNecessary() {
|
||||||
// nothing yet
|
// nothing yet
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,7 +201,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
||||||
|
|
||||||
public Set<Include> getRevIncludes() {
|
public Set<Include> getRevIncludes() {
|
||||||
if (myRevIncludes == null) {
|
if (myRevIncludes == null) {
|
||||||
myRevIncludes = new HashSet<Include>();
|
myRevIncludes = new HashSet<>();
|
||||||
}
|
}
|
||||||
return myRevIncludes;
|
return myRevIncludes;
|
||||||
}
|
}
|
||||||
|
@ -210,6 +210,22 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
||||||
return mySort;
|
return mySort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will only return true if all parameters have no modifier of any kind
|
||||||
|
*/
|
||||||
|
public boolean isAllParametersHaveNoModifier() {
|
||||||
|
for (List<List<? extends IQueryParameterType>> nextParamName : values()) {
|
||||||
|
for (List<? extends IQueryParameterType> nextAnd : nextParamName) {
|
||||||
|
for (IQueryParameterType nextOr : nextAnd) {
|
||||||
|
if (isNotBlank(nextOr.getQueryParameterQualifier())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set, tells the server to load these results synchronously, and not to load
|
* If set, tells the server to load these results synchronously, and not to load
|
||||||
* more than X results
|
* more than X results
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.data;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedCompositeStringUnique;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface IResourceIndexedCompositeStringUniqueDao extends JpaRepository<ResourceIndexedCompositeStringUnique, Long> {
|
||||||
|
|
||||||
|
@Query("SELECT r FROM ResourceIndexedCompositeStringUnique r WHERE r.myIndexString = :str")
|
||||||
|
ResourceIndexedCompositeStringUnique findByQueryString(@Param("str") String theQueryString);
|
||||||
|
|
||||||
|
@Query("SELECT r.myResourceId FROM ResourceIndexedCompositeStringUnique r WHERE r.myIndexString IN :str")
|
||||||
|
Collection<Long> findResourcePidsByQueryStrings(@Param("str") Collection<String> theQueryString);
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,26 @@
|
||||||
package ca.uhn.fhir.jpa.dao.dstu3;
|
package ca.uhn.fhir.jpa.dao.dstu3;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.BaseSearchParamExtractor;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.ISearchParamRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||||
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
import org.hl7.fhir.dstu3.model.Bundle;
|
||||||
|
import org.hl7.fhir.dstu3.model.Enumerations;
|
||||||
|
import org.hl7.fhir.dstu3.model.Meta;
|
||||||
|
import org.hl7.fhir.dstu3.model.SearchParameter;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.transaction.TransactionDefinition;
|
||||||
|
import org.springframework.transaction.TransactionStatus;
|
||||||
|
import org.springframework.transaction.support.TransactionCallback;
|
||||||
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
|
@ -22,27 +42,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
|
||||||
import org.hl7.fhir.dstu3.model.Bundle;
|
|
||||||
import org.hl7.fhir.dstu3.model.Meta;
|
|
||||||
import org.hl7.fhir.dstu3.model.SearchParameter;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.transaction.TransactionDefinition;
|
|
||||||
import org.springframework.transaction.TransactionStatus;
|
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
|
||||||
import org.springframework.transaction.support.TransactionCallback;
|
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.BaseSearchParamExtractor;
|
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter;
|
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchParamRegistry;
|
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
|
||||||
import ca.uhn.fhir.util.ElementUtil;
|
|
||||||
|
|
||||||
public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
|
public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoSearchParameterDstu3.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoSearchParameterDstu3.class);
|
||||||
|
@ -56,6 +55,7 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
|
||||||
protected void markAffectedResources(SearchParameter theResource) {
|
protected void markAffectedResources(SearchParameter theResource) {
|
||||||
if (theResource != null) {
|
if (theResource != null) {
|
||||||
String expression = theResource.getExpression();
|
String expression = theResource.getExpression();
|
||||||
|
if (isNotBlank(expression)) {
|
||||||
final String resourceType = expression.substring(0, expression.indexOf('.'));
|
final String resourceType = expression.substring(0, expression.indexOf('.'));
|
||||||
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", expression);
|
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", expression);
|
||||||
|
|
||||||
|
@ -70,6 +70,7 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
|
||||||
|
|
||||||
ourLog.info("Marked {} resources for reindexing", updatedCount);
|
ourLog.info("Marked {} resources for reindexing", updatedCount);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mySearchParamRegistry.forceRefresh();
|
mySearchParamRegistry.forceRefresh();
|
||||||
}
|
}
|
||||||
|
@ -126,13 +127,15 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
|
||||||
}
|
}
|
||||||
|
|
||||||
String expression = theResource.getExpression();
|
String expression = theResource.getExpression();
|
||||||
if (isBlank(expression)) {
|
if (theResource.getType() == Enumerations.SearchParamType.COMPOSITE && isBlank(expression)) {
|
||||||
throw new UnprocessableEntityException("SearchParameter.expression is missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ElementUtil.isEmpty(theResource.getBase())) {
|
// this is ok
|
||||||
throw new UnprocessableEntityException("SearchParameter.base is missing");
|
|
||||||
}
|
} else if (isBlank(expression)) {
|
||||||
|
|
||||||
|
throw new UnprocessableEntityException("SearchParameter.expression is missing");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
expression = expression.trim();
|
expression = expression.trim();
|
||||||
theResource.setExpression(expression);
|
theResource.setExpression(expression);
|
||||||
|
@ -164,6 +167,8 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // if have expression
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,29 +133,33 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
|
||||||
if (nextValue.isEmpty()) {
|
if (nextValue.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getValue(), nextValue.getValue());
|
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getValue(), nextValue.getValue(), nextValue.getValueAsString());
|
||||||
} else if (nextObject instanceof Period) {
|
} else if (nextObject instanceof Period) {
|
||||||
Period nextValue = (Period) nextObject;
|
Period nextValue = (Period) nextObject;
|
||||||
if (nextValue.isEmpty()) {
|
if (nextValue.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getStart(), nextValue.getEnd());
|
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getStart(), nextValue.getEnd(), nextValue.getStartElement().getValueAsString());
|
||||||
} else if (nextObject instanceof Timing) {
|
} else if (nextObject instanceof Timing) {
|
||||||
Timing nextValue = (Timing) nextObject;
|
Timing nextValue = (Timing) nextObject;
|
||||||
if (nextValue.isEmpty()) {
|
if (nextValue.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
TreeSet<Date> dates = new TreeSet<Date>();
|
String firstValue = null;
|
||||||
|
TreeSet<Date> dates = new TreeSet<>();
|
||||||
for (DateTimeType nextEvent : nextValue.getEvent()) {
|
for (DateTimeType nextEvent : nextValue.getEvent()) {
|
||||||
if (nextEvent.getValue() != null) {
|
if (nextEvent.getValue() != null) {
|
||||||
dates.add(nextEvent.getValue());
|
dates.add(nextEvent.getValue());
|
||||||
|
if (firstValue == null) {
|
||||||
|
firstValue = nextEvent.getValueAsString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dates.isEmpty()) {
|
if (dates.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), dates.first(), dates.last());
|
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), dates.first(), dates.last(), firstValue);
|
||||||
} else if (nextObject instanceof StringType) {
|
} else if (nextObject instanceof StringType) {
|
||||||
// CarePlan.activitydate can be a string
|
// CarePlan.activitydate can be a string
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -26,11 +26,15 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.jpa.util.JpaConstants;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
import org.hl7.fhir.dstu3.model.CodeType;
|
import org.hl7.fhir.dstu3.model.CodeType;
|
||||||
|
import org.hl7.fhir.dstu3.model.Extension;
|
||||||
import org.hl7.fhir.dstu3.model.SearchParameter;
|
import org.hl7.fhir.dstu3.model.SearchParameter;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
|
@ -42,6 +46,7 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
|
||||||
public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
|
public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamRegistryDstu3.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamRegistryDstu3.class);
|
||||||
|
public static final int MAX_MANAGED_PARAM_COUNT = 10000;
|
||||||
|
|
||||||
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams;
|
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams;
|
||||||
|
|
||||||
|
@ -62,33 +67,33 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() {
|
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() {
|
||||||
refreshCacheIfNeccesary();
|
refreshCacheIfNecessary();
|
||||||
return myActiveSearchParams;
|
return myActiveSearchParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
|
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
|
||||||
refreshCacheIfNeccesary();
|
refreshCacheIfNecessary();
|
||||||
return myActiveSearchParams.get(theResourceName);
|
return myActiveSearchParams.get(theResourceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
|
private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
|
||||||
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
|
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
|
||||||
if (retVal == null) {
|
if (retVal == null) {
|
||||||
retVal = new HashMap<String, RuntimeSearchParam>();
|
retVal = new HashMap<>();
|
||||||
searchParams.put(theResourceName, retVal);
|
searchParams.put(theResourceName, retVal);
|
||||||
}
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshCacheIfNeccesary() {
|
protected void refreshCacheIfNecessary() {
|
||||||
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
|
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
|
||||||
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
|
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
|
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
|
||||||
StopWatch sw = new StopWatch();
|
StopWatch sw = new StopWatch();
|
||||||
|
|
||||||
Map<String, Map<String, RuntimeSearchParam>> searchParams = new HashMap<String, Map<String, RuntimeSearchParam>>();
|
Map<String, Map<String, RuntimeSearchParam>> searchParams = new HashMap<>();
|
||||||
for (Entry<String, Map<String, RuntimeSearchParam>> nextBuiltInEntry : getBuiltInSearchParams().entrySet()) {
|
for (Entry<String, Map<String, RuntimeSearchParam>> nextBuiltInEntry : getBuiltInSearchParams().entrySet()) {
|
||||||
for (RuntimeSearchParam nextParam : nextBuiltInEntry.getValue().values()) {
|
for (RuntimeSearchParam nextParam : nextBuiltInEntry.getValue().values()) {
|
||||||
String nextResourceName = nextBuiltInEntry.getKey();
|
String nextResourceName = nextBuiltInEntry.getKey();
|
||||||
|
@ -97,40 +102,41 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchParameterMap params = new SearchParameterMap();
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
params.setLoadSynchronous(true);
|
params.setLoadSynchronousUpTo(MAX_MANAGED_PARAM_COUNT);
|
||||||
|
|
||||||
IBundleProvider allSearchParamsBp = mySpDao.search(params);
|
IBundleProvider allSearchParamsBp = mySpDao.search(params);
|
||||||
int size = allSearchParamsBp.size();
|
int size = allSearchParamsBp.size();
|
||||||
|
|
||||||
// Just in case..
|
// Just in case..
|
||||||
if (size > 10000) {
|
if (size > MAX_MANAGED_PARAM_COUNT) {
|
||||||
ourLog.warn("Unable to support >10000 search params!");
|
ourLog.warn("Unable to support >" + MAX_MANAGED_PARAM_COUNT + " search params!");
|
||||||
size = 10000;
|
size = MAX_MANAGED_PARAM_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<IBaseResource> allSearchParams = allSearchParamsBp.getResources(0, size);
|
List<IBaseResource> allSearchParams = allSearchParamsBp.getResources(0, size);
|
||||||
for (IBaseResource nextResource : allSearchParams) {
|
for (IBaseResource nextResource : allSearchParams) {
|
||||||
SearchParameter nextSp = (SearchParameter) nextResource;
|
SearchParameter nextSp = (SearchParameter) nextResource;
|
||||||
RuntimeSearchParam runtimeSp = toRuntimeSp(nextSp);
|
JpaRuntimeSearchParam runtimeSp = toRuntimeSp(nextSp);
|
||||||
if (runtimeSp == null) {
|
if (runtimeSp == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dotIdx = runtimeSp.getPath().indexOf('.');
|
for (org.hl7.fhir.dstu3.model.CodeType nextBaseName : nextSp.getBase()) {
|
||||||
if (dotIdx == -1) {
|
String resourceType = nextBaseName.getValue();
|
||||||
ourLog.warn("Can not determine resource type of {}", runtimeSp.getPath());
|
if (isBlank(resourceType)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String resourceType = runtimeSp.getPath().substring(0, dotIdx);
|
|
||||||
|
|
||||||
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, resourceType);
|
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, resourceType);
|
||||||
String name = runtimeSp.getName();
|
String name = runtimeSp.getName();
|
||||||
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
|
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
|
||||||
searchParamMap.put(name, runtimeSp);
|
searchParamMap.put(name, runtimeSp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Map<String, RuntimeSearchParam>> activeSearchParams = new HashMap<String, Map<String, RuntimeSearchParam>>();
|
Map<String, Map<String, RuntimeSearchParam>> activeSearchParams = new HashMap<>();
|
||||||
for (Entry<String, Map<String, RuntimeSearchParam>> nextEntry : searchParams.entrySet()) {
|
for (Entry<String, Map<String, RuntimeSearchParam>> nextEntry : searchParams.entrySet()) {
|
||||||
for (RuntimeSearchParam nextSp : nextEntry.getValue().values()) {
|
for (RuntimeSearchParam nextSp : nextEntry.getValue().values()) {
|
||||||
String nextName = nextSp.getName();
|
String nextName = nextSp.getName();
|
||||||
|
@ -155,6 +161,8 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
|
||||||
|
|
||||||
myActiveSearchParams = activeSearchParams;
|
myActiveSearchParams = activeSearchParams;
|
||||||
|
|
||||||
|
super.populateActiveSearchParams(activeSearchParams);
|
||||||
|
|
||||||
myLastRefresh = System.currentTimeMillis();
|
myLastRefresh = System.currentTimeMillis();
|
||||||
ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis());
|
ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis());
|
||||||
}
|
}
|
||||||
|
@ -162,7 +170,7 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
|
private JpaRuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
|
||||||
String name = theNextSp.getCode();
|
String name = theNextSp.getCode();
|
||||||
String description = theNextSp.getDescription();
|
String description = theNextSp.getDescription();
|
||||||
String path = theNextSp.getExpression();
|
String path = theNextSp.getExpression();
|
||||||
|
@ -215,12 +223,31 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
|
||||||
Set<String> targets = toStrings(theNextSp.getTarget());
|
Set<String> targets = toStrings(theNextSp.getTarget());
|
||||||
|
|
||||||
if (isBlank(name) || isBlank(path) || paramType == null) {
|
if (isBlank(name) || isBlank(path) || paramType == null) {
|
||||||
|
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IIdType id = theNextSp.getIdElement();
|
IIdType id = theNextSp.getIdElement();
|
||||||
String uri = "";
|
String uri = "";
|
||||||
RuntimeSearchParam retVal = new RuntimeSearchParam(id, uri, name, description, path, paramType, null, providesMembershipInCompartments, targets, status);
|
boolean unique = false;
|
||||||
|
|
||||||
|
List<Extension> uniqueExts = theNextSp.getExtensionsByUrl(JpaConstants.EXT_SP_UNIQUE);
|
||||||
|
if (uniqueExts.size() > 0) {
|
||||||
|
IPrimitiveType<?> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
|
||||||
|
if (uniqueExtsValuePrimitive != null) {
|
||||||
|
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
|
||||||
|
unique = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<JpaRuntimeSearchParam.Component> components = new ArrayList<>();
|
||||||
|
for (SearchParameter.SearchParameterComponentComponent next : theNextSp.getComponent()) {
|
||||||
|
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), next.getDefinition()));
|
||||||
|
}
|
||||||
|
|
||||||
|
JpaRuntimeSearchParam retVal = new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4;
|
package ca.uhn.fhir.jpa.dao.r4;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
|
@ -24,6 +25,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
|
import org.hl7.fhir.r4.model.Enumerations;
|
||||||
import org.hl7.fhir.r4.model.Meta;
|
import org.hl7.fhir.r4.model.Meta;
|
||||||
import org.hl7.fhir.r4.model.SearchParameter;
|
import org.hl7.fhir.r4.model.SearchParameter;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -56,6 +58,7 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchPa
|
||||||
protected void markAffectedResources(SearchParameter theResource) {
|
protected void markAffectedResources(SearchParameter theResource) {
|
||||||
if (theResource != null) {
|
if (theResource != null) {
|
||||||
String expression = theResource.getExpression();
|
String expression = theResource.getExpression();
|
||||||
|
if (isNotBlank(expression)) {
|
||||||
final String resourceType = expression.substring(0, expression.indexOf('.'));
|
final String resourceType = expression.substring(0, expression.indexOf('.'));
|
||||||
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", expression);
|
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", expression);
|
||||||
|
|
||||||
|
@ -70,6 +73,7 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchPa
|
||||||
|
|
||||||
ourLog.info("Marked {} resources for reindexing", updatedCount);
|
ourLog.info("Marked {} resources for reindexing", updatedCount);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mySearchParamRegistry.forceRefresh();
|
mySearchParamRegistry.forceRefresh();
|
||||||
}
|
}
|
||||||
|
@ -125,15 +129,21 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchPa
|
||||||
throw new UnprocessableEntityException("SearchParameter.status is missing or invalid: " + theResource.getStatusElement().getValueAsString());
|
throw new UnprocessableEntityException("SearchParameter.status is missing or invalid: " + theResource.getStatusElement().getValueAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
String expression = theResource.getExpression();
|
|
||||||
if (isBlank(expression)) {
|
|
||||||
throw new UnprocessableEntityException("SearchParameter.expression is missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ElementUtil.isEmpty(theResource.getBase())) {
|
if (ElementUtil.isEmpty(theResource.getBase())) {
|
||||||
throw new UnprocessableEntityException("SearchParameter.base is missing");
|
throw new UnprocessableEntityException("SearchParameter.base is missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String expression = theResource.getExpression();
|
||||||
|
if (theResource.getType() == Enumerations.SearchParamType.COMPOSITE && isBlank(expression)) {
|
||||||
|
|
||||||
|
// this is ok
|
||||||
|
|
||||||
|
} else if (isBlank(expression)) {
|
||||||
|
|
||||||
|
throw new UnprocessableEntityException("SearchParameter.expression is missing");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
expression = expression.trim();
|
expression = expression.trim();
|
||||||
theResource.setExpression(expression);
|
theResource.setExpression(expression);
|
||||||
|
|
||||||
|
@ -164,6 +174,8 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchPa
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // if have expression
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,29 +133,33 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
||||||
if (nextValue.isEmpty()) {
|
if (nextValue.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getValue(), nextValue.getValue());
|
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getValue(), nextValue.getValue(), nextValue.getValueAsString());
|
||||||
} else if (nextObject instanceof Period) {
|
} else if (nextObject instanceof Period) {
|
||||||
Period nextValue = (Period) nextObject;
|
Period nextValue = (Period) nextObject;
|
||||||
if (nextValue.isEmpty()) {
|
if (nextValue.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getStart(), nextValue.getEnd());
|
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getStart(), nextValue.getEnd(), nextValue.getStartElement().getValueAsString());
|
||||||
} else if (nextObject instanceof Timing) {
|
} else if (nextObject instanceof Timing) {
|
||||||
Timing nextValue = (Timing) nextObject;
|
Timing nextValue = (Timing) nextObject;
|
||||||
if (nextValue.isEmpty()) {
|
if (nextValue.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
TreeSet<Date> dates = new TreeSet<Date>();
|
TreeSet<Date> dates = new TreeSet<>();
|
||||||
|
String firstValue = null;
|
||||||
for (DateTimeType nextEvent : nextValue.getEvent()) {
|
for (DateTimeType nextEvent : nextValue.getEvent()) {
|
||||||
if (nextEvent.getValue() != null) {
|
if (nextEvent.getValue() != null) {
|
||||||
dates.add(nextEvent.getValue());
|
dates.add(nextEvent.getValue());
|
||||||
|
if (firstValue == null) {
|
||||||
|
firstValue = nextEvent.getValueAsString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dates.isEmpty()) {
|
if (dates.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), dates.first(), dates.last());
|
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), dates.first(), dates.last(), firstValue);
|
||||||
} else if (nextObject instanceof StringType) {
|
} else if (nextObject instanceof StringType) {
|
||||||
// CarePlan.activitydate can be a string
|
// CarePlan.activitydate can be a string
|
||||||
continue;
|
continue;
|
||||||
|
@ -450,7 +454,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IBaseResource theResource) {
|
public Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IBaseResource theResource) {
|
||||||
HashSet<BaseResourceIndexedSearchParam> retVal = new HashSet<BaseResourceIndexedSearchParam>();
|
HashSet<BaseResourceIndexedSearchParam> retVal = new HashSet<>();
|
||||||
|
|
||||||
String useSystem = null;
|
String useSystem = null;
|
||||||
if (theResource instanceof CodeSystem) {
|
if (theResource instanceof CodeSystem) {
|
||||||
|
@ -477,16 +481,6 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
||||||
List<String> systems = new ArrayList<String>();
|
List<String> systems = new ArrayList<String>();
|
||||||
List<String> codes = new ArrayList<String>();
|
List<String> codes = new ArrayList<String>();
|
||||||
|
|
||||||
// String needContactPointSystem = null;
|
|
||||||
// if (nextPath.contains(".where(system='phone')")) {
|
|
||||||
// nextPath = nextPath.replace(".where(system='phone')", "");
|
|
||||||
// needContactPointSystem = "phone";
|
|
||||||
// }
|
|
||||||
// if (nextPath.contains(".where(system='email')")) {
|
|
||||||
// nextPath = nextPath.replace(".where(system='email')", "");
|
|
||||||
// needContactPointSystem = "email";
|
|
||||||
// }
|
|
||||||
|
|
||||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||||
|
|
||||||
if (nextObject == null) {
|
if (nextObject == null) {
|
||||||
|
|
|
@ -20,14 +20,19 @@ package ca.uhn.fhir.jpa.dao.r4;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.jpa.util.JpaConstants;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.hl7.fhir.r4.model.CodeType;
|
import org.hl7.fhir.r4.model.CodeType;
|
||||||
|
import org.hl7.fhir.r4.model.Extension;
|
||||||
import org.hl7.fhir.r4.model.SearchParameter;
|
import org.hl7.fhir.r4.model.SearchParameter;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
@ -42,6 +47,7 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
|
||||||
public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
|
public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamRegistryR4.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamRegistryR4.class);
|
||||||
|
public static final int MAX_MANAGED_PARAM_COUNT = 10000;
|
||||||
|
|
||||||
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams;
|
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams;
|
||||||
|
|
||||||
|
@ -62,33 +68,33 @@ public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() {
|
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() {
|
||||||
refreshCacheIfNeccesary();
|
refreshCacheIfNecessary();
|
||||||
return myActiveSearchParams;
|
return myActiveSearchParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
|
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
|
||||||
refreshCacheIfNeccesary();
|
refreshCacheIfNecessary();
|
||||||
return myActiveSearchParams.get(theResourceName);
|
return myActiveSearchParams.get(theResourceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
|
private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
|
||||||
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
|
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
|
||||||
if (retVal == null) {
|
if (retVal == null) {
|
||||||
retVal = new HashMap<String, RuntimeSearchParam>();
|
retVal = new HashMap<>();
|
||||||
searchParams.put(theResourceName, retVal);
|
searchParams.put(theResourceName, retVal);
|
||||||
}
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshCacheIfNeccesary() {
|
protected void refreshCacheIfNecessary() {
|
||||||
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
|
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
|
||||||
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
|
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
|
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
|
||||||
StopWatch sw = new StopWatch();
|
StopWatch sw = new StopWatch();
|
||||||
|
|
||||||
Map<String, Map<String, RuntimeSearchParam>> searchParams = new HashMap<String, Map<String, RuntimeSearchParam>>();
|
Map<String, Map<String, RuntimeSearchParam>> searchParams = new HashMap<>();
|
||||||
for (Entry<String, Map<String, RuntimeSearchParam>> nextBuiltInEntry : getBuiltInSearchParams().entrySet()) {
|
for (Entry<String, Map<String, RuntimeSearchParam>> nextBuiltInEntry : getBuiltInSearchParams().entrySet()) {
|
||||||
for (RuntimeSearchParam nextParam : nextBuiltInEntry.getValue().values()) {
|
for (RuntimeSearchParam nextParam : nextBuiltInEntry.getValue().values()) {
|
||||||
String nextResourceName = nextBuiltInEntry.getKey();
|
String nextResourceName = nextBuiltInEntry.getKey();
|
||||||
|
@ -97,15 +103,15 @@ public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchParameterMap params = new SearchParameterMap();
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
params.setLoadSynchronous(true);
|
params.setLoadSynchronousUpTo(MAX_MANAGED_PARAM_COUNT);
|
||||||
|
|
||||||
IBundleProvider allSearchParamsBp = mySpDao.search(params);
|
IBundleProvider allSearchParamsBp = mySpDao.search(params);
|
||||||
int size = allSearchParamsBp.size();
|
int size = allSearchParamsBp.size();
|
||||||
|
|
||||||
// Just in case..
|
// Just in case..
|
||||||
if (size > 10000) {
|
if (size >= MAX_MANAGED_PARAM_COUNT) {
|
||||||
ourLog.warn("Unable to support >10000 search params!");
|
ourLog.warn("Unable to support >" + MAX_MANAGED_PARAM_COUNT + " search params!");
|
||||||
size = 10000;
|
size = MAX_MANAGED_PARAM_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<IBaseResource> allSearchParams = allSearchParamsBp.getResources(0, size);
|
List<IBaseResource> allSearchParams = allSearchParamsBp.getResources(0, size);
|
||||||
|
@ -116,21 +122,22 @@ public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dotIdx = runtimeSp.getPath().indexOf('.');
|
for (CodeType nextBaseName : nextSp.getBase()) {
|
||||||
if (dotIdx == -1) {
|
String resourceType = nextBaseName.getValue();
|
||||||
ourLog.warn("Can not determine resource type of {}", runtimeSp.getPath());
|
if (isBlank(resourceType)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String resourceType = runtimeSp.getPath().substring(0, dotIdx);
|
|
||||||
|
|
||||||
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, resourceType);
|
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, resourceType);
|
||||||
String name = runtimeSp.getName();
|
String name = runtimeSp.getName();
|
||||||
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
|
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
|
||||||
searchParamMap.put(name, runtimeSp);
|
searchParamMap.put(name, runtimeSp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Map<String, RuntimeSearchParam>> activeSearchParams = new HashMap<String, Map<String, RuntimeSearchParam>>();
|
Map<String, Map<String, RuntimeSearchParam>> activeSearchParams = new HashMap<>();
|
||||||
for (Entry<String, Map<String, RuntimeSearchParam>> nextEntry : searchParams.entrySet()) {
|
for (Entry<String, Map<String, RuntimeSearchParam>> nextEntry : searchParams.entrySet()) {
|
||||||
for (RuntimeSearchParam nextSp : nextEntry.getValue().values()) {
|
for (RuntimeSearchParam nextSp : nextEntry.getValue().values()) {
|
||||||
String nextName = nextSp.getName();
|
String nextName = nextSp.getName();
|
||||||
|
@ -155,6 +162,8 @@ public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
|
||||||
|
|
||||||
myActiveSearchParams = activeSearchParams;
|
myActiveSearchParams = activeSearchParams;
|
||||||
|
|
||||||
|
super.populateActiveSearchParams(activeSearchParams);
|
||||||
|
|
||||||
myLastRefresh = System.currentTimeMillis();
|
myLastRefresh = System.currentTimeMillis();
|
||||||
ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis());
|
ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis());
|
||||||
}
|
}
|
||||||
|
@ -215,17 +224,36 @@ public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
|
||||||
Set<String> targets = toStrings(theNextSp.getTarget());
|
Set<String> targets = toStrings(theNextSp.getTarget());
|
||||||
|
|
||||||
if (isBlank(name) || isBlank(path) || paramType == null) {
|
if (isBlank(name) || isBlank(path) || paramType == null) {
|
||||||
|
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IIdType id = theNextSp.getIdElement();
|
IIdType id = theNextSp.getIdElement();
|
||||||
String uri = "";
|
String uri = "";
|
||||||
RuntimeSearchParam retVal = new RuntimeSearchParam(id, uri, name, description, path, paramType, null, providesMembershipInCompartments, targets, status);
|
boolean unique = false;
|
||||||
|
|
||||||
|
List<Extension> uniqueExts = theNextSp.getExtensionsByUrl(JpaConstants.EXT_SP_UNIQUE);
|
||||||
|
if (uniqueExts.size() > 0) {
|
||||||
|
IPrimitiveType<?> uniqueExtsValuePrimitive = uniqueExts.get(0).getValueAsPrimitive();
|
||||||
|
if (uniqueExtsValuePrimitive != null) {
|
||||||
|
if ("true".equalsIgnoreCase(uniqueExtsValuePrimitive.getValueAsString())) {
|
||||||
|
unique = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<JpaRuntimeSearchParam.Component> components = new ArrayList<>();
|
||||||
|
for (org.hl7.fhir.r4.model.SearchParameter.SearchParameterComponentComponent next : theNextSp.getComponent()) {
|
||||||
|
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), next.getDefinition()));
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeSearchParam retVal = new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> toStrings(List<CodeType> theTarget) {
|
private Set<String> toStrings(List<CodeType> theTarget) {
|
||||||
HashSet<String> retVal = new HashSet<String>();
|
HashSet<String> retVal = new HashSet<>();
|
||||||
for (CodeType next : theTarget) {
|
for (CodeType next : theTarget) {
|
||||||
if (isNotBlank(next.getValue())) {
|
if (isNotBlank(next.getValue())) {
|
||||||
retVal.add(next.getValue());
|
retVal.add(next.getValue());
|
||||||
|
|
|
@ -20,14 +20,14 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.Serializable;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import javax.persistence.*;
|
|
||||||
|
|
||||||
import org.hibernate.search.annotations.ContainedIn;
|
import org.hibernate.search.annotations.ContainedIn;
|
||||||
import org.hibernate.search.annotations.Field;
|
import org.hibernate.search.annotations.Field;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
||||||
|
|
||||||
|
@ -67,10 +67,19 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
||||||
return myParamName;
|
return myParamName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setParamName(String theName) {
|
||||||
|
myParamName = theName;
|
||||||
|
}
|
||||||
|
|
||||||
public ResourceTable getResource() {
|
public ResourceTable getResource() {
|
||||||
return myResource;
|
return myResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setResource(ResourceTable theResource) {
|
||||||
|
myResource = theResource;
|
||||||
|
myResourceType = theResource.getResourceType();
|
||||||
|
}
|
||||||
|
|
||||||
public Long getResourcePid() {
|
public Long getResourcePid() {
|
||||||
return myResourcePid;
|
return myResourcePid;
|
||||||
}
|
}
|
||||||
|
@ -83,6 +92,10 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
||||||
return myUpdated;
|
return myUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUpdated(Date theUpdated) {
|
||||||
|
myUpdated = theUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isMissing() {
|
public boolean isMissing() {
|
||||||
return Boolean.TRUE.equals(myMissing);
|
return Boolean.TRUE.equals(myMissing);
|
||||||
}
|
}
|
||||||
|
@ -91,17 +104,5 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
||||||
myMissing = theMissing;
|
myMissing = theMissing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamName(String theName) {
|
public abstract IQueryParameterType toQueryParameterType();
|
||||||
myParamName = theName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setResource(ResourceTable theResource) {
|
|
||||||
myResource = theResource;
|
|
||||||
myResourceType = theResource.getResourceType();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUpdated(Date theUpdated) {
|
|
||||||
myUpdated = theUpdated;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
package ca.uhn.fhir.jpa.entity;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.builder.*;
|
||||||
|
import org.hl7.fhir.r4.model.Resource;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
@Table(name = "HFJ_IDX_CMP_STRING_UNIQ", indexes = {
|
||||||
|
@Index(name = ResourceIndexedCompositeStringUnique.IDX_IDXCMPSTRUNIQ_STRING, columnList = "IDX_STRING", unique = true)
|
||||||
|
})
|
||||||
|
public class ResourceIndexedCompositeStringUnique implements Comparable<ResourceIndexedCompositeStringUnique> {
|
||||||
|
|
||||||
|
public static final int MAX_STRING_LENGTH = 800;
|
||||||
|
public static final String IDX_IDXCMPSTRUNIQ_STRING = "IDX_IDXCMPSTRUNIQ_STRING";
|
||||||
|
|
||||||
|
@SequenceGenerator(name = "SEQ_IDXCMPSTRUNIQ_ID", sequenceName = "SEQ_IDXCMPSTRUNIQ_ID")
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_IDXCMPSTRUNIQ_ID")
|
||||||
|
@Id
|
||||||
|
@Column(name = "PID")
|
||||||
|
private Long myId;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID")
|
||||||
|
private ResourceTable myResource;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||||
|
.append("resourceId", myResourceId)
|
||||||
|
.append("indexString", myIndexString)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Column(name="RES_ID", insertable = false, updatable = false)
|
||||||
|
private Long myResourceId;
|
||||||
|
|
||||||
|
@Column(name = "IDX_STRING", nullable = false, length = MAX_STRING_LENGTH)
|
||||||
|
private String myIndexString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public ResourceIndexedCompositeStringUnique() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public ResourceIndexedCompositeStringUnique(ResourceTable theResource, String theIndexString) {
|
||||||
|
setResource(theResource);
|
||||||
|
setIndexString(theIndexString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(ResourceIndexedCompositeStringUnique theO) {
|
||||||
|
CompareToBuilder b = new CompareToBuilder();
|
||||||
|
b.append(myResource, theO.getResource());
|
||||||
|
b.append(myIndexString, theO.getIndexString());
|
||||||
|
return b.toComparison();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object theO) {
|
||||||
|
if (this == theO) return true;
|
||||||
|
|
||||||
|
if (theO == null || getClass() != theO.getClass()) return false;
|
||||||
|
|
||||||
|
ResourceIndexedCompositeStringUnique that = (ResourceIndexedCompositeStringUnique) theO;
|
||||||
|
|
||||||
|
return new EqualsBuilder()
|
||||||
|
.append(getResource(), that.getResource())
|
||||||
|
.append(myIndexString, that.myIndexString)
|
||||||
|
.isEquals();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIndexString() {
|
||||||
|
return myIndexString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndexString(String theIndexString) {
|
||||||
|
myIndexString = theIndexString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceTable getResource() {
|
||||||
|
return myResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResource(ResourceTable theResource) {
|
||||||
|
myResource = theResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return new HashCodeBuilder(17, 37)
|
||||||
|
.append(getResource())
|
||||||
|
.append(myIndexString)
|
||||||
|
.toHashCode();
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,8 @@ import javax.persistence.Index;
|
||||||
import javax.persistence.SequenceGenerator;
|
import javax.persistence.SequenceGenerator;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
import com.sun.prism.image.Coords;
|
||||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
|
@ -99,6 +101,11 @@ public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchP
|
||||||
return myId;
|
return myId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IQueryParameterType toQueryParameterType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public double getLatitude() {
|
public double getLatitude() {
|
||||||
return myLatitude;
|
return myLatitude;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,27 +20,20 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.Date;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||||
import javax.persistence.Column;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import javax.persistence.Embeddable;
|
import ca.uhn.fhir.rest.param.DateParam;
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.GeneratedValue;
|
|
||||||
import javax.persistence.GenerationType;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import javax.persistence.Index;
|
|
||||||
import javax.persistence.SequenceGenerator;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
import javax.persistence.Temporal;
|
|
||||||
import javax.persistence.TemporalType;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
import org.hibernate.search.annotations.Field;
|
import org.hibernate.search.annotations.Field;
|
||||||
|
import org.hl7.fhir.r4.model.DateTimeType;
|
||||||
|
import org.hl7.fhir.r4.model.InstantType;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import javax.persistence.*;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
@Embeddable
|
@Embeddable
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -53,21 +46,22 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Id
|
@Transient
|
||||||
@SequenceGenerator(name = "SEQ_SPIDX_DATE", sequenceName = "SEQ_SPIDX_DATE")
|
private transient String myOriginalValue;
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_DATE")
|
|
||||||
@Column(name = "SP_ID")
|
|
||||||
private Long myId;
|
|
||||||
|
|
||||||
@Column(name = "SP_VALUE_HIGH", nullable = true)
|
@Column(name = "SP_VALUE_HIGH", nullable = true)
|
||||||
@Temporal(TemporalType.TIMESTAMP)
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
@Field
|
@Field
|
||||||
public Date myValueHigh;
|
public Date myValueHigh;
|
||||||
|
|
||||||
@Column(name = "SP_VALUE_LOW", nullable = true)
|
@Column(name = "SP_VALUE_LOW", nullable = true)
|
||||||
@Temporal(TemporalType.TIMESTAMP)
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
@Field
|
@Field
|
||||||
public Date myValueLow;
|
public Date myValueLow;
|
||||||
|
@Id
|
||||||
|
@SequenceGenerator(name = "SEQ_SPIDX_DATE", sequenceName = "SEQ_SPIDX_DATE")
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_DATE")
|
||||||
|
@Column(name = "SP_ID")
|
||||||
|
private Long myId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -78,10 +72,11 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public ResourceIndexedSearchParamDate(String theName, Date theLow, Date theHigh) {
|
public ResourceIndexedSearchParamDate(String theName, Date theLow, Date theHigh, String theOriginalValue) {
|
||||||
setParamName(theName);
|
setParamName(theName);
|
||||||
setValueLow(theLow);
|
setValueLow(theLow);
|
||||||
setValueHigh(theHigh);
|
setValueHigh(theHigh);
|
||||||
|
myOriginalValue = theOriginalValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -113,10 +108,18 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
||||||
return myValueHigh;
|
return myValueHigh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setValueHigh(Date theValueHigh) {
|
||||||
|
myValueHigh = theValueHigh;
|
||||||
|
}
|
||||||
|
|
||||||
public Date getValueLow() {
|
public Date getValueLow() {
|
||||||
return myValueLow;
|
return myValueLow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setValueLow(Date theValueLow) {
|
||||||
|
myValueLow = theValueLow;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
HashCodeBuilder b = new HashCodeBuilder();
|
HashCodeBuilder b = new HashCodeBuilder();
|
||||||
|
@ -127,12 +130,13 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
||||||
return b.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValueHigh(Date theValueHigh) {
|
@Override
|
||||||
myValueHigh = theValueHigh;
|
public IQueryParameterType toQueryParameterType() {
|
||||||
|
DateTimeType value = new DateTimeType(myOriginalValue);
|
||||||
|
if (value.getPrecision().ordinal() > TemporalPrecisionEnum.DAY.ordinal()) {
|
||||||
|
value.setTimeZoneZulu(true);
|
||||||
}
|
}
|
||||||
|
return new DateParam(value.getValueAsString());
|
||||||
public void setValueLow(Date theValueLow) {
|
|
||||||
myValueLow = theValueLow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,18 +20,9 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import ca.uhn.fhir.jpa.util.BigDecimalNumericFieldBridge;
|
||||||
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import javax.persistence.Column;
|
import ca.uhn.fhir.rest.param.NumberParam;
|
||||||
import javax.persistence.Embeddable;
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.GeneratedValue;
|
|
||||||
import javax.persistence.GenerationType;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import javax.persistence.Index;
|
|
||||||
import javax.persistence.SequenceGenerator;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
|
@ -40,7 +31,8 @@ import org.hibernate.search.annotations.Field;
|
||||||
import org.hibernate.search.annotations.FieldBridge;
|
import org.hibernate.search.annotations.FieldBridge;
|
||||||
import org.hibernate.search.annotations.NumericField;
|
import org.hibernate.search.annotations.NumericField;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.util.BigDecimalNumericFieldBridge;
|
import javax.persistence.*;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@Embeddable
|
@Embeddable
|
||||||
|
@ -54,18 +46,16 @@ import ca.uhn.fhir.jpa.util.BigDecimalNumericFieldBridge;
|
||||||
public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchParam {
|
public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchParam {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Id
|
|
||||||
@SequenceGenerator(name = "SEQ_SPIDX_NUMBER", sequenceName = "SEQ_SPIDX_NUMBER")
|
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_NUMBER")
|
|
||||||
@Column(name = "SP_ID")
|
|
||||||
private Long myId;
|
|
||||||
|
|
||||||
@Column(name = "SP_VALUE", nullable = true)
|
@Column(name = "SP_VALUE", nullable = true)
|
||||||
@Field
|
@Field
|
||||||
@NumericField
|
@NumericField
|
||||||
@FieldBridge(impl = BigDecimalNumericFieldBridge.class)
|
@FieldBridge(impl = BigDecimalNumericFieldBridge.class)
|
||||||
public BigDecimal myValue;
|
public BigDecimal myValue;
|
||||||
|
@Id
|
||||||
|
@SequenceGenerator(name = "SEQ_SPIDX_NUMBER", sequenceName = "SEQ_SPIDX_NUMBER")
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_NUMBER")
|
||||||
|
@Column(name = "SP_ID")
|
||||||
|
private Long myId;
|
||||||
|
|
||||||
public ResourceIndexedSearchParamNumber() {
|
public ResourceIndexedSearchParamNumber() {
|
||||||
}
|
}
|
||||||
|
@ -103,6 +93,10 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
|
||||||
return myValue;
|
return myValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setValue(BigDecimal theValue) {
|
||||||
|
myValue = theValue;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
HashCodeBuilder b = new HashCodeBuilder();
|
HashCodeBuilder b = new HashCodeBuilder();
|
||||||
|
@ -112,8 +106,9 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
|
||||||
return b.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValue(BigDecimal theValue) {
|
@Override
|
||||||
myValue = theValue;
|
public IQueryParameterType toQueryParameterType() {
|
||||||
|
return new NumberParam(myValue.toPlainString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,18 +20,9 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import ca.uhn.fhir.jpa.util.BigDecimalNumericFieldBridge;
|
||||||
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import javax.persistence.Column;
|
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||||
import javax.persistence.Embeddable;
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.GeneratedValue;
|
|
||||||
import javax.persistence.GenerationType;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import javax.persistence.Index;
|
|
||||||
import javax.persistence.SequenceGenerator;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
|
@ -40,7 +31,8 @@ import org.hibernate.search.annotations.Field;
|
||||||
import org.hibernate.search.annotations.FieldBridge;
|
import org.hibernate.search.annotations.FieldBridge;
|
||||||
import org.hibernate.search.annotations.NumericField;
|
import org.hibernate.search.annotations.NumericField;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.util.BigDecimalNumericFieldBridge;
|
import javax.persistence.*;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@Embeddable
|
@Embeddable
|
||||||
|
@ -56,26 +48,22 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
private static final int MAX_LENGTH = 200;
|
private static final int MAX_LENGTH = 200;
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Id
|
|
||||||
@SequenceGenerator(name = "SEQ_SPIDX_QUANTITY", sequenceName = "SEQ_SPIDX_QUANTITY")
|
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_QUANTITY")
|
|
||||||
@Column(name = "SP_ID")
|
|
||||||
private Long myId;
|
|
||||||
|
|
||||||
@Column(name = "SP_SYSTEM", nullable = true, length = MAX_LENGTH)
|
@Column(name = "SP_SYSTEM", nullable = true, length = MAX_LENGTH)
|
||||||
@Field
|
@Field
|
||||||
public String mySystem;
|
public String mySystem;
|
||||||
|
|
||||||
@Column(name = "SP_UNITS", nullable = true, length = MAX_LENGTH)
|
@Column(name = "SP_UNITS", nullable = true, length = MAX_LENGTH)
|
||||||
@Field
|
@Field
|
||||||
public String myUnits;
|
public String myUnits;
|
||||||
|
|
||||||
@Column(name = "SP_VALUE", nullable = true)
|
@Column(name = "SP_VALUE", nullable = true)
|
||||||
@Field
|
@Field
|
||||||
@NumericField
|
@NumericField
|
||||||
@FieldBridge(impl = BigDecimalNumericFieldBridge.class)
|
@FieldBridge(impl = BigDecimalNumericFieldBridge.class)
|
||||||
public BigDecimal myValue;
|
public BigDecimal myValue;
|
||||||
|
@Id
|
||||||
|
@SequenceGenerator(name = "SEQ_SPIDX_QUANTITY", sequenceName = "SEQ_SPIDX_QUANTITY")
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_QUANTITY")
|
||||||
|
@Column(name = "SP_ID")
|
||||||
|
private Long myId;
|
||||||
|
|
||||||
public ResourceIndexedSearchParamQuantity() {
|
public ResourceIndexedSearchParamQuantity() {
|
||||||
// nothing
|
// nothing
|
||||||
|
@ -118,14 +106,26 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
return mySystem;
|
return mySystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSystem(String theSystem) {
|
||||||
|
mySystem = theSystem;
|
||||||
|
}
|
||||||
|
|
||||||
public String getUnits() {
|
public String getUnits() {
|
||||||
return myUnits;
|
return myUnits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUnits(String theUnits) {
|
||||||
|
myUnits = theUnits;
|
||||||
|
}
|
||||||
|
|
||||||
public BigDecimal getValue() {
|
public BigDecimal getValue() {
|
||||||
return myValue;
|
return myValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setValue(BigDecimal theValue) {
|
||||||
|
myValue = theValue;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
HashCodeBuilder b = new HashCodeBuilder();
|
HashCodeBuilder b = new HashCodeBuilder();
|
||||||
|
@ -137,16 +137,9 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
return b.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSystem(String theSystem) {
|
@Override
|
||||||
mySystem = theSystem;
|
public IQueryParameterType toQueryParameterType() {
|
||||||
}
|
return new QuantityParam(null, getValue(), getSystem(), getUnits());
|
||||||
|
|
||||||
public void setUnits(String theUnits) {
|
|
||||||
myUnits = theUnits;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(BigDecimal theValue) {
|
|
||||||
myValue = theValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,31 +20,17 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import javax.persistence.Column;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import javax.persistence.Embeddable;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.ForeignKey;
|
|
||||||
import javax.persistence.GeneratedValue;
|
|
||||||
import javax.persistence.GenerationType;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import javax.persistence.Index;
|
|
||||||
import javax.persistence.JoinColumn;
|
|
||||||
import javax.persistence.ManyToOne;
|
|
||||||
import javax.persistence.SequenceGenerator;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
import org.hibernate.search.annotations.Analyze;
|
import org.hibernate.search.annotations.*;
|
||||||
import org.hibernate.search.annotations.Analyzer;
|
|
||||||
import org.hibernate.search.annotations.ContainedIn;
|
import javax.persistence.*;
|
||||||
import org.hibernate.search.annotations.Field;
|
import javax.persistence.Index;
|
||||||
import org.hibernate.search.annotations.Fields;
|
|
||||||
import org.hibernate.search.annotations.Indexed;
|
|
||||||
import org.hibernate.search.annotations.Store;
|
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@Embeddable
|
@Embeddable
|
||||||
|
@ -169,10 +155,24 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
||||||
return myValueExact;
|
return myValueExact;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setValueExact(String theValueExact) {
|
||||||
|
if (StringUtils.defaultString(theValueExact).length() > MAX_LENGTH) {
|
||||||
|
throw new IllegalArgumentException("Value is too long: " + theValueExact.length());
|
||||||
|
}
|
||||||
|
myValueExact = theValueExact;
|
||||||
|
}
|
||||||
|
|
||||||
public String getValueNormalized() {
|
public String getValueNormalized() {
|
||||||
return myValueNormalized;
|
return myValueNormalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setValueNormalized(String theValueNormalized) {
|
||||||
|
if (StringUtils.defaultString(theValueNormalized).length() > MAX_LENGTH) {
|
||||||
|
throw new IllegalArgumentException("Value is too long: " + theValueNormalized.length());
|
||||||
|
}
|
||||||
|
myValueNormalized = theValueNormalized;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
HashCodeBuilder b = new HashCodeBuilder();
|
HashCodeBuilder b = new HashCodeBuilder();
|
||||||
|
@ -182,18 +182,9 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
||||||
return b.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setValueExact(String theValueExact) {
|
@Override
|
||||||
if (StringUtils.defaultString(theValueExact).length() > MAX_LENGTH) {
|
public IQueryParameterType toQueryParameterType() {
|
||||||
throw new IllegalArgumentException("Value is too long: " + theValueExact.length());
|
return new StringParam(getValueExact());
|
||||||
}
|
|
||||||
myValueExact = theValueExact;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValueNormalized(String theValueNormalized) {
|
|
||||||
if (StringUtils.defaultString(theValueNormalized).length() > MAX_LENGTH) {
|
|
||||||
throw new IllegalArgumentException("Value is too long: " + theValueNormalized.length());
|
|
||||||
}
|
|
||||||
myValueNormalized = theValueNormalized;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,16 +20,8 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import javax.persistence.Column;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import javax.persistence.Embeddable;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.GeneratedValue;
|
|
||||||
import javax.persistence.GenerationType;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import javax.persistence.Index;
|
|
||||||
import javax.persistence.SequenceGenerator;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
|
@ -37,6 +29,8 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
import org.hibernate.search.annotations.Field;
|
import org.hibernate.search.annotations.Field;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@Embeddable
|
@Embeddable
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -52,21 +46,18 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
public static final int MAX_LENGTH = 200;
|
public static final int MAX_LENGTH = 200;
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
@Field()
|
||||||
|
@Column(name = "SP_SYSTEM", nullable = true, length = MAX_LENGTH)
|
||||||
|
public String mySystem;
|
||||||
|
@Field()
|
||||||
|
@Column(name = "SP_VALUE", nullable = true, length = MAX_LENGTH)
|
||||||
|
public String myValue;
|
||||||
@Id
|
@Id
|
||||||
@SequenceGenerator(name = "SEQ_SPIDX_TOKEN", sequenceName = "SEQ_SPIDX_TOKEN")
|
@SequenceGenerator(name = "SEQ_SPIDX_TOKEN", sequenceName = "SEQ_SPIDX_TOKEN")
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_TOKEN")
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_TOKEN")
|
||||||
@Column(name = "SP_ID")
|
@Column(name = "SP_ID")
|
||||||
private Long myId;
|
private Long myId;
|
||||||
|
|
||||||
@Field()
|
|
||||||
@Column(name = "SP_SYSTEM", nullable = true, length = MAX_LENGTH)
|
|
||||||
public String mySystem;
|
|
||||||
|
|
||||||
@Field()
|
|
||||||
@Column(name = "SP_VALUE", nullable = true, length = MAX_LENGTH)
|
|
||||||
public String myValue;
|
|
||||||
|
|
||||||
public ResourceIndexedSearchParamToken() {
|
public ResourceIndexedSearchParamToken() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,10 +96,18 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
return mySystem;
|
return mySystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSystem(String theSystem) {
|
||||||
|
mySystem = StringUtils.defaultIfBlank(theSystem, null);
|
||||||
|
}
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return myValue;
|
return myValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setValue(String theValue) {
|
||||||
|
myValue = StringUtils.defaultIfBlank(theValue, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
HashCodeBuilder b = new HashCodeBuilder();
|
HashCodeBuilder b = new HashCodeBuilder();
|
||||||
|
@ -119,12 +118,9 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
return b.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSystem(String theSystem) {
|
@Override
|
||||||
mySystem = StringUtils.defaultIfBlank(theSystem, null);
|
public IQueryParameterType toQueryParameterType() {
|
||||||
}
|
return new TokenParam(getSystem(), getValue());
|
||||||
|
|
||||||
public void setValue(String theValue) {
|
|
||||||
myValue = StringUtils.defaultIfBlank(theValue, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,22 +20,16 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import javax.persistence.Column;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import javax.persistence.Embeddable;
|
import ca.uhn.fhir.rest.param.UriParam;
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.GeneratedValue;
|
|
||||||
import javax.persistence.GenerationType;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import javax.persistence.Index;
|
|
||||||
import javax.persistence.SequenceGenerator;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
import org.hibernate.search.annotations.Field;
|
import org.hibernate.search.annotations.Field;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@Embeddable
|
@Embeddable
|
||||||
@Entity
|
@Entity
|
||||||
|
@ -54,17 +48,15 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
||||||
public static final int MAX_LENGTH = 255;
|
public static final int MAX_LENGTH = 255;
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
@Column(name = "SP_URI", nullable = true, length = MAX_LENGTH)
|
||||||
|
@Field()
|
||||||
|
public String myUri;
|
||||||
@Id
|
@Id
|
||||||
@SequenceGenerator(name = "SEQ_SPIDX_URI", sequenceName = "SEQ_SPIDX_URI")
|
@SequenceGenerator(name = "SEQ_SPIDX_URI", sequenceName = "SEQ_SPIDX_URI")
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_URI")
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_URI")
|
||||||
@Column(name = "SP_ID")
|
@Column(name = "SP_ID")
|
||||||
private Long myId;
|
private Long myId;
|
||||||
|
|
||||||
@Column(name = "SP_URI", nullable = true, length = MAX_LENGTH)
|
|
||||||
@Field()
|
|
||||||
public String myUri;
|
|
||||||
|
|
||||||
public ResourceIndexedSearchParamUri() {
|
public ResourceIndexedSearchParamUri() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +93,10 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
||||||
return myUri;
|
return myUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setUri(String theUri) {
|
||||||
|
myUri = StringUtils.defaultIfBlank(theUri, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
HashCodeBuilder b = new HashCodeBuilder();
|
HashCodeBuilder b = new HashCodeBuilder();
|
||||||
|
@ -110,8 +106,9 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
||||||
return b.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUri(String theUri) {
|
@Override
|
||||||
myUri = StringUtils.defaultIfBlank(theUri, null);
|
public IQueryParameterType toQueryParameterType() {
|
||||||
|
return new UriParam(getUri());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,27 +19,11 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.persistence.CascadeType;
|
|
||||||
import javax.persistence.Column;
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.FetchType;
|
|
||||||
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.Transient;
|
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.IndexNonDeletedInterceptor;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
import org.apache.lucene.analysis.core.LowerCaseFilterFactory;
|
import org.apache.lucene.analysis.core.LowerCaseFilterFactory;
|
||||||
|
@ -52,23 +36,18 @@ import org.apache.lucene.analysis.phonetic.PhoneticFilterFactory;
|
||||||
import org.apache.lucene.analysis.snowball.SnowballPorterFilterFactory;
|
import org.apache.lucene.analysis.snowball.SnowballPorterFilterFactory;
|
||||||
import org.apache.lucene.analysis.standard.StandardFilterFactory;
|
import org.apache.lucene.analysis.standard.StandardFilterFactory;
|
||||||
import org.apache.lucene.analysis.standard.StandardTokenizerFactory;
|
import org.apache.lucene.analysis.standard.StandardTokenizerFactory;
|
||||||
import org.hibernate.search.annotations.Analyze;
|
import org.hibernate.search.annotations.*;
|
||||||
import org.hibernate.search.annotations.Analyzer;
|
|
||||||
import org.hibernate.search.annotations.AnalyzerDef;
|
|
||||||
import org.hibernate.search.annotations.AnalyzerDefs;
|
|
||||||
import org.hibernate.search.annotations.Field;
|
|
||||||
import org.hibernate.search.annotations.Fields;
|
|
||||||
import org.hibernate.search.annotations.Indexed;
|
|
||||||
import org.hibernate.search.annotations.IndexedEmbedded;
|
|
||||||
import org.hibernate.search.annotations.Parameter;
|
import org.hibernate.search.annotations.Parameter;
|
||||||
import org.hibernate.search.annotations.Store;
|
|
||||||
import org.hibernate.search.annotations.TokenFilterDef;
|
|
||||||
import org.hibernate.search.annotations.TokenizerDef;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.search.IndexNonDeletedInterceptor;
|
import javax.persistence.*;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import javax.persistence.Index;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import java.io.Serializable;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@Indexed(interceptor = IndexNonDeletedInterceptor.class)
|
@Indexed(interceptor = IndexNonDeletedInterceptor.class)
|
||||||
|
@ -129,17 +108,14 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
)
|
)
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
public class ResourceTable extends BaseHasResource implements Serializable {
|
public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
|
static final int RESTYPE_LEN = 30;
|
||||||
private static final int MAX_LANGUAGE_LENGTH = 20;
|
private static final int MAX_LANGUAGE_LENGTH = 20;
|
||||||
private static final int MAX_PROFILE_LENGTH = 200;
|
private static final int MAX_PROFILE_LENGTH = 200;
|
||||||
|
|
||||||
static final int RESTYPE_LEN = 30;
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the narrative text only - Used for Fulltext searching but not directly stored in the DB
|
* Holds the narrative text only - Used for Fulltext searching but not directly stored in the DB
|
||||||
*/
|
*/
|
||||||
//@formatter:off
|
|
||||||
@Transient()
|
@Transient()
|
||||||
@Fields({
|
@Fields({
|
||||||
@Field(name = "myContentText", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "standardAnalyzer")),
|
@Field(name = "myContentText", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "standardAnalyzer")),
|
||||||
|
@ -147,7 +123,6 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
@Field(name = "myContentTextNGram", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompleteNGramAnalyzer")),
|
@Field(name = "myContentTextNGram", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompleteNGramAnalyzer")),
|
||||||
@Field(name = "myContentTextPhonetic", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompletePhoneticAnalyzer"))
|
@Field(name = "myContentTextPhonetic", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompletePhoneticAnalyzer"))
|
||||||
})
|
})
|
||||||
//@formatter:on
|
|
||||||
private String myContentText;
|
private String myContentText;
|
||||||
|
|
||||||
@Column(name = "HASH_SHA256", length = 64, nullable = true)
|
@Column(name = "HASH_SHA256", length = 64, nullable = true)
|
||||||
|
@ -227,24 +202,22 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
|
|
||||||
@Column(name = "RES_PROFILE", length = MAX_PROFILE_LENGTH, nullable = true)
|
@Column(name = "RES_PROFILE", length = MAX_PROFILE_LENGTH, nullable = true)
|
||||||
private String myProfile;
|
private String myProfile;
|
||||||
|
@OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
|
||||||
|
private Collection<ResourceIndexedCompositeStringUnique> myParamsCompositeStringUnique;
|
||||||
|
@Column(name = "SP_CMPSTR_UNIQ_PRESENT")
|
||||||
|
private boolean myParamsCompositeStringUniquePresent;
|
||||||
@OneToMany(mappedBy = "mySourceResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
|
@OneToMany(mappedBy = "mySourceResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
|
||||||
@IndexedEmbedded()
|
@IndexedEmbedded()
|
||||||
private Collection<ResourceLink> myResourceLinks;
|
private Collection<ResourceLink> myResourceLinks;
|
||||||
|
|
||||||
@Column(name = "RES_TYPE", length = RESTYPE_LEN)
|
@Column(name = "RES_TYPE", length = RESTYPE_LEN)
|
||||||
@Field
|
@Field
|
||||||
private String myResourceType;
|
private String myResourceType;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
|
@OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
|
||||||
private Collection<SearchParamPresent> mySearchParamPresents;
|
private Collection<SearchParamPresent> mySearchParamPresents;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
|
@OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
|
||||||
private Set<ResourceTag> myTags;
|
private Set<ResourceTag> myTags;
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
private transient boolean myUnchangedInCurrentOperation;
|
private transient boolean myUnchangedInCurrentOperation;
|
||||||
|
|
||||||
@Column(name = "RES_VER")
|
@Column(name = "RES_VER")
|
||||||
private long myVersion;
|
private long myVersion;
|
||||||
|
|
||||||
|
@ -264,11 +237,19 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
return myHashSha256;
|
return myHashSha256;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setHashSha256(String theHashSha256) {
|
||||||
|
myHashSha256 = theHashSha256;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return myId;
|
return myId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setId(Long theId) {
|
||||||
|
myId = theId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdDt getIdDt() {
|
public IdDt getIdDt() {
|
||||||
if (getForcedId() == null) {
|
if (getForcedId() == null) {
|
||||||
|
@ -283,157 +264,14 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
return myIndexStatus;
|
return myIndexStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLanguage() {
|
|
||||||
return myLanguage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ResourceIndexedSearchParamCoords> getParamsCoords() {
|
|
||||||
if (myParamsCoords == null) {
|
|
||||||
myParamsCoords = new ArrayList<ResourceIndexedSearchParamCoords>();
|
|
||||||
}
|
|
||||||
return myParamsCoords;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ResourceIndexedSearchParamDate> getParamsDate() {
|
|
||||||
if (myParamsDate == null) {
|
|
||||||
myParamsDate = new ArrayList<ResourceIndexedSearchParamDate>();
|
|
||||||
}
|
|
||||||
return myParamsDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ResourceIndexedSearchParamNumber> getParamsNumber() {
|
|
||||||
if (myParamsNumber == null) {
|
|
||||||
myParamsNumber = new ArrayList<ResourceIndexedSearchParamNumber>();
|
|
||||||
}
|
|
||||||
return myParamsNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ResourceIndexedSearchParamQuantity> getParamsQuantity() {
|
|
||||||
if (myParamsQuantity == null) {
|
|
||||||
myParamsQuantity = new ArrayList<ResourceIndexedSearchParamQuantity>();
|
|
||||||
}
|
|
||||||
return myParamsQuantity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ResourceIndexedSearchParamString> getParamsString() {
|
|
||||||
if (myParamsString == null) {
|
|
||||||
myParamsString = new ArrayList<ResourceIndexedSearchParamString>();
|
|
||||||
}
|
|
||||||
return myParamsString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ResourceIndexedSearchParamToken> getParamsToken() {
|
|
||||||
if (myParamsToken == null) {
|
|
||||||
myParamsToken = new ArrayList<ResourceIndexedSearchParamToken>();
|
|
||||||
}
|
|
||||||
return myParamsToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ResourceIndexedSearchParamUri> getParamsUri() {
|
|
||||||
if (myParamsUri == null) {
|
|
||||||
myParamsUri = new ArrayList<ResourceIndexedSearchParamUri>();
|
|
||||||
}
|
|
||||||
return myParamsUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getProfile() {
|
|
||||||
return myProfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ResourceLink> getResourceLinks() {
|
|
||||||
if (myResourceLinks == null) {
|
|
||||||
myResourceLinks = new ArrayList<ResourceLink>();
|
|
||||||
}
|
|
||||||
return myResourceLinks;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getResourceType() {
|
|
||||||
return myResourceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<ResourceTag> getTags() {
|
|
||||||
if (myTags == null) {
|
|
||||||
myTags = new HashSet<ResourceTag>();
|
|
||||||
}
|
|
||||||
return myTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getVersion() {
|
|
||||||
return myVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasTag(System theSystem, String theTerm) {
|
|
||||||
for (ResourceTag next : getTags()) {
|
|
||||||
if (next.getTag().getSystem().equals(theSystem) && next.getTag().getCode().equals(theTerm)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isHasLinks() {
|
|
||||||
return myHasLinks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isParamsCoordsPopulated() {
|
|
||||||
return myParamsCoordsPopulated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isParamsDatePopulated() {
|
|
||||||
return myParamsDatePopulated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isParamsNumberPopulated() {
|
|
||||||
return myParamsNumberPopulated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isParamsQuantityPopulated() {
|
|
||||||
return myParamsQuantityPopulated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isParamsStringPopulated() {
|
|
||||||
return myParamsStringPopulated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isParamsTokenPopulated() {
|
|
||||||
return myParamsTokenPopulated;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isParamsUriPopulated() {
|
|
||||||
return myParamsUriPopulated;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transient (not saved in DB) flag indicating that this resource was found to be unchanged by the current operation
|
|
||||||
* and was not re-saved in the database
|
|
||||||
*/
|
|
||||||
public boolean isUnchangedInCurrentOperation() {
|
|
||||||
return myUnchangedInCurrentOperation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setContentTextParsedIntoWords(String theContentText) {
|
|
||||||
myContentText = theContentText;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHashSha256(String theHashSha256) {
|
|
||||||
myHashSha256 = theHashSha256;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHasLinks(boolean theHasLinks) {
|
|
||||||
myHasLinks = theHasLinks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(Long theId) {
|
|
||||||
myId = theId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIndexStatus(Long theIndexStatus) {
|
public void setIndexStatus(Long theIndexStatus) {
|
||||||
myIndexStatus = theIndexStatus;
|
myIndexStatus = theIndexStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getLanguage() {
|
||||||
|
return myLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
public void setLanguage(String theLanguage) {
|
public void setLanguage(String theLanguage) {
|
||||||
if (defaultString(theLanguage).length() > MAX_LANGUAGE_LENGTH) {
|
if (defaultString(theLanguage).length() > MAX_LANGUAGE_LENGTH) {
|
||||||
throw new UnprocessableEntityException("Language exceeds maximum length of " + MAX_LANGUAGE_LENGTH + " chars: " + theLanguage);
|
throw new UnprocessableEntityException("Language exceeds maximum length of " + MAX_LANGUAGE_LENGTH + " chars: " + theLanguage);
|
||||||
|
@ -441,8 +279,22 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
myLanguage = theLanguage;
|
myLanguage = theLanguage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNarrativeTextParsedIntoWords(String theNarrativeText) {
|
public Collection<ResourceIndexedCompositeStringUnique> getParamsCompositeStringUnique() {
|
||||||
myNarrativeText = theNarrativeText;
|
if (myParamsCompositeStringUnique == null) {
|
||||||
|
myParamsCompositeStringUnique = new ArrayList<>();
|
||||||
|
}
|
||||||
|
return myParamsCompositeStringUnique;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParamsCompositeStringUnique(Collection<ResourceIndexedCompositeStringUnique> theParamsCompositeStringUnique) {
|
||||||
|
myParamsCompositeStringUnique = theParamsCompositeStringUnique;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<ResourceIndexedSearchParamCoords> getParamsCoords() {
|
||||||
|
if (myParamsCoords == null) {
|
||||||
|
myParamsCoords = new ArrayList<>();
|
||||||
|
}
|
||||||
|
return myParamsCoords;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsCoords(Collection<ResourceIndexedSearchParamCoords> theParamsCoords) {
|
public void setParamsCoords(Collection<ResourceIndexedSearchParamCoords> theParamsCoords) {
|
||||||
|
@ -453,8 +305,11 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
getParamsCoords().addAll(theParamsCoords);
|
getParamsCoords().addAll(theParamsCoords);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsCoordsPopulated(boolean theParamsCoordsPopulated) {
|
public Collection<ResourceIndexedSearchParamDate> getParamsDate() {
|
||||||
myParamsCoordsPopulated = theParamsCoordsPopulated;
|
if (myParamsDate == null) {
|
||||||
|
myParamsDate = new ArrayList<>();
|
||||||
|
}
|
||||||
|
return myParamsDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsDate(Collection<ResourceIndexedSearchParamDate> theParamsDate) {
|
public void setParamsDate(Collection<ResourceIndexedSearchParamDate> theParamsDate) {
|
||||||
|
@ -465,8 +320,11 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
getParamsDate().addAll(theParamsDate);
|
getParamsDate().addAll(theParamsDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsDatePopulated(boolean theParamsDatePopulated) {
|
public Collection<ResourceIndexedSearchParamNumber> getParamsNumber() {
|
||||||
myParamsDatePopulated = theParamsDatePopulated;
|
if (myParamsNumber == null) {
|
||||||
|
myParamsNumber = new ArrayList<>();
|
||||||
|
}
|
||||||
|
return myParamsNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsNumber(Collection<ResourceIndexedSearchParamNumber> theNumberParams) {
|
public void setParamsNumber(Collection<ResourceIndexedSearchParamNumber> theNumberParams) {
|
||||||
|
@ -477,8 +335,11 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
getParamsNumber().addAll(theNumberParams);
|
getParamsNumber().addAll(theNumberParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsNumberPopulated(boolean theParamsNumberPopulated) {
|
public Collection<ResourceIndexedSearchParamQuantity> getParamsQuantity() {
|
||||||
myParamsNumberPopulated = theParamsNumberPopulated;
|
if (myParamsQuantity == null) {
|
||||||
|
myParamsQuantity = new ArrayList<>();
|
||||||
|
}
|
||||||
|
return myParamsQuantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsQuantity(Collection<ResourceIndexedSearchParamQuantity> theQuantityParams) {
|
public void setParamsQuantity(Collection<ResourceIndexedSearchParamQuantity> theQuantityParams) {
|
||||||
|
@ -489,8 +350,11 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
getParamsQuantity().addAll(theQuantityParams);
|
getParamsQuantity().addAll(theQuantityParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsQuantityPopulated(boolean theParamsQuantityPopulated) {
|
public Collection<ResourceIndexedSearchParamString> getParamsString() {
|
||||||
myParamsQuantityPopulated = theParamsQuantityPopulated;
|
if (myParamsString == null) {
|
||||||
|
myParamsString = new ArrayList<>();
|
||||||
|
}
|
||||||
|
return myParamsString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsString(Collection<ResourceIndexedSearchParamString> theParamsString) {
|
public void setParamsString(Collection<ResourceIndexedSearchParamString> theParamsString) {
|
||||||
|
@ -501,8 +365,11 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
getParamsString().addAll(theParamsString);
|
getParamsString().addAll(theParamsString);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsStringPopulated(boolean theParamsStringPopulated) {
|
public Collection<ResourceIndexedSearchParamToken> getParamsToken() {
|
||||||
myParamsStringPopulated = theParamsStringPopulated;
|
if (myParamsToken == null) {
|
||||||
|
myParamsToken = new ArrayList<>();
|
||||||
|
}
|
||||||
|
return myParamsToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsToken(Collection<ResourceIndexedSearchParamToken> theParamsToken) {
|
public void setParamsToken(Collection<ResourceIndexedSearchParamToken> theParamsToken) {
|
||||||
|
@ -513,8 +380,11 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
getParamsToken().addAll(theParamsToken);
|
getParamsToken().addAll(theParamsToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsTokenPopulated(boolean theParamsTokenPopulated) {
|
public Collection<ResourceIndexedSearchParamUri> getParamsUri() {
|
||||||
myParamsTokenPopulated = theParamsTokenPopulated;
|
if (myParamsUri == null) {
|
||||||
|
myParamsUri = new ArrayList<>();
|
||||||
|
}
|
||||||
|
return myParamsUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsUri(Collection<ResourceIndexedSearchParamUri> theParamsUri) {
|
public void setParamsUri(Collection<ResourceIndexedSearchParamUri> theParamsUri) {
|
||||||
|
@ -525,8 +395,8 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
getParamsUri().addAll(theParamsUri);
|
getParamsUri().addAll(theParamsUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsUriPopulated(boolean theParamsUriPopulated) {
|
public String getProfile() {
|
||||||
myParamsUriPopulated = theParamsUriPopulated;
|
return myProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProfile(String theProfile) {
|
public void setProfile(String theProfile) {
|
||||||
|
@ -536,6 +406,13 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
myProfile = theProfile;
|
myProfile = theProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection<ResourceLink> getResourceLinks() {
|
||||||
|
if (myResourceLinks == null) {
|
||||||
|
myResourceLinks = new ArrayList<>();
|
||||||
|
}
|
||||||
|
return myResourceLinks;
|
||||||
|
}
|
||||||
|
|
||||||
public void setResourceLinks(Collection<ResourceLink> theLinks) {
|
public void setResourceLinks(Collection<ResourceLink> theLinks) {
|
||||||
if (!isHasLinks() && theLinks.isEmpty()) {
|
if (!isHasLinks() && theLinks.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -544,10 +421,112 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
getResourceLinks().addAll(theLinks);
|
getResourceLinks().addAll(theLinks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getResourceType() {
|
||||||
|
return myResourceType;
|
||||||
|
}
|
||||||
|
|
||||||
public void setResourceType(String theResourceType) {
|
public void setResourceType(String theResourceType) {
|
||||||
myResourceType = theResourceType;
|
myResourceType = theResourceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ResourceTag> getTags() {
|
||||||
|
if (myTags == null) {
|
||||||
|
myTags = new HashSet<>();
|
||||||
|
}
|
||||||
|
return myTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getVersion() {
|
||||||
|
return myVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(long theVersion) {
|
||||||
|
myVersion = theVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHasLinks() {
|
||||||
|
return myHasLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasLinks(boolean theHasLinks) {
|
||||||
|
myHasLinks = theHasLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isParamsCompositeStringUniquePresent() {
|
||||||
|
return myParamsCompositeStringUniquePresent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParamsCompositeStringUniquePresent(boolean theParamsCompositeStringUniquePresent) {
|
||||||
|
myParamsCompositeStringUniquePresent = theParamsCompositeStringUniquePresent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isParamsCoordsPopulated() {
|
||||||
|
return myParamsCoordsPopulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParamsCoordsPopulated(boolean theParamsCoordsPopulated) {
|
||||||
|
myParamsCoordsPopulated = theParamsCoordsPopulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isParamsDatePopulated() {
|
||||||
|
return myParamsDatePopulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParamsDatePopulated(boolean theParamsDatePopulated) {
|
||||||
|
myParamsDatePopulated = theParamsDatePopulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isParamsNumberPopulated() {
|
||||||
|
return myParamsNumberPopulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParamsNumberPopulated(boolean theParamsNumberPopulated) {
|
||||||
|
myParamsNumberPopulated = theParamsNumberPopulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isParamsQuantityPopulated() {
|
||||||
|
return myParamsQuantityPopulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParamsQuantityPopulated(boolean theParamsQuantityPopulated) {
|
||||||
|
myParamsQuantityPopulated = theParamsQuantityPopulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isParamsStringPopulated() {
|
||||||
|
return myParamsStringPopulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParamsStringPopulated(boolean theParamsStringPopulated) {
|
||||||
|
myParamsStringPopulated = theParamsStringPopulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isParamsTokenPopulated() {
|
||||||
|
return myParamsTokenPopulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParamsTokenPopulated(boolean theParamsTokenPopulated) {
|
||||||
|
myParamsTokenPopulated = theParamsTokenPopulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isParamsUriPopulated() {
|
||||||
|
return myParamsUriPopulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParamsUriPopulated(boolean theParamsUriPopulated) {
|
||||||
|
myParamsUriPopulated = theParamsUriPopulated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transient (not saved in DB) flag indicating that this resource was found to be unchanged by the current operation
|
||||||
|
* and was not re-saved in the database
|
||||||
|
*/
|
||||||
|
public boolean isUnchangedInCurrentOperation() {
|
||||||
|
return myUnchangedInCurrentOperation;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transient (not saved in DB) flag indicating that this resource was found to be unchanged by the current operation
|
* Transient (not saved in DB) flag indicating that this resource was found to be unchanged by the current operation
|
||||||
* and was not re-saved in the database
|
* and was not re-saved in the database
|
||||||
|
@ -556,8 +535,12 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
myUnchangedInCurrentOperation = theUnchangedInCurrentOperation;
|
myUnchangedInCurrentOperation = theUnchangedInCurrentOperation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVersion(long theVersion) {
|
public void setContentTextParsedIntoWords(String theContentText) {
|
||||||
myVersion = theVersion;
|
myContentText = theContentText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNarrativeTextParsedIntoWords(String theNarrativeText) {
|
||||||
|
myNarrativeText = theNarrativeText;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceHistoryTable toHistory(ResourceHistoryTable theResourceHistoryTable) {
|
public ResourceHistoryTable toHistory(ResourceHistoryTable theResourceHistoryTable) {
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
package ca.uhn.fhir.jpa.search;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
|
public class JpaRuntimeSearchParam extends RuntimeSearchParam {
|
||||||
|
|
||||||
|
private final boolean myUnique;
|
||||||
|
private final List<Component> myComponents;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public JpaRuntimeSearchParam(IIdType theId, String theUri, String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, Set<String> theProvidesMembershipInCompartments, Set<String> theTargets, RuntimeSearchParamStatusEnum theStatus, boolean theUnique, List<Component> theComponents, Collection<? extends IPrimitiveType<String>> theBase) {
|
||||||
|
super(theId, theUri, theName, theDescription, thePath, theParamType, createCompositeList(theParamType), theProvidesMembershipInCompartments, theTargets, theStatus, toStrings(theBase));
|
||||||
|
myUnique = theUnique;
|
||||||
|
myComponents = Collections.unmodifiableList(theComponents);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Collection<String> toStrings(Collection<? extends IPrimitiveType<String>> theBase) {
|
||||||
|
HashSet<String> retVal = new HashSet<>();
|
||||||
|
for (IPrimitiveType<String> next : theBase) {
|
||||||
|
if (isNotBlank(next.getValueAsString())) {
|
||||||
|
retVal.add(next.getValueAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Component> getComponents() {
|
||||||
|
return myComponents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUnique() {
|
||||||
|
return myUnique;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ArrayList<RuntimeSearchParam> createCompositeList(RestSearchParameterTypeEnum theParamType) {
|
||||||
|
if (theParamType == RestSearchParameterTypeEnum.COMPOSITE) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Component {
|
||||||
|
private final String myExpression;
|
||||||
|
private final IBaseReference myReference;
|
||||||
|
|
||||||
|
public Component(String theExpression, IBaseReference theReference) {
|
||||||
|
myExpression = theExpression;
|
||||||
|
myReference = theReference;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExpression() {
|
||||||
|
return myExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBaseReference getReference() {
|
||||||
|
return myReference;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package ca.uhn.fhir.jpa.util;
|
||||||
|
|
||||||
|
public class JpaConstants {
|
||||||
|
|
||||||
|
public static final String EXT_SP_UNIQUE = "http://hapifhir.io/fhir/StructureDefinition/sp-unique";
|
||||||
|
|
||||||
|
}
|
|
@ -48,10 +48,10 @@ public class TestR4Config extends BaseJavaConfigR4 {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ourLog.error("Exceeded maximum wait for connection", e);
|
ourLog.error("Exceeded maximum wait for connection", e);
|
||||||
logGetConnectionStackTrace();
|
logGetConnectionStackTrace();
|
||||||
if ("true".equals(System.getProperty("ci"))) {
|
// if ("true".equals(System.getProperty("ci"))) {
|
||||||
fail("Exceeded maximum wait for connection: " + e.toString());
|
fail("Exceeded maximum wait for connection: " + e.toString());
|
||||||
}
|
// }
|
||||||
System.exit(1);
|
// System.exit(1);
|
||||||
retVal = null;
|
retVal = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -236,6 +236,7 @@ public abstract class BaseJpaTest {
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamToken.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 " + ResourceIndexedSearchParamUri.class.getSimpleName() + " d").executeUpdate();
|
||||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamCoords.class.getSimpleName() + " d").executeUpdate();
|
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamCoords.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
entityManager.createQuery("DELETE from " + ResourceIndexedCompositeStringUnique.class.getSimpleName() + " d").executeUpdate();
|
||||||
entityManager.createQuery("DELETE from " + ResourceLink.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 " + SearchResult.class.getSimpleName() + " d").executeUpdate();
|
||||||
entityManager.createQuery("DELETE from " + SearchInclude.class.getSimpleName() + " d").executeUpdate();
|
entityManager.createQuery("DELETE from " + SearchInclude.class.getSimpleName() + " d").executeUpdate();
|
||||||
|
|
|
@ -57,8 +57,8 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
||||||
private static JpaValidationSupportChainDstu3 ourJpaValidationSupportChainDstu3;
|
private static JpaValidationSupportChainDstu3 ourJpaValidationSupportChainDstu3;
|
||||||
private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao;
|
private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao;
|
||||||
|
|
||||||
// @Autowired
|
@Autowired
|
||||||
// protected HapiWorkerContext myHapiWorkerContext;
|
protected IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("myAllergyIntoleranceDaoDstu3")
|
@Qualifier("myAllergyIntoleranceDaoDstu3")
|
||||||
protected IFhirResourceDao<AllergyIntolerance> myAllergyIntoleranceDao;
|
protected IFhirResourceDao<AllergyIntolerance> myAllergyIntoleranceDao;
|
||||||
|
|
|
@ -0,0 +1,295 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.dstu3;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||||
|
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedCompositeStringUnique;
|
||||||
|
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.jpa.util.JpaConstants;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.param.DateParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
import org.hl7.fhir.dstu3.model.*;
|
||||||
|
import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.orm.jpa.JpaSystemException;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
|
import static org.hamcrest.Matchers.empty;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "deprecation"})
|
||||||
|
public class FhirResourceDaoDstu3UniqueSearchParamTest extends BaseJpaDstu3Test {
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3UniqueSearchParamTest.class);
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() {
|
||||||
|
myDaoConfig.setDefaultSearchParamsCanBeOverridden(new DaoConfig().isDefaultSearchParamsCanBeOverridden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
myDaoConfig.setDefaultSearchParamsCanBeOverridden(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createUniqueBirthdateAndGenderSps() {
|
||||||
|
SearchParameter sp = new SearchParameter();
|
||||||
|
sp.setId("SearchParameter/patient-gender");
|
||||||
|
sp.setType(Enumerations.SearchParamType.TOKEN);
|
||||||
|
sp.setCode("gender");
|
||||||
|
sp.setExpression("Patient.gender");
|
||||||
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("Patient");
|
||||||
|
mySearchParameterDao.update(sp);
|
||||||
|
|
||||||
|
sp = new SearchParameter();
|
||||||
|
sp.setId("SearchParameter/patient-birthdate");
|
||||||
|
sp.setType(Enumerations.SearchParamType.DATE);
|
||||||
|
sp.setCode("birthdate");
|
||||||
|
sp.setExpression("Patient.birthDate");
|
||||||
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("Patient");
|
||||||
|
mySearchParameterDao.update(sp);
|
||||||
|
|
||||||
|
sp = new SearchParameter();
|
||||||
|
sp.setId("SearchParameter/patient-gender-birthdate");
|
||||||
|
sp.setType(Enumerations.SearchParamType.COMPOSITE);
|
||||||
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("Patient");
|
||||||
|
sp.addComponent()
|
||||||
|
.setExpression("Patient")
|
||||||
|
.setDefinition(new Reference("SearchParameter/patient-gender"));
|
||||||
|
sp.addComponent()
|
||||||
|
.setExpression("Patient")
|
||||||
|
.setDefinition(new Reference("SearchParameter/patient-birthdate"));
|
||||||
|
sp.addExtension()
|
||||||
|
.setUrl(JpaConstants.EXT_SP_UNIQUE)
|
||||||
|
.setValue(new BooleanType(true));
|
||||||
|
mySearchParameterDao.update(sp);
|
||||||
|
|
||||||
|
mySearchParamRegsitry.forceRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createUniqueNameAndManagingOrganizationSps() {
|
||||||
|
SearchParameter sp = new SearchParameter();
|
||||||
|
sp.setId("SearchParameter/patient-name");
|
||||||
|
sp.setType(Enumerations.SearchParamType.STRING);
|
||||||
|
sp.setCode("name");
|
||||||
|
sp.setExpression("Patient.name");
|
||||||
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("Patient");
|
||||||
|
mySearchParameterDao.update(sp);
|
||||||
|
|
||||||
|
sp = new SearchParameter();
|
||||||
|
sp.setId("SearchParameter/patient-organization");
|
||||||
|
sp.setType(Enumerations.SearchParamType.REFERENCE);
|
||||||
|
sp.setCode("organization");
|
||||||
|
sp.setExpression("Patient.managingOrganization");
|
||||||
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("Patient");
|
||||||
|
mySearchParameterDao.update(sp);
|
||||||
|
|
||||||
|
sp = new SearchParameter();
|
||||||
|
sp.setId("SearchParameter/patient-name-organization");
|
||||||
|
sp.setType(Enumerations.SearchParamType.COMPOSITE);
|
||||||
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("Patient");
|
||||||
|
sp.addComponent()
|
||||||
|
.setExpression("Patient")
|
||||||
|
.setDefinition(new Reference("SearchParameter/patient-name"));
|
||||||
|
sp.addComponent()
|
||||||
|
.setExpression("Patient")
|
||||||
|
.setDefinition(new Reference("SearchParameter/patient-organization"));
|
||||||
|
sp.addExtension()
|
||||||
|
.setUrl(JpaConstants.EXT_SP_UNIQUE)
|
||||||
|
.setValue(new BooleanType(true));
|
||||||
|
mySearchParameterDao.update(sp);
|
||||||
|
|
||||||
|
mySearchParamRegsitry.forceRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDetectUniqueSearchParams() {
|
||||||
|
createUniqueBirthdateAndGenderSps();
|
||||||
|
List<JpaRuntimeSearchParam> params = mySearchParamRegsitry.getActiveUniqueSearchParams("Patient");
|
||||||
|
|
||||||
|
assertEquals(1, params.size());
|
||||||
|
assertEquals(params.get(0).isUnique(), true);
|
||||||
|
assertEquals(2, params.get(0).getCompositeOf().size());
|
||||||
|
// Should be alphabetical order
|
||||||
|
assertEquals("birthdate", params.get(0).getCompositeOf().get(0).getName());
|
||||||
|
assertEquals("gender", params.get(0).getCompositeOf().get(1).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDuplicateUniqueValuesAreRejected() {
|
||||||
|
createUniqueBirthdateAndGenderSps();
|
||||||
|
|
||||||
|
Patient pt1 = new Patient();
|
||||||
|
pt1.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
pt1.setBirthDateElement(new DateType("2011-01-01"));
|
||||||
|
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
try {
|
||||||
|
myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||||
|
fail();
|
||||||
|
} catch (JpaSystemException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
|
||||||
|
Patient pt2 = new Patient();
|
||||||
|
pt2.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
IIdType id2 = myPatientDao.create(pt2).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
pt2 = new Patient();
|
||||||
|
pt2.setId(id2);
|
||||||
|
pt2.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
pt2.setBirthDateElement(new DateType("2011-01-01"));
|
||||||
|
try {
|
||||||
|
myPatientDao.update(pt2);
|
||||||
|
fail();
|
||||||
|
} catch (JpaSystemException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUniqueValuesAreIndexed_DateAndToken() {
|
||||||
|
createUniqueBirthdateAndGenderSps();
|
||||||
|
|
||||||
|
Patient pt1 = new Patient();
|
||||||
|
pt1.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
pt1.setBirthDateElement(new DateType("2011-01-01"));
|
||||||
|
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
List<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
|
||||||
|
assertEquals(1, uniques.size());
|
||||||
|
assertEquals("Patient/" + id1.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||||
|
assertEquals("Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale", uniques.get(0).getIndexString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchSynchronousUsingUniqueComposite() {
|
||||||
|
createUniqueBirthdateAndGenderSps();
|
||||||
|
|
||||||
|
Patient pt1 = new Patient();
|
||||||
|
pt1.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
pt1.setBirthDateElement(new DateType("2011-01-01"));
|
||||||
|
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
Patient pt2 = new Patient();
|
||||||
|
pt2.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
pt2.setBirthDateElement(new DateType("2011-01-02"));
|
||||||
|
IIdType id2 = myPatientDao.create(pt2).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
||||||
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
|
params.setLoadSynchronousUpTo(100);
|
||||||
|
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
|
||||||
|
params.add("birthdate", new DateParam("2011-01-01"));
|
||||||
|
IBundleProvider results = myPatientDao.search(params);
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(results), containsInAnyOrder(id1.getValue()));
|
||||||
|
assertEquals(SearchBuilder.HandlerTypeEnum.UNIQUE_INDEX, SearchBuilder.getLastHandlerMechanismForUnitTest());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchUsingUniqueComposite() {
|
||||||
|
createUniqueBirthdateAndGenderSps();
|
||||||
|
|
||||||
|
Patient pt1 = new Patient();
|
||||||
|
pt1.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
pt1.setBirthDateElement(new DateType("2011-01-01"));
|
||||||
|
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
Patient pt2 = new Patient();
|
||||||
|
pt2.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
pt2.setBirthDateElement(new DateType("2011-01-02"));
|
||||||
|
IIdType id2 = myPatientDao.create(pt2).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
||||||
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
|
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
|
||||||
|
params.add("birthdate", new DateParam("2011-01-01"));
|
||||||
|
IBundleProvider results = myPatientDao.search(params);
|
||||||
|
String searchId = results.getUuid();
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(results), containsInAnyOrder(id1.getValue()));
|
||||||
|
assertEquals(SearchBuilder.HandlerTypeEnum.UNIQUE_INDEX, SearchBuilder.getLastHandlerMechanismForUnitTest());
|
||||||
|
|
||||||
|
// Other order
|
||||||
|
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
||||||
|
params = new SearchParameterMap();
|
||||||
|
params.add("birthdate", new DateParam("2011-01-01"));
|
||||||
|
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
|
||||||
|
results = myPatientDao.search(params);
|
||||||
|
assertEquals(searchId, results.getUuid());
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(results), containsInAnyOrder(id1.getValue()));
|
||||||
|
// Null because we just reuse the last search
|
||||||
|
assertEquals(null, SearchBuilder.getLastHandlerMechanismForUnitTest());
|
||||||
|
|
||||||
|
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
||||||
|
params = new SearchParameterMap();
|
||||||
|
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
|
||||||
|
params.add("birthdate", new DateParam("2011-01-03"));
|
||||||
|
results = myPatientDao.search(params);
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(results), empty());
|
||||||
|
assertEquals(SearchBuilder.HandlerTypeEnum.UNIQUE_INDEX, SearchBuilder.getLastHandlerMechanismForUnitTest());
|
||||||
|
|
||||||
|
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
||||||
|
params = new SearchParameterMap();
|
||||||
|
params.add("birthdate", new DateParam("2011-01-03"));
|
||||||
|
results = myPatientDao.search(params);
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(results), empty());
|
||||||
|
assertEquals(SearchBuilder.HandlerTypeEnum.STANDARD_QUERY, SearchBuilder.getLastHandlerMechanismForUnitTest());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUniqueValuesAreIndexed_StringAndReference() {
|
||||||
|
createUniqueNameAndManagingOrganizationSps();
|
||||||
|
|
||||||
|
Organization org = new Organization();
|
||||||
|
org.setId("Organization/ORG");
|
||||||
|
org.setName("ORG");
|
||||||
|
myOrganizationDao.update(org);
|
||||||
|
|
||||||
|
Patient pt1 = new Patient();
|
||||||
|
pt1.addName()
|
||||||
|
.setFamily("FAMILY1")
|
||||||
|
.addGiven("GIVEN1")
|
||||||
|
.addGiven("GIVEN2")
|
||||||
|
.addGiven("GIVEN2"); // GIVEN2 happens twice
|
||||||
|
pt1.setManagingOrganization(new Reference("Organization/ORG"));
|
||||||
|
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
List<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
|
||||||
|
Collections.sort(uniques);
|
||||||
|
|
||||||
|
assertEquals(3, uniques.size());
|
||||||
|
assertEquals("Patient/" + id1.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||||
|
assertEquals("Patient?name=FAMILY1&organization=Organization%2FORG", uniques.get(0).getIndexString());
|
||||||
|
|
||||||
|
assertEquals("Patient/" + id1.getIdPart(), uniques.get(1).getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||||
|
assertEquals("Patient?name=GIVEN1&organization=Organization%2FORG", uniques.get(1).getIndexString());
|
||||||
|
|
||||||
|
assertEquals("Patient/" + id1.getIdPart(), uniques.get(2).getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||||
|
assertEquals("Patient?name=GIVEN2&organization=Organization%2FORG", uniques.get(2).getIndexString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClassClearContext() {
|
||||||
|
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -3,9 +3,11 @@ package ca.uhn.fhir.jpa.dao.dstu3;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
|
||||||
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
|
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
|
||||||
import org.hl7.fhir.dstu3.hapi.validation.IValidationSupport;
|
import org.hl7.fhir.dstu3.hapi.validation.IValidationSupport;
|
||||||
import org.hl7.fhir.dstu3.model.Observation;
|
import org.hl7.fhir.dstu3.model.Observation;
|
||||||
|
@ -53,6 +55,16 @@ public class SearchParamExtractorDstu3Test {
|
||||||
return sps;
|
return sps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName, Set<String> theParamNames) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void forceRefresh() {
|
public void forceRefresh() {
|
||||||
// nothing
|
// nothing
|
||||||
|
|
|
@ -47,17 +47,15 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
|
||||||
//@formatter:off
|
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@ContextConfiguration(classes= {TestR4Config.class})
|
@ContextConfiguration(classes= {TestR4Config.class})
|
||||||
//@formatter:on
|
|
||||||
public abstract class BaseJpaR4Test extends BaseJpaTest {
|
public abstract class BaseJpaR4Test extends BaseJpaTest {
|
||||||
|
|
||||||
private static JpaValidationSupportChainR4 ourJpaValidationSupportChainR4;
|
private static JpaValidationSupportChainR4 ourJpaValidationSupportChainR4;
|
||||||
private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao;
|
private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao;
|
||||||
|
|
||||||
// @Autowired
|
@Autowired
|
||||||
// protected HapiWorkerContext myHapiWorkerContext;
|
protected IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("myAllergyIntoleranceDaoR4")
|
@Qualifier("myAllergyIntoleranceDaoR4")
|
||||||
protected IFhirResourceDao<AllergyIntolerance> myAllergyIntoleranceDao;
|
protected IFhirResourceDao<AllergyIntolerance> myAllergyIntoleranceDao;
|
||||||
|
|
|
@ -0,0 +1,296 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.r4;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||||
|
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedCompositeStringUnique;
|
||||||
|
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.jpa.util.JpaConstants;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.param.DateParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||||
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.r4.model.*;
|
||||||
|
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.orm.jpa.JpaSystemException;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
|
import static org.hamcrest.Matchers.empty;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "deprecation"})
|
||||||
|
public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4UniqueSearchParamTest.class);
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() {
|
||||||
|
myDaoConfig.setDefaultSearchParamsCanBeOverridden(new DaoConfig().isDefaultSearchParamsCanBeOverridden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
myDaoConfig.setDefaultSearchParamsCanBeOverridden(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createUniqueBirthdateAndGenderSps() {
|
||||||
|
SearchParameter sp = new SearchParameter();
|
||||||
|
sp.setId("SearchParameter/patient-gender");
|
||||||
|
sp.setType(Enumerations.SearchParamType.TOKEN);
|
||||||
|
sp.setCode("gender");
|
||||||
|
sp.setExpression("Patient.gender");
|
||||||
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("Patient");
|
||||||
|
mySearchParameterDao.update(sp);
|
||||||
|
|
||||||
|
sp = new SearchParameter();
|
||||||
|
sp.setId("SearchParameter/patient-birthdate");
|
||||||
|
sp.setType(Enumerations.SearchParamType.DATE);
|
||||||
|
sp.setCode("birthdate");
|
||||||
|
sp.setExpression("Patient.birthDate");
|
||||||
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("Patient");
|
||||||
|
mySearchParameterDao.update(sp);
|
||||||
|
|
||||||
|
sp = new SearchParameter();
|
||||||
|
sp.setId("SearchParameter/patient-gender-birthdate");
|
||||||
|
sp.setType(Enumerations.SearchParamType.COMPOSITE);
|
||||||
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("Patient");
|
||||||
|
sp.addComponent()
|
||||||
|
.setExpression("Patient")
|
||||||
|
.setDefinition(new Reference("SearchParameter/patient-gender"));
|
||||||
|
sp.addComponent()
|
||||||
|
.setExpression("Patient")
|
||||||
|
.setDefinition(new Reference("SearchParameter/patient-birthdate"));
|
||||||
|
sp.addExtension()
|
||||||
|
.setUrl(JpaConstants.EXT_SP_UNIQUE)
|
||||||
|
.setValue(new BooleanType(true));
|
||||||
|
mySearchParameterDao.update(sp);
|
||||||
|
|
||||||
|
mySearchParamRegsitry.forceRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createUniqueNameAndManagingOrganizationSps() {
|
||||||
|
SearchParameter sp = new SearchParameter();
|
||||||
|
sp.setId("SearchParameter/patient-name");
|
||||||
|
sp.setType(Enumerations.SearchParamType.STRING);
|
||||||
|
sp.setCode("name");
|
||||||
|
sp.setExpression("Patient.name");
|
||||||
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("Patient");
|
||||||
|
mySearchParameterDao.update(sp);
|
||||||
|
|
||||||
|
sp = new SearchParameter();
|
||||||
|
sp.setId("SearchParameter/patient-organization");
|
||||||
|
sp.setType(Enumerations.SearchParamType.REFERENCE);
|
||||||
|
sp.setCode("organization");
|
||||||
|
sp.setExpression("Patient.managingOrganization");
|
||||||
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("Patient");
|
||||||
|
mySearchParameterDao.update(sp);
|
||||||
|
|
||||||
|
sp = new SearchParameter();
|
||||||
|
sp.setId("SearchParameter/patient-name-organization");
|
||||||
|
sp.setType(Enumerations.SearchParamType.COMPOSITE);
|
||||||
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("Patient");
|
||||||
|
sp.addComponent()
|
||||||
|
.setExpression("Patient")
|
||||||
|
.setDefinition(new Reference("SearchParameter/patient-name"));
|
||||||
|
sp.addComponent()
|
||||||
|
.setExpression("Patient")
|
||||||
|
.setDefinition(new Reference("SearchParameter/patient-organization"));
|
||||||
|
sp.addExtension()
|
||||||
|
.setUrl(JpaConstants.EXT_SP_UNIQUE)
|
||||||
|
.setValue(new BooleanType(true));
|
||||||
|
mySearchParameterDao.update(sp);
|
||||||
|
|
||||||
|
mySearchParamRegsitry.forceRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDetectUniqueSearchParams() {
|
||||||
|
createUniqueBirthdateAndGenderSps();
|
||||||
|
List<JpaRuntimeSearchParam> params = mySearchParamRegsitry.getActiveUniqueSearchParams("Patient");
|
||||||
|
|
||||||
|
assertEquals(1, params.size());
|
||||||
|
assertEquals(params.get(0).isUnique(), true);
|
||||||
|
assertEquals(2, params.get(0).getCompositeOf().size());
|
||||||
|
// Should be alphabetical order
|
||||||
|
assertEquals("birthdate", params.get(0).getCompositeOf().get(0).getName());
|
||||||
|
assertEquals("gender", params.get(0).getCompositeOf().get(1).getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDuplicateUniqueValuesAreRejected() {
|
||||||
|
createUniqueBirthdateAndGenderSps();
|
||||||
|
|
||||||
|
Patient pt1 = new Patient();
|
||||||
|
pt1.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
pt1.setBirthDateElement(new DateType("2011-01-01"));
|
||||||
|
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
try {
|
||||||
|
myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||||
|
fail();
|
||||||
|
} catch (JpaSystemException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
|
||||||
|
Patient pt2 = new Patient();
|
||||||
|
pt2.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
IIdType id2 = myPatientDao.create(pt2).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
pt2 = new Patient();
|
||||||
|
pt2.setId(id2);
|
||||||
|
pt2.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
pt2.setBirthDateElement(new DateType("2011-01-01"));
|
||||||
|
try {
|
||||||
|
myPatientDao.update(pt2);
|
||||||
|
fail();
|
||||||
|
} catch (JpaSystemException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUniqueValuesAreIndexed_DateAndToken() {
|
||||||
|
createUniqueBirthdateAndGenderSps();
|
||||||
|
|
||||||
|
Patient pt1 = new Patient();
|
||||||
|
pt1.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
pt1.setBirthDateElement(new DateType("2011-01-01"));
|
||||||
|
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
List<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
|
||||||
|
assertEquals(1, uniques.size());
|
||||||
|
assertEquals("Patient/" + id1.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||||
|
assertEquals("Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale", uniques.get(0).getIndexString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchSynchronousUsingUniqueComposite() {
|
||||||
|
createUniqueBirthdateAndGenderSps();
|
||||||
|
|
||||||
|
Patient pt1 = new Patient();
|
||||||
|
pt1.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
pt1.setBirthDateElement(new DateType("2011-01-01"));
|
||||||
|
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
Patient pt2 = new Patient();
|
||||||
|
pt2.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
pt2.setBirthDateElement(new DateType("2011-01-02"));
|
||||||
|
IIdType id2 = myPatientDao.create(pt2).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
||||||
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
|
params.setLoadSynchronousUpTo(100);
|
||||||
|
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
|
||||||
|
params.add("birthdate", new DateParam("2011-01-01"));
|
||||||
|
IBundleProvider results = myPatientDao.search(params);
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(results), containsInAnyOrder(id1.getValue()));
|
||||||
|
assertEquals(SearchBuilder.HandlerTypeEnum.UNIQUE_INDEX, SearchBuilder.getLastHandlerMechanismForUnitTest());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchUsingUniqueComposite() {
|
||||||
|
createUniqueBirthdateAndGenderSps();
|
||||||
|
|
||||||
|
Patient pt1 = new Patient();
|
||||||
|
pt1.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
pt1.setBirthDateElement(new DateType("2011-01-01"));
|
||||||
|
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
Patient pt2 = new Patient();
|
||||||
|
pt2.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
|
pt2.setBirthDateElement(new DateType("2011-01-02"));
|
||||||
|
IIdType id2 = myPatientDao.create(pt2).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
||||||
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
|
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
|
||||||
|
params.add("birthdate", new DateParam("2011-01-01"));
|
||||||
|
IBundleProvider results = myPatientDao.search(params);
|
||||||
|
String searchId = results.getUuid();
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(results), containsInAnyOrder(id1.getValue()));
|
||||||
|
assertEquals(SearchBuilder.HandlerTypeEnum.UNIQUE_INDEX, SearchBuilder.getLastHandlerMechanismForUnitTest());
|
||||||
|
|
||||||
|
// Other order
|
||||||
|
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
||||||
|
params = new SearchParameterMap();
|
||||||
|
params.add("birthdate", new DateParam("2011-01-01"));
|
||||||
|
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
|
||||||
|
results = myPatientDao.search(params);
|
||||||
|
assertEquals(searchId, results.getUuid());
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(results), containsInAnyOrder(id1.getValue()));
|
||||||
|
// Null because we just reuse the last search
|
||||||
|
assertEquals(null, SearchBuilder.getLastHandlerMechanismForUnitTest());
|
||||||
|
|
||||||
|
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
||||||
|
params = new SearchParameterMap();
|
||||||
|
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
|
||||||
|
params.add("birthdate", new DateParam("2011-01-03"));
|
||||||
|
results = myPatientDao.search(params);
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(results), empty());
|
||||||
|
assertEquals(SearchBuilder.HandlerTypeEnum.UNIQUE_INDEX, SearchBuilder.getLastHandlerMechanismForUnitTest());
|
||||||
|
|
||||||
|
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
||||||
|
params = new SearchParameterMap();
|
||||||
|
params.add("birthdate", new DateParam("2011-01-03"));
|
||||||
|
results = myPatientDao.search(params);
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(results), empty());
|
||||||
|
assertEquals(SearchBuilder.HandlerTypeEnum.STANDARD_QUERY, SearchBuilder.getLastHandlerMechanismForUnitTest());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUniqueValuesAreIndexed_StringAndReference() {
|
||||||
|
createUniqueNameAndManagingOrganizationSps();
|
||||||
|
|
||||||
|
Organization org = new Organization();
|
||||||
|
org.setId("Organization/ORG");
|
||||||
|
org.setName("ORG");
|
||||||
|
myOrganizationDao.update(org);
|
||||||
|
|
||||||
|
Patient pt1 = new Patient();
|
||||||
|
pt1.addName()
|
||||||
|
.setFamily("FAMILY1")
|
||||||
|
.addGiven("GIVEN1")
|
||||||
|
.addGiven("GIVEN2")
|
||||||
|
.addGiven("GIVEN2"); // GIVEN2 happens twice
|
||||||
|
pt1.setManagingOrganization(new Reference("Organization/ORG"));
|
||||||
|
IIdType id1 = myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
List<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAll();
|
||||||
|
Collections.sort(uniques);
|
||||||
|
|
||||||
|
assertEquals(3, uniques.size());
|
||||||
|
assertEquals("Patient/" + id1.getIdPart(), uniques.get(0).getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||||
|
assertEquals("Patient?name=FAMILY1&organization=Organization%2FORG", uniques.get(0).getIndexString());
|
||||||
|
|
||||||
|
assertEquals("Patient/" + id1.getIdPart(), uniques.get(1).getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||||
|
assertEquals("Patient?name=GIVEN1&organization=Organization%2FORG", uniques.get(1).getIndexString());
|
||||||
|
|
||||||
|
assertEquals("Patient/" + id1.getIdPart(), uniques.get(2).getResource().getIdDt().toUnqualifiedVersionless().getValue());
|
||||||
|
assertEquals("Patient?name=GIVEN2&organization=Organization%2FORG", uniques.get(2).getIndexString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClassClearContext() {
|
||||||
|
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
|
||||||
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
|
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
|
||||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||||
import org.hl7.fhir.r4.model.Observation;
|
import org.hl7.fhir.r4.model.Observation;
|
||||||
|
@ -45,6 +46,16 @@ public class SearchParamExtractorR4Test {
|
||||||
return sps;
|
return sps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName, Set<String> theParamNames) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void forceRefresh() {
|
public void forceRefresh() {
|
||||||
// nothing
|
// nothing
|
||||||
|
|
|
@ -49,7 +49,7 @@ public interface IServerOperationInterceptor extends IServerInterceptor {
|
||||||
* User code may call this method to indicate to an interceptor that
|
* User code may call this method to indicate to an interceptor that
|
||||||
* a resource is being updated
|
* a resource is being updated
|
||||||
*
|
*
|
||||||
* @deprecated Deprecated in HAPI FHIR 2.6 in favour of {@link #resourceUpdated(RequestDetails, IBaseResource, IBaseResource)}
|
* @deprecated Deprecated in HAPI FHIR 3.0.0 in favour of {@link #resourceUpdated(RequestDetails, IBaseResource, IBaseResource)}
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
void resourceUpdated(RequestDetails theRequest, IBaseResource theResource);
|
void resourceUpdated(RequestDetails theRequest, IBaseResource theResource);
|
||||||
|
|
|
@ -349,6 +349,12 @@
|
||||||
HAPI FHIR 2.5, but this was not documented. This variable has now been
|
HAPI FHIR 2.5, but this was not documented. This variable has now been
|
||||||
documented as a part of the available features.
|
documented as a part of the available features.
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
A new experimental feature has been added to the JPA server which allows
|
||||||
|
you to define certain search parameter combinations as being resource keys,
|
||||||
|
so that a database constraint will prevent more than one resource from
|
||||||
|
having a matching pair
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="2.5" date="2017-06-08">
|
<release version="2.5" date="2017-06-08">
|
||||||
<action type="fix">
|
<action type="fix">
|
||||||
|
|
Loading…
Reference in New Issue