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;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.trim;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -50,7 +51,12 @@ public class RuntimeSearchParam {
|
|||
}
|
||||
|
||||
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();
|
||||
myId = theId;
|
||||
myUri = theUri;
|
||||
|
@ -70,13 +76,19 @@ public class RuntimeSearchParam {
|
|||
} else {
|
||||
myTargets = null;
|
||||
}
|
||||
|
||||
HashSet<String> base = new HashSet<String>();
|
||||
int indexOf = thePath.indexOf('.');
|
||||
if (indexOf != -1) {
|
||||
base.add(trim(thePath.substring(0, indexOf)));
|
||||
|
||||
if (theBase == null || theBase.isEmpty()) {
|
||||
HashSet<String> base = new HashSet<>();
|
||||
if (isNotBlank(thePath)) {
|
||||
int indexOf = thePath.indexOf('.');
|
||||
if (indexOf != -1) {
|
||||
base.add(trim(thePath.substring(0, indexOf)));
|
||||
}
|
||||
}
|
||||
myBase = Collections.unmodifiableSet(base);
|
||||
} else {
|
||||
myBase = Collections.unmodifiableSet(new HashSet<>(theBase));
|
||||
}
|
||||
myBase = Collections.unmodifiableSet(base);
|
||||
}
|
||||
|
||||
public Set<String> getBase() {
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.springframework.context.annotation.Configuration;
|
|||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.orm.hibernate5.HibernateExceptionTranslator;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
|
|
|
@ -19,19 +19,49 @@ package ca.uhn.fhir.jpa.dao;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.Normalizer;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.persistence.criteria.*;
|
||||
import javax.xml.stream.events.Characters;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
|
||||
import org.apache.commons.lang3.*;
|
||||
import ca.uhn.fhir.context.*;
|
||||
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.JpaRuntimeSearchParam;
|
||||
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.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.client.utils.URLEncodedUtils;
|
||||
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.transaction.PlatformTransactionManager;
|
||||
|
||||
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 javax.persistence.*;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
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 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.*;
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
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_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 OO_SEVERITY_ERROR = "error";
|
||||
public static final String OO_SEVERITY_INFO = "information";
|
||||
public static final String OO_SEVERITY_WARN = "warning";
|
||||
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";
|
||||
public static final String UCUM_NS = "http://unitsofmeasure.org";
|
||||
static final Set<String> EXCLUDE_ELEMENTS_IN_ENCODED;
|
||||
/**
|
||||
* 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)}
|
||||
*/
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
@Autowired(required = true)
|
||||
private DaoConfig myConfig;
|
||||
private FhirContext myContext;
|
||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||
protected EntityManager myEntityManager;
|
||||
@Autowired
|
||||
protected IForcedIdDao myForcedIdDao;
|
||||
@Autowired(required = false)
|
||||
protected IFulltextSearchSvc myFulltextSearchSvc;
|
||||
@Autowired
|
||||
private PlatformTransactionManager myPlatformTransactionManager;
|
||||
|
||||
@Autowired
|
||||
private List<IFhirResourceDao<?>> myResourceDaos;
|
||||
|
||||
@Autowired
|
||||
private IResourceHistoryTableDao myResourceHistoryTableDao;
|
||||
|
||||
@Autowired()
|
||||
protected IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
|
||||
|
||||
private Map<Class<? extends IBaseResource>, IFhirResourceDao<?>> myResourceTypeToDao;
|
||||
|
||||
@Autowired
|
||||
protected ISearchCoordinatorSvc mySearchCoordinatorSvc;
|
||||
|
||||
@Autowired
|
||||
private ISearchDao mySearchDao;
|
||||
|
||||
@Autowired
|
||||
private ISearchParamExtractor mySearchParamExtractor;
|
||||
|
||||
@Autowired
|
||||
private ISearchParamPresenceSvc mySearchParamPresenceSvc;
|
||||
|
||||
@Autowired
|
||||
private ISearchParamRegistry mySearchParamRegistry;
|
||||
|
||||
@Autowired
|
||||
private ISearchResultDao mySearchResultDao;
|
||||
|
||||
@Autowired
|
||||
protected ISearchParamRegistry mySerarchParamRegistry;
|
||||
|
||||
@Autowired()
|
||||
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) {
|
||||
if (theRequestDetails != null) {
|
||||
|
@ -188,13 +196,84 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
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
|
||||
* were found to have a value
|
||||
* were found to have a value
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
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..
|
||||
|
@ -243,12 +322,12 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
/*
|
||||
* This can only really happen if the DAO is being called
|
||||
* programatically with a Bundle (not through the FHIR REST API)
|
||||
* but Smile does this
|
||||
* but Smile does this
|
||||
*/
|
||||
if (nextId.isEmpty() && nextValue.getResource() != null) {
|
||||
nextId = nextValue.getResource().getIdElement();
|
||||
}
|
||||
|
||||
|
||||
if (nextId.isEmpty() || nextId.getValue().startsWith("#")) {
|
||||
// This is a blank or contained resource reference
|
||||
continue;
|
||||
|
@ -291,7 +370,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
resourceDefinition = getContext().getResourceDefinition(typeString);
|
||||
} catch (DataFormatException e) {
|
||||
throw new InvalidRequestException(
|
||||
"Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " + nextId.getValue());
|
||||
"Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " + nextId.getValue());
|
||||
}
|
||||
|
||||
if (isNotBlank(baseUrl)) {
|
||||
|
@ -352,14 +431,14 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
|
||||
if (!typeString.equals(target.getResourceType())) {
|
||||
throw new UnprocessableEntityException(
|
||||
"Resource contains reference to " + nextId.getValue() + " but resource with ID " + nextId.getIdPart() + " is actually of type " + target.getResourceType());
|
||||
"Resource contains reference to " + nextId.getValue() + " but resource with ID " + nextId.getIdPart() + " is actually of type " + target.getResourceType());
|
||||
}
|
||||
|
||||
if (target.getDeleted() != null) {
|
||||
String resName = targetResourceDef.getName();
|
||||
throw new InvalidRequestException("Resource " + resName + "/" + id + " is deleted, specified in path: " + nextPathsUnsplit);
|
||||
}
|
||||
|
||||
|
||||
if (nextSpDef.getTargets() != null && !nextSpDef.getTargets().contains(typeString)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -375,11 +454,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
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) {
|
||||
return mySearchParamExtractor.extractSearchParamCoords(theEntity, theResource);
|
||||
}
|
||||
|
@ -509,7 +583,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <RT extends BaseResourceIndexedSearchParam> void findMissingSearchParams(ResourceTable theEntity, Set<Entry<String, RuntimeSearchParam>> activeSearchParams, RestSearchParameterTypeEnum type,
|
||||
Set<RT> paramCollection) {
|
||||
Set<RT> paramCollection) {
|
||||
for (Entry<String, RuntimeSearchParam> nextEntry : activeSearchParams) {
|
||||
String nextParamName = nextEntry.getKey();
|
||||
if (nextEntry.getValue().getParamType() == type) {
|
||||
|
@ -569,11 +643,20 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
return myConfig;
|
||||
}
|
||||
|
||||
public void setConfig(DaoConfig theConfig) {
|
||||
myConfig = theConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FhirContext getContext() {
|
||||
return myContext;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setContext(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
public FhirContext getContext(FhirVersionEnum theVersion) {
|
||||
Validate.notNull(theVersion, "theVersion must not be null");
|
||||
synchronized (ourRetrievalContexts) {
|
||||
|
@ -606,6 +689,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
return dao;
|
||||
}
|
||||
|
||||
public IResourceIndexedCompositeStringUniqueDao getResourceIndexedCompositeStringUniqueDao() {
|
||||
return myResourceIndexedCompositeStringUniqueDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeSearchParam getSearchParamByName(RuntimeResourceDefinition theResourceDef, String theParamName) {
|
||||
Map<String, RuntimeSearchParam> params = mySearchParamRegistry.getActiveSearchParams(theResourceDef.getName());
|
||||
|
@ -621,23 +708,23 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
if (isBlank(theScheme) && isBlank(theTerm) && isBlank(theLabel)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<TagDefinition> cq = builder.createQuery(TagDefinition.class);
|
||||
Root<TagDefinition> from = cq.from(TagDefinition.class);
|
||||
|
||||
if (isNotBlank(theScheme)) {
|
||||
cq.where(
|
||||
builder.and(
|
||||
builder.equal(from.get("myTagType"), theTagType),
|
||||
builder.equal(from.get("mySystem"), theScheme),
|
||||
builder.equal(from.get("myCode"), theTerm)));
|
||||
builder.and(
|
||||
builder.equal(from.get("myTagType"), theTagType),
|
||||
builder.equal(from.get("mySystem"), theScheme),
|
||||
builder.equal(from.get("myCode"), theTerm)));
|
||||
} else {
|
||||
cq.where(
|
||||
builder.and(
|
||||
builder.equal(from.get("myTagType"), theTagType),
|
||||
builder.isNull(from.get("mySystem")),
|
||||
builder.equal(from.get("myCode"), theTerm)));
|
||||
builder.and(
|
||||
builder.equal(from.get("myTagType"), theTagType),
|
||||
builder.isNull(from.get("mySystem")),
|
||||
builder.equal(from.get("myCode"), theTerm)));
|
||||
}
|
||||
|
||||
TypedQuery<TagDefinition> q = myEntityManager.createQuery(cq);
|
||||
|
@ -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, ResourceHistoryTag.class);
|
||||
if (tagIds.isEmpty()) {
|
||||
|
@ -764,8 +851,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
@Override
|
||||
public SearchBuilder newSearchBuilder() {
|
||||
SearchBuilder builder = new SearchBuilder(getContext(), myEntityManager, myFulltextSearchSvc, this, myResourceIndexedSearchParamUriDao,
|
||||
myForcedIdDao,
|
||||
myTerminologySvc, mySerarchParamRegistry);
|
||||
myForcedIdDao,
|
||||
myTerminologySvc, mySerarchParamRegistry);
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
@ -773,7 +860,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
if (theRequestDetails.getId() != null && theRequestDetails.getId().hasResourceType() && isNotBlank(theRequestDetails.getResourceType())) {
|
||||
if (theRequestDetails.getId().getResourceType().equals(theRequestDetails.getResourceType()) == false) {
|
||||
throw new InternalErrorException(
|
||||
"Inconsistent server state - Resource types don't match: " + theRequestDetails.getId().getResourceType() + " / " + theRequestDetails.getResourceType());
|
||||
"Inconsistent server state - Resource types don't match: " + theRequestDetails.getId().getResourceType() + " / " + theRequestDetails.getResourceType());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -791,7 +878,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
@SuppressWarnings("rawtypes")
|
||||
List<IPrimitiveType> childElements = getContext().newTerser().getAllPopulatedChildElementsOfType(theResource, IPrimitiveType.class);
|
||||
for (@SuppressWarnings("rawtypes")
|
||||
IPrimitiveType nextType : childElements) {
|
||||
IPrimitiveType nextType : childElements) {
|
||||
if (nextType instanceof StringDt || nextType.getClass().getSimpleName().equals("StringType")) {
|
||||
String nextValue = nextType.getValueAsString();
|
||||
if (isNotBlank(nextValue)) {
|
||||
|
@ -1063,11 +1150,9 @@ 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.
|
||||
*
|
||||
* @param theEntity
|
||||
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
|
||||
* @param theResource
|
||||
* The resource being persisted
|
||||
*
|
||||
* @param theEntity The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
|
||||
* @param theResource The resource being persisted
|
||||
*/
|
||||
protected void postPersist(ResourceTable theEntity, T theResource) {
|
||||
// nothing
|
||||
|
@ -1075,11 +1160,9 @@ 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
|
||||
*
|
||||
* @param theEntity
|
||||
* The resource
|
||||
* @param theResource
|
||||
* The resource being persisted
|
||||
*
|
||||
* @param theEntity The resource
|
||||
* @param theResource The resource being persisted
|
||||
*/
|
||||
protected void postUpdate(ResourceTable theEntity, T theResource) {
|
||||
// nothing
|
||||
|
@ -1111,15 +1194,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
throw new NotImplementedException("");
|
||||
}
|
||||
|
||||
public void setConfig(DaoConfig theConfig) {
|
||||
myConfig = theConfig;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setContext(FhirContext theContext) {
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
public void setEntityManager(EntityManager theEntityManager) {
|
||||
myEntityManager = theEntityManager;
|
||||
}
|
||||
|
@ -1142,11 +1216,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
* <p>
|
||||
* 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>
|
||||
*
|
||||
* @param theEntity
|
||||
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
|
||||
* @param theTag
|
||||
* The tag
|
||||
*
|
||||
* @param theEntity The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
|
||||
* @param theTag The tag
|
||||
* @return Retturns <code>true</code> if the tag should be removed
|
||||
*/
|
||||
protected boolean shouldDroppedTagBeRemovedOnUpdate(ResourceTable theEntity, ResourceTag theTag) {
|
||||
|
@ -1156,6 +1228,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
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) {
|
||||
// ResourceTable retVal = new ResourceTable();
|
||||
//
|
||||
|
@ -1164,13 +1243,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
// 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")
|
||||
@Override
|
||||
public <R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation) {
|
||||
|
@ -1268,7 +1340,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ResourceTable updateEntity(final IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
|
||||
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
|
||||
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
|
||||
ourLog.debug("Starting entity update");
|
||||
|
||||
/*
|
||||
|
@ -1281,7 +1353,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
String resourceType = myContext.getResourceDefinition(theResource).getName();
|
||||
if (isNotBlank(theEntity.getResourceType()) && !theEntity.getResourceType().equals(resourceType)) {
|
||||
throw new UnprocessableEntityException(
|
||||
"Existing resource ID[" + theEntity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + theEntity.getResourceType() + "] - Cannot update with [" + resourceType + "]");
|
||||
"Existing resource ID[" + theEntity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + theEntity.getResourceType() + "] - Cannot update with [" + resourceType + "]");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1291,38 +1363,42 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
theEntity.setPublished(theUpdateTime);
|
||||
}
|
||||
|
||||
Collection<ResourceIndexedSearchParamString> paramsString = new ArrayList<>();
|
||||
Collection<ResourceIndexedSearchParamString> existingStringParams = new ArrayList<>();
|
||||
if (theEntity.isParamsStringPopulated()) {
|
||||
paramsString.addAll(theEntity.getParamsString());
|
||||
existingStringParams.addAll(theEntity.getParamsString());
|
||||
}
|
||||
Collection<ResourceIndexedSearchParamToken> paramsToken = new ArrayList<>();
|
||||
Collection<ResourceIndexedSearchParamToken> existingTokenParams = new ArrayList<>();
|
||||
if (theEntity.isParamsTokenPopulated()) {
|
||||
paramsToken.addAll(theEntity.getParamsToken());
|
||||
existingTokenParams.addAll(theEntity.getParamsToken());
|
||||
}
|
||||
Collection<ResourceIndexedSearchParamNumber> paramsNumber = new ArrayList<>();
|
||||
Collection<ResourceIndexedSearchParamNumber> existingNumberParams = new ArrayList<>();
|
||||
if (theEntity.isParamsNumberPopulated()) {
|
||||
paramsNumber.addAll(theEntity.getParamsNumber());
|
||||
existingNumberParams.addAll(theEntity.getParamsNumber());
|
||||
}
|
||||
Collection<ResourceIndexedSearchParamQuantity> paramsQuantity = new ArrayList<>();
|
||||
Collection<ResourceIndexedSearchParamQuantity> existingQuantityParams = new ArrayList<>();
|
||||
if (theEntity.isParamsQuantityPopulated()) {
|
||||
paramsQuantity.addAll(theEntity.getParamsQuantity());
|
||||
existingQuantityParams.addAll(theEntity.getParamsQuantity());
|
||||
}
|
||||
Collection<ResourceIndexedSearchParamDate> paramsDate = new ArrayList<>();
|
||||
Collection<ResourceIndexedSearchParamDate> existingDateParams = new ArrayList<>();
|
||||
if (theEntity.isParamsDatePopulated()) {
|
||||
paramsDate.addAll(theEntity.getParamsDate());
|
||||
existingDateParams.addAll(theEntity.getParamsDate());
|
||||
}
|
||||
Collection<ResourceIndexedSearchParamUri> paramsUri = new ArrayList<>();
|
||||
Collection<ResourceIndexedSearchParamUri> existingUriParams = new ArrayList<>();
|
||||
if (theEntity.isParamsUriPopulated()) {
|
||||
paramsUri.addAll(theEntity.getParamsUri());
|
||||
existingUriParams.addAll(theEntity.getParamsUri());
|
||||
}
|
||||
Collection<ResourceIndexedSearchParamCoords> paramsCoords = new ArrayList<>();
|
||||
Collection<ResourceIndexedSearchParamCoords> existingCoordsParams = new ArrayList<>();
|
||||
if (theEntity.isParamsCoordsPopulated()) {
|
||||
paramsCoords.addAll(theEntity.getParamsCoords());
|
||||
existingCoordsParams.addAll(theEntity.getParamsCoords());
|
||||
}
|
||||
Collection<ResourceLink> existingResourceLinks = new ArrayList<>();
|
||||
if (theEntity.isHasLinks()) {
|
||||
existingResourceLinks.addAll(theEntity.getResourceLinks());
|
||||
}
|
||||
Collection<ResourceIndexedCompositeStringUnique> existingCompositeStringUniques = new ArrayList<>();
|
||||
if (theEntity.isParamsCompositeStringUniquePresent()) {
|
||||
existingCompositeStringUniques.addAll(theEntity.getParamsCompositeStringUnique());
|
||||
}
|
||||
|
||||
Set<ResourceIndexedSearchParamString> stringParams = null;
|
||||
Set<ResourceIndexedSearchParamToken> tokenParams = null;
|
||||
|
@ -1331,6 +1407,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
Set<ResourceIndexedSearchParamDate> dateParams = null;
|
||||
Set<ResourceIndexedSearchParamUri> uriParams = null;
|
||||
Set<ResourceIndexedSearchParamCoords> coordsParams = null;
|
||||
Set<ResourceIndexedCompositeStringUnique> compositeStringUniques = null;
|
||||
Set<ResourceLink> links = null;
|
||||
|
||||
Set<String> populatedResourceLinkParameters = Collections.emptySet();
|
||||
|
@ -1365,10 +1442,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
uriParams = extractSearchParamUri(theEntity, theResource);
|
||||
coordsParams = extractSearchParamCoords(theEntity, theResource);
|
||||
|
||||
// ourLog.info("Indexing resource: {}", entity.getId());
|
||||
ourLog.trace("Storing date indexes: {}", dateParams);
|
||||
|
||||
tokenParams = new HashSet<ResourceIndexedSearchParamToken>();
|
||||
tokenParams = new HashSet<>();
|
||||
for (BaseResourceIndexedSearchParam next : extractSearchParamTokens(theEntity, theResource)) {
|
||||
if (next instanceof ResourceIndexedSearchParamToken) {
|
||||
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
|
||||
* matching
|
||||
* resource.
|
||||
* matching resource.
|
||||
*/
|
||||
if (myConfig.isAllowInlineMatchUrlReferences()) {
|
||||
FhirTerser terser = getContext().newTerser();
|
||||
|
@ -1443,13 +1518,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
}
|
||||
|
||||
links = new HashSet<ResourceLink>();
|
||||
links = new HashSet<>();
|
||||
populatedResourceLinkParameters = extractResourceLinks(theEntity, theResource, links, theUpdateTime);
|
||||
|
||||
/*
|
||||
* If the existing resource already has links and those match links we still want, use them instead of removing them and re adding them
|
||||
*/
|
||||
for (Iterator<ResourceLink> existingLinkIter = existingResourceLinks.iterator(); existingLinkIter.hasNext();) {
|
||||
for (Iterator<ResourceLink> existingLinkIter = existingResourceLinks.iterator(); existingLinkIter.hasNext(); ) {
|
||||
ResourceLink nextExisting = existingLinkIter.next();
|
||||
if (links.remove(nextExisting)) {
|
||||
existingLinkIter.remove();
|
||||
|
@ -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);
|
||||
|
||||
theEntity.setUpdated(theUpdateTime);
|
||||
|
@ -1479,6 +1560,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
theEntity.setParamsUriPopulated(uriParams.isEmpty() == false);
|
||||
theEntity.setParamsCoords(coordsParams);
|
||||
theEntity.setParamsCoordsPopulated(coordsParams.isEmpty() == false);
|
||||
theEntity.setParamsCompositeStringUnique(compositeStringUniques);
|
||||
theEntity.setParamsCompositeStringUniquePresent(compositeStringUniques.isEmpty() == false);
|
||||
theEntity.setResourceLinks(links);
|
||||
theEntity.setHasLinks(links.isEmpty() == false);
|
||||
theEntity.setIndexStatus(INDEX_STATUS_INDEXED);
|
||||
|
@ -1529,7 +1612,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
/*
|
||||
* Update the "search param present" table which is used for the
|
||||
* ?foo:missing=true queries
|
||||
*
|
||||
*
|
||||
* Note that we're only populating this for reference params
|
||||
* because the index tables for all other types have a MISSING column
|
||||
* right on them for handling the :missing queries. We can't use the
|
||||
|
@ -1567,28 +1650,28 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
*/
|
||||
if (thePerformIndexing) {
|
||||
|
||||
for (ResourceIndexedSearchParamString next : paramsString) {
|
||||
for (ResourceIndexedSearchParamString next : existingStringParams) {
|
||||
myEntityManager.remove(next);
|
||||
}
|
||||
for (ResourceIndexedSearchParamString next : stringParams) {
|
||||
myEntityManager.persist(next);
|
||||
}
|
||||
|
||||
for (ResourceIndexedSearchParamToken next : paramsToken) {
|
||||
for (ResourceIndexedSearchParamToken next : existingTokenParams) {
|
||||
myEntityManager.remove(next);
|
||||
}
|
||||
for (ResourceIndexedSearchParamToken next : tokenParams) {
|
||||
myEntityManager.persist(next);
|
||||
}
|
||||
|
||||
for (ResourceIndexedSearchParamNumber next : paramsNumber) {
|
||||
for (ResourceIndexedSearchParamNumber next : existingNumberParams) {
|
||||
myEntityManager.remove(next);
|
||||
}
|
||||
for (ResourceIndexedSearchParamNumber next : numberParams) {
|
||||
myEntityManager.persist(next);
|
||||
}
|
||||
|
||||
for (ResourceIndexedSearchParamQuantity next : paramsQuantity) {
|
||||
for (ResourceIndexedSearchParamQuantity next : existingQuantityParams) {
|
||||
myEntityManager.remove(next);
|
||||
}
|
||||
for (ResourceIndexedSearchParamQuantity next : quantityParams) {
|
||||
|
@ -1596,7 +1679,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
|
||||
// Store date SP's
|
||||
for (ResourceIndexedSearchParamDate next : paramsDate) {
|
||||
for (ResourceIndexedSearchParamDate next : existingDateParams) {
|
||||
myEntityManager.remove(next);
|
||||
}
|
||||
for (ResourceIndexedSearchParamDate next : dateParams) {
|
||||
|
@ -1604,7 +1687,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
|
||||
// Store URI SP's
|
||||
for (ResourceIndexedSearchParamUri next : paramsUri) {
|
||||
for (ResourceIndexedSearchParamUri next : existingUriParams) {
|
||||
myEntityManager.remove(next);
|
||||
}
|
||||
for (ResourceIndexedSearchParamUri next : uriParams) {
|
||||
|
@ -1612,7 +1695,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
|
||||
// Store Coords SP's
|
||||
for (ResourceIndexedSearchParamCoords next : paramsCoords) {
|
||||
for (ResourceIndexedSearchParamCoords next : existingCoordsParams) {
|
||||
myEntityManager.remove(next);
|
||||
}
|
||||
for (ResourceIndexedSearchParamCoords next : coordsParams) {
|
||||
|
@ -1629,6 +1712,14 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
// make sure links are indexed
|
||||
theEntity.setResourceLinks(links);
|
||||
|
||||
// Store composite string uniques
|
||||
for (ResourceIndexedCompositeStringUnique next : existingCompositeStringUniques) {
|
||||
myEntityManager.remove(next);
|
||||
}
|
||||
for (ResourceIndexedCompositeStringUnique next : compositeStringUniques) {
|
||||
myEntityManager.persist(next);
|
||||
}
|
||||
|
||||
theEntity.toString();
|
||||
|
||||
} // if thePerformIndexing
|
||||
|
@ -1693,7 +1784,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
if (!referencedId.getValue().contains("?")) {
|
||||
if (!validTypes.contains(referencedId.getResourceType())) {
|
||||
throw new UnprocessableEntityException(
|
||||
"Invalid reference found at path '" + newPath + "'. Resource type '" + referencedId.getResourceType() + "' is not valid for this path");
|
||||
"Invalid reference found at path '" + newPath + "'. Resource type '" + referencedId.getResourceType() + "' is not valid for this path");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1740,17 +1831,15 @@ 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
|
||||
* "subsetted" tag and rejects resources which have it. Subclasses should call the superclass implementation to preserve this check.
|
||||
*
|
||||
* @param theResource
|
||||
* The resource that is about to be persisted
|
||||
* @param theEntityToSave
|
||||
* TODO
|
||||
*
|
||||
* @param theResource The resource that is about to be persisted
|
||||
* @param theEntityToSave TODO
|
||||
*/
|
||||
protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) {
|
||||
Object tag = null;
|
||||
|
||||
|
||||
int totalMetaCount = 0;
|
||||
|
||||
|
||||
if (theResource instanceof IResource) {
|
||||
IResource res = (IResource) theResource;
|
||||
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(res);
|
||||
|
@ -1778,7 +1867,77 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
validateChildReferences(theResource, resName);
|
||||
|
||||
validateMetaCount(totalMetaCount);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
@ -1990,7 +2149,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
RuntimeSearchParam paramDef = theCallingDao.getSearchParamByName(resourceDef, nextParamName);
|
||||
if (paramDef == null) {
|
||||
throw new InvalidRequestException(
|
||||
"Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName);
|
||||
"Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName);
|
||||
}
|
||||
|
||||
IQueryParameterAnd<?> param = ParameterUtil.parseQueryParams(theContext, paramDef, nextParamName, paramList);
|
||||
|
@ -2023,7 +2182,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
public static void validateResourceType(BaseHasResource theEntity, String theResourceName) {
|
||||
if (!theResourceName.equals(theEntity.getResourceType())) {
|
||||
throw new ResourceNotFoundException(
|
||||
"Resource with ID " + theEntity.getIdDt().getIdPart() + " exists but it is not of type " + theResourceName + ", found resource of type " + theEntity.getResourceType());
|
||||
"Resource with ID " + theEntity.getIdDt().getIdPart() + " exists but it is not of type " + theResourceName + ", found resource of type " + theEntity.getResourceType());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,10 @@ import ca.uhn.fhir.jpa.util.DeleteConflict;
|
|||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
|
||||
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.rest.api.PatchTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.QualifiedParamList;
|
||||
|
@ -384,7 +387,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
// Notify JPA interceptors
|
||||
if (theRequestDetails != null) {
|
||||
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), theResource);
|
||||
theRequestDetails.getRequestOperationCallback().resourceCreated(theResource);
|
||||
}
|
||||
for (IServerInterceptor next : getConfig().getInterceptors()) {
|
||||
|
@ -871,8 +873,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
throw new ResourceNotFoundException(theId);
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
for (BaseTag next : new ArrayList<BaseTag>(entity.getTags())) {
|
||||
for (BaseTag next : new ArrayList<>(entity.getTags())) {
|
||||
if (ObjectUtil.equals(next.getTag().getTagType(), theTagType) &&
|
||||
ObjectUtil.equals(next.getTag().getSystem(), theScheme) &&
|
||||
ObjectUtil.equals(next.getTag().getCode(), theTerm)) {
|
||||
|
@ -880,7 +881,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
entity.getTags().remove(next);
|
||||
}
|
||||
}
|
||||
//@formatter:on
|
||||
|
||||
if (entity.getTags().isEmpty()) {
|
||||
entity.setHasTags(false);
|
||||
|
|
|
@ -20,27 +20,28 @@ package ca.uhn.fhir.jpa.dao;
|
|||
* #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.RuntimeResourceDefinition;
|
||||
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 {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(BaseSearchParamRegistry.class);
|
||||
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
|
||||
private FhirContext myCtx;
|
||||
|
||||
@Autowired
|
||||
private Collection<IFhirResourceDao<?>> myDaos;
|
||||
|
||||
|
@ -75,18 +76,119 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
|
|||
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() {
|
||||
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
|
||||
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) {
|
||||
RuntimeResourceDefinition nextResDef = myCtx.getResourceDefinition(nextDao.getResourceType());
|
||||
String nextResourceName = nextResDef.getName();
|
||||
HashMap<String, RuntimeSearchParam> nameToParam = new HashMap<String, RuntimeSearchParam>();
|
||||
HashMap<String, RuntimeSearchParam> nameToParam = new HashMap<>();
|
||||
resourceNameToSearchParams.put(nextResourceName, nameToParam);
|
||||
|
||||
for (RuntimeSearchParam nextSp : nextResDef.getSearchParams()) {
|
||||
|
@ -97,4 +199,6 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
|
|||
myBuiltInSearchParams = Collections.unmodifiableMap(resourceNameToSearchParams);
|
||||
}
|
||||
|
||||
protected abstract void refreshCacheIfNecessary();
|
||||
|
||||
}
|
||||
|
|
|
@ -20,21 +20,27 @@ package ca.uhn.fhir.jpa.dao;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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 {
|
||||
|
||||
void forceRefresh();
|
||||
|
||||
Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams();
|
||||
|
||||
Map<String,RuntimeSearchParam> getActiveSearchParams(String theResourceName);
|
||||
|
||||
/**
|
||||
* @return Returns {@literal null} if no match
|
||||
*/
|
||||
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.
|
||||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.*;
|
||||
import javax.persistence.criteria.CriteriaBuilder.In;
|
||||
|
||||
import org.apache.commons.lang3.*;
|
||||
import ca.uhn.fhir.context.*;
|
||||
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.search.JpaRuntimeSearchParam;
|
||||
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.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.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
@ -39,28 +60,20 @@ import org.hibernate.ScrollMode;
|
|||
import org.hibernate.ScrollableResults;
|
||||
import org.hibernate.query.Query;
|
||||
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 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;
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
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 Long NO_MORE = Long.valueOf(-1);
|
||||
private static HandlerTypeEnum ourLastHandlerMechanismForUnitTest;
|
||||
private List<Long> myAlsoIncludePids;
|
||||
private CriteriaBuilder myBuilder;
|
||||
private BaseHapiFhirDao<?> myCallingDao;
|
||||
|
@ -94,8 +108,8 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
* Constructor
|
||||
*/
|
||||
public SearchBuilder(FhirContext theFhirContext, EntityManager theEntityManager, IFulltextSearchSvc theFulltextSearchSvc,
|
||||
BaseHapiFhirDao<?> theDao,
|
||||
IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao, IForcedIdDao theForcedIdDao, IHapiTerminologySvc theTerminologySvc, ISearchParamRegistry theSearchParamRegistry) {
|
||||
BaseHapiFhirDao<?> theDao,
|
||||
IResourceIndexedSearchParamUriDao theResourceIndexedSearchParamUriDao, IForcedIdDao theForcedIdDao, IHapiTerminologySvc theTerminologySvc, ISearchParamRegistry theSearchParamRegistry) {
|
||||
myContext = theFhirContext;
|
||||
myEntityManager = theEntityManager;
|
||||
myFulltextSearchSvc = theFulltextSearchSvc;
|
||||
|
@ -135,7 +149,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
return;
|
||||
}
|
||||
|
||||
List<Predicate> codePredicates = new ArrayList<Predicate>();
|
||||
List<Predicate> codePredicates = new ArrayList<>();
|
||||
for (IQueryParameterType nextOr : theList) {
|
||||
IQueryParameterType params = nextOr;
|
||||
Predicate p = createPredicateDate(params, theResourceName, theParamName, myBuilder, join);
|
||||
|
@ -358,7 +372,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
if (!ref.getValue().matches("[a-zA-Z]+\\/.*")) {
|
||||
|
||||
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam(theResourceName, theParamName);
|
||||
resourceTypes = new ArrayList<Class<? extends IBaseResource>>();
|
||||
resourceTypes = new ArrayList<>();
|
||||
|
||||
Set<String> targetTypes = param.getTargets();
|
||||
|
||||
|
@ -457,7 +471,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
IQueryParameterType chainValue;
|
||||
if (remainingChain != null) {
|
||||
if (param == null || param.getParamType() != RestSearchParameterTypeEnum.REFERENCE) {
|
||||
ourLog.debug("Type {} parameter {} is not a reference, can not chain {}", new Object[] { nextType.getSimpleName(), chain, remainingChain });
|
||||
ourLog.debug("Type {} parameter {} is not a reference, can not chain {}", new Object[]{nextType.getSimpleName(), chain, remainingChain});
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -785,7 +799,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
*/
|
||||
ourLog.info("Searching for candidate URI:above parameters for Resource[{}] param[{}]", myResourceName, theParamName);
|
||||
Collection<String> candidates = myResourceIndexedSearchParamUriDao.findAllByResourceTypeAndParamName(myResourceName, theParamName);
|
||||
List<String> toFind = new ArrayList<String>();
|
||||
List<String> toFind = new ArrayList<>();
|
||||
for (String next : candidates) {
|
||||
if (value.length() >= next.length()) {
|
||||
if (value.substring(0, next.length()).equals(next)) {
|
||||
|
@ -798,12 +812,12 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
continue;
|
||||
}
|
||||
|
||||
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) {
|
||||
predicate = myBuilder.like(join.<Object> get("myUri").as(String.class), createLeftMatchLikeExpression(value));
|
||||
predicate = myBuilder.like(join.get("myUri").as(String.class), createLeftMatchLikeExpression(value));
|
||||
} 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);
|
||||
} else {
|
||||
|
@ -817,7 +831,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
* just add a predicate that can never match
|
||||
*/
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
@ -840,32 +854,32 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
private Predicate createCompositeParamPart(String theResourceName, Root<ResourceTable> theRoot, RuntimeSearchParam theParam, IQueryParameterType leftValue) {
|
||||
Predicate retVal = null;
|
||||
switch (theParam.getParamType()) {
|
||||
case STRING: {
|
||||
From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> stringJoin = theRoot.join("myParamsString", JoinType.INNER);
|
||||
retVal = createPredicateString(leftValue, theResourceName, theParam.getName(), myBuilder, stringJoin);
|
||||
break;
|
||||
}
|
||||
case TOKEN: {
|
||||
From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> tokenJoin = theRoot.join("myParamsToken", JoinType.INNER);
|
||||
retVal = createPredicateToken(leftValue, theResourceName, theParam.getName(), myBuilder, tokenJoin);
|
||||
break;
|
||||
}
|
||||
case DATE: {
|
||||
From<ResourceIndexedSearchParamDate, ResourceIndexedSearchParamDate> dateJoin = theRoot.join("myParamsDate", JoinType.INNER);
|
||||
retVal = createPredicateDate(leftValue, theResourceName, theParam.getName(), myBuilder, dateJoin);
|
||||
break;
|
||||
}
|
||||
case QUANTITY: {
|
||||
From<ResourceIndexedSearchParamQuantity, ResourceIndexedSearchParamQuantity> dateJoin = theRoot.join("myParamsQuantity", JoinType.INNER);
|
||||
retVal = createPredicateQuantity(leftValue, theResourceName, theParam.getName(), myBuilder, dateJoin);
|
||||
break;
|
||||
}
|
||||
case COMPOSITE:
|
||||
case HAS:
|
||||
case NUMBER:
|
||||
case REFERENCE:
|
||||
case URI:
|
||||
break;
|
||||
case STRING: {
|
||||
From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> stringJoin = theRoot.join("myParamsString", JoinType.INNER);
|
||||
retVal = createPredicateString(leftValue, theResourceName, theParam.getName(), myBuilder, stringJoin);
|
||||
break;
|
||||
}
|
||||
case TOKEN: {
|
||||
From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> tokenJoin = theRoot.join("myParamsToken", JoinType.INNER);
|
||||
retVal = createPredicateToken(leftValue, theResourceName, theParam.getName(), myBuilder, tokenJoin);
|
||||
break;
|
||||
}
|
||||
case DATE: {
|
||||
From<ResourceIndexedSearchParamDate, ResourceIndexedSearchParamDate> dateJoin = theRoot.join("myParamsDate", JoinType.INNER);
|
||||
retVal = createPredicateDate(leftValue, theResourceName, theParam.getName(), myBuilder, dateJoin);
|
||||
break;
|
||||
}
|
||||
case QUANTITY: {
|
||||
From<ResourceIndexedSearchParamQuantity, ResourceIndexedSearchParamQuantity> dateJoin = theRoot.join("myParamsQuantity", JoinType.INNER);
|
||||
retVal = createPredicateQuantity(leftValue, theResourceName, theParam.getName(), myBuilder, dateJoin);
|
||||
break;
|
||||
}
|
||||
case COMPOSITE:
|
||||
case HAS:
|
||||
case NUMBER:
|
||||
case REFERENCE:
|
||||
case URI:
|
||||
break;
|
||||
}
|
||||
|
||||
if (retVal == null) {
|
||||
|
@ -880,27 +894,27 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
Join<ResourceTable, ResourceIndexedSearchParamDate> join = null;
|
||||
|
||||
switch (theType) {
|
||||
case DATE:
|
||||
join = myResourceTableRoot.join("myParamsDate", JoinType.LEFT);
|
||||
break;
|
||||
case NUMBER:
|
||||
join = myResourceTableRoot.join("myParamsNumber", JoinType.LEFT);
|
||||
break;
|
||||
case QUANTITY:
|
||||
join = myResourceTableRoot.join("myParamsQuantity", JoinType.LEFT);
|
||||
break;
|
||||
case REFERENCE:
|
||||
join = myResourceTableRoot.join("myResourceLinks", JoinType.LEFT);
|
||||
break;
|
||||
case STRING:
|
||||
join = myResourceTableRoot.join("myParamsString", JoinType.LEFT);
|
||||
break;
|
||||
case URI:
|
||||
join = myResourceTableRoot.join("myParamsUri", JoinType.LEFT);
|
||||
break;
|
||||
case TOKEN:
|
||||
join = myResourceTableRoot.join("myParamsToken", JoinType.LEFT);
|
||||
break;
|
||||
case DATE:
|
||||
join = myResourceTableRoot.join("myParamsDate", JoinType.LEFT);
|
||||
break;
|
||||
case NUMBER:
|
||||
join = myResourceTableRoot.join("myParamsNumber", JoinType.LEFT);
|
||||
break;
|
||||
case QUANTITY:
|
||||
join = myResourceTableRoot.join("myParamsQuantity", JoinType.LEFT);
|
||||
break;
|
||||
case REFERENCE:
|
||||
join = myResourceTableRoot.join("myResourceLinks", JoinType.LEFT);
|
||||
break;
|
||||
case STRING:
|
||||
join = myResourceTableRoot.join("myParamsString", JoinType.LEFT);
|
||||
break;
|
||||
case URI:
|
||||
join = myResourceTableRoot.join("myParamsUri", JoinType.LEFT);
|
||||
break;
|
||||
case TOKEN:
|
||||
join = myResourceTableRoot.join("myParamsToken", JoinType.LEFT);
|
||||
break;
|
||||
}
|
||||
|
||||
JoinKey key = new JoinKey(theSearchParameterName, theType);
|
||||
|
@ -938,8 +952,8 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
Predicate lb = null;
|
||||
if (lowerBound != null) {
|
||||
Predicate gt = theBuilder.greaterThanOrEqualTo(theFrom.<Date> get("myValueLow"), lowerBound);
|
||||
Predicate lt = theBuilder.greaterThanOrEqualTo(theFrom.<Date> get("myValueHigh"), lowerBound);
|
||||
Predicate gt = theBuilder.greaterThanOrEqualTo(theFrom.<Date>get("myValueLow"), lowerBound);
|
||||
Predicate lt = theBuilder.greaterThanOrEqualTo(theFrom.<Date>get("myValueHigh"), lowerBound);
|
||||
if (theRange.getLowerBound().getPrefix() == ParamPrefixEnum.STARTS_AFTER || theRange.getLowerBound().getPrefix() == ParamPrefixEnum.EQUAL) {
|
||||
lb = gt;
|
||||
} else {
|
||||
|
@ -949,8 +963,8 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
Predicate ub = null;
|
||||
if (upperBound != null) {
|
||||
Predicate gt = theBuilder.lessThanOrEqualTo(theFrom.<Date> get("myValueLow"), upperBound);
|
||||
Predicate lt = theBuilder.lessThanOrEqualTo(theFrom.<Date> get("myValueHigh"), upperBound);
|
||||
Predicate gt = theBuilder.lessThanOrEqualTo(theFrom.<Date>get("myValueLow"), upperBound);
|
||||
Predicate lt = theBuilder.lessThanOrEqualTo(theFrom.<Date>get("myValueHigh"), upperBound);
|
||||
if (theRange.getUpperBound().getPrefix() == ParamPrefixEnum.ENDS_BEFORE || theRange.getUpperBound().getPrefix() == ParamPrefixEnum.EQUAL) {
|
||||
ub = lt;
|
||||
} else {
|
||||
|
@ -968,45 +982,45 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
private Predicate createPredicateNumeric(String theResourceName, String theParamName, From<?, ? extends BaseResourceIndexedSearchParam> theFrom, CriteriaBuilder builder,
|
||||
IQueryParameterType theParam, ParamPrefixEnum thePrefix, BigDecimal theValue, final Expression<BigDecimal> thePath,
|
||||
String invalidMessageName) {
|
||||
IQueryParameterType theParam, ParamPrefixEnum thePrefix, BigDecimal theValue, final Expression<BigDecimal> thePath,
|
||||
String invalidMessageName) {
|
||||
Predicate num;
|
||||
switch (thePrefix) {
|
||||
case GREATERTHAN:
|
||||
num = builder.gt(thePath, theValue);
|
||||
break;
|
||||
case GREATERTHAN_OR_EQUALS:
|
||||
num = builder.ge(thePath, theValue);
|
||||
break;
|
||||
case LESSTHAN:
|
||||
num = builder.lt(thePath, theValue);
|
||||
break;
|
||||
case LESSTHAN_OR_EQUALS:
|
||||
num = builder.le(thePath, theValue);
|
||||
break;
|
||||
case APPROXIMATE:
|
||||
case EQUAL:
|
||||
case NOT_EQUAL:
|
||||
BigDecimal mul = calculateFuzzAmount(thePrefix, theValue);
|
||||
BigDecimal low = theValue.subtract(mul, MathContext.DECIMAL64);
|
||||
BigDecimal high = theValue.add(mul, MathContext.DECIMAL64);
|
||||
Predicate lowPred;
|
||||
Predicate highPred;
|
||||
if (thePrefix != ParamPrefixEnum.NOT_EQUAL) {
|
||||
lowPred = builder.ge(thePath.as(BigDecimal.class), low);
|
||||
highPred = builder.le(thePath.as(BigDecimal.class), high);
|
||||
num = builder.and(lowPred, highPred);
|
||||
ourLog.trace("Searching for {} <= val <= {}", low, high);
|
||||
} else {
|
||||
// Prefix was "ne", so reverse it!
|
||||
lowPred = builder.lt(thePath.as(BigDecimal.class), low);
|
||||
highPred = builder.gt(thePath.as(BigDecimal.class), high);
|
||||
num = builder.or(lowPred, highPred);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
String msg = myContext.getLocalizer().getMessage(SearchBuilder.class, invalidMessageName, thePrefix.getValue(), theParam.getValueAsQueryToken(myContext));
|
||||
throw new InvalidRequestException(msg);
|
||||
case GREATERTHAN:
|
||||
num = builder.gt(thePath, theValue);
|
||||
break;
|
||||
case GREATERTHAN_OR_EQUALS:
|
||||
num = builder.ge(thePath, theValue);
|
||||
break;
|
||||
case LESSTHAN:
|
||||
num = builder.lt(thePath, theValue);
|
||||
break;
|
||||
case LESSTHAN_OR_EQUALS:
|
||||
num = builder.le(thePath, theValue);
|
||||
break;
|
||||
case APPROXIMATE:
|
||||
case EQUAL:
|
||||
case NOT_EQUAL:
|
||||
BigDecimal mul = calculateFuzzAmount(thePrefix, theValue);
|
||||
BigDecimal low = theValue.subtract(mul, MathContext.DECIMAL64);
|
||||
BigDecimal high = theValue.add(mul, MathContext.DECIMAL64);
|
||||
Predicate lowPred;
|
||||
Predicate highPred;
|
||||
if (thePrefix != ParamPrefixEnum.NOT_EQUAL) {
|
||||
lowPred = builder.ge(thePath.as(BigDecimal.class), low);
|
||||
highPred = builder.le(thePath.as(BigDecimal.class), high);
|
||||
num = builder.and(lowPred, highPred);
|
||||
ourLog.trace("Searching for {} <= val <= {}", low, high);
|
||||
} else {
|
||||
// Prefix was "ne", so reverse it!
|
||||
lowPred = builder.lt(thePath.as(BigDecimal.class), low);
|
||||
highPred = builder.gt(thePath.as(BigDecimal.class), high);
|
||||
num = builder.or(lowPred, highPred);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
String msg = myContext.getLocalizer().getMessage(SearchBuilder.class, invalidMessageName, thePrefix.getValue(), theParam.getValueAsQueryToken(myContext));
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
if (theParamName == null) {
|
||||
|
@ -1016,7 +1030,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
private Predicate createPredicateQuantity(IQueryParameterType theParam, String theResourceName, String theParamName, CriteriaBuilder theBuilder,
|
||||
From<?, ResourceIndexedSearchParamQuantity> theFrom) {
|
||||
From<?, ResourceIndexedSearchParamQuantity> theFrom) {
|
||||
String systemValue;
|
||||
String unitsValue;
|
||||
ParamPrefixEnum cmpValue;
|
||||
|
@ -1069,7 +1083,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
private Predicate createPredicateString(IQueryParameterType theParameter, String theResourceName, String theParamName, CriteriaBuilder theBuilder,
|
||||
From<?, ResourceIndexedSearchParamString> theFrom) {
|
||||
From<?, ResourceIndexedSearchParamString> theFrom) {
|
||||
String rawSearchTerm;
|
||||
if (theParameter instanceof TokenParam) {
|
||||
TokenParam id = (TokenParam) theParameter;
|
||||
|
@ -1089,7 +1103,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
if (rawSearchTerm.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||
throw new InvalidRequestException("Parameter[" + theParamName + "] has length (" + rawSearchTerm.length() + ") that is longer than maximum allowed ("
|
||||
+ ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm);
|
||||
+ ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm);
|
||||
}
|
||||
|
||||
String likeExpression = BaseHapiFhirDao.normalizeString(rawSearchTerm);
|
||||
|
@ -1121,7 +1135,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
private Predicate createPredicateToken(IQueryParameterType theParameter, String theResourceName, String theParamName, CriteriaBuilder theBuilder,
|
||||
From<?, ResourceIndexedSearchParamToken> theFrom) {
|
||||
From<?, ResourceIndexedSearchParamToken> theFrom) {
|
||||
String code;
|
||||
String system;
|
||||
TokenParamModifier modifier = null;
|
||||
|
@ -1144,12 +1158,12 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
if (system != null && system.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
throw new InvalidRequestException(
|
||||
"Parameter[" + theParamName + "] has system (" + system.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + system);
|
||||
"Parameter[" + theParamName + "] has system (" + system.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + system);
|
||||
}
|
||||
|
||||
if (code != null && code.length() > ResourceIndexedSearchParamToken.MAX_LENGTH) {
|
||||
throw new InvalidRequestException(
|
||||
"Parameter[" + theParamName + "] has code (" + code.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + code);
|
||||
"Parameter[" + theParamName + "] has code (" + code.length() + ") that is longer than maximum allowed (" + ResourceIndexedSearchParamToken.MAX_LENGTH + "): " + code);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1240,6 +1254,45 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
myBuilder = myEntityManager.getCriteriaBuilder();
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -1334,7 +1387,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
/*
|
||||
* Add a predicate to make sure we only include non-deleted resources, and only include
|
||||
* resources of the right type.
|
||||
*
|
||||
*
|
||||
* If we have any joins to index tables, we get this behaviour already guaranteed so we don't
|
||||
* need an explicit predicate for it.
|
||||
*/
|
||||
|
@ -1370,7 +1423,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
/**
|
||||
* @return Returns {@literal true} if any search parameter sorts were found, or false if
|
||||
* no sorts were found, or only non-search parameters ones (e.g. _id, _lastUpdated)
|
||||
* no sorts were found, or only non-search parameters ones (e.g. _id, _lastUpdated)
|
||||
*/
|
||||
private boolean createSort(CriteriaBuilder theBuilder, Root<ResourceTable> theFrom, SortSpec theSort, List<Order> theOrders, List<Predicate> thePredicates) {
|
||||
if (theSort == null || isBlank(theSort.getParamName())) {
|
||||
|
@ -1411,43 +1464,43 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
JoinEnum joinType;
|
||||
|
||||
switch (param.getParamType()) {
|
||||
case STRING:
|
||||
joinAttrName = "myParamsString";
|
||||
sortAttrName = new String[] { "myValueExact" };
|
||||
joinType = JoinEnum.STRING;
|
||||
break;
|
||||
case DATE:
|
||||
joinAttrName = "myParamsDate";
|
||||
sortAttrName = new String[] { "myValueLow" };
|
||||
joinType = JoinEnum.DATE;
|
||||
break;
|
||||
case REFERENCE:
|
||||
joinAttrName = "myResourceLinks";
|
||||
sortAttrName = new String[] { "myTargetResourcePid" };
|
||||
joinType = JoinEnum.REFERENCE;
|
||||
break;
|
||||
case TOKEN:
|
||||
joinAttrName = "myParamsToken";
|
||||
sortAttrName = new String[] { "mySystem", "myValue" };
|
||||
joinType = JoinEnum.TOKEN;
|
||||
break;
|
||||
case NUMBER:
|
||||
joinAttrName = "myParamsNumber";
|
||||
sortAttrName = new String[] { "myValue" };
|
||||
joinType = JoinEnum.NUMBER;
|
||||
break;
|
||||
case URI:
|
||||
joinAttrName = "myParamsUri";
|
||||
sortAttrName = new String[] { "myUri" };
|
||||
joinType = JoinEnum.URI;
|
||||
break;
|
||||
case QUANTITY:
|
||||
joinAttrName = "myParamsQuantity";
|
||||
sortAttrName = new String[] { "myValue" };
|
||||
joinType = JoinEnum.QUANTITY;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidRequestException("This server does not support _sort specifications of type " + param.getParamType() + " - Can't serve _sort=" + theSort.getParamName());
|
||||
case STRING:
|
||||
joinAttrName = "myParamsString";
|
||||
sortAttrName = new String[]{"myValueExact"};
|
||||
joinType = JoinEnum.STRING;
|
||||
break;
|
||||
case DATE:
|
||||
joinAttrName = "myParamsDate";
|
||||
sortAttrName = new String[]{"myValueLow"};
|
||||
joinType = JoinEnum.DATE;
|
||||
break;
|
||||
case REFERENCE:
|
||||
joinAttrName = "myResourceLinks";
|
||||
sortAttrName = new String[]{"myTargetResourcePid"};
|
||||
joinType = JoinEnum.REFERENCE;
|
||||
break;
|
||||
case TOKEN:
|
||||
joinAttrName = "myParamsToken";
|
||||
sortAttrName = new String[]{"mySystem", "myValue"};
|
||||
joinType = JoinEnum.TOKEN;
|
||||
break;
|
||||
case NUMBER:
|
||||
joinAttrName = "myParamsNumber";
|
||||
sortAttrName = new String[]{"myValue"};
|
||||
joinType = JoinEnum.NUMBER;
|
||||
break;
|
||||
case URI:
|
||||
joinAttrName = "myParamsUri";
|
||||
sortAttrName = new String[]{"myUri"};
|
||||
joinType = JoinEnum.URI;
|
||||
break;
|
||||
case QUANTITY:
|
||||
joinAttrName = "myParamsQuantity";
|
||||
sortAttrName = new String[]{"myValue"};
|
||||
joinType = JoinEnum.QUANTITY;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidRequestException("This server does not support _sort specifications of type " + param.getParamType() + " - Can't serve _sort=" + theSort.getParamName());
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1513,7 +1566,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
private void doLoadPids(List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation, EntityManager entityManager, FhirContext context, IDao theDao,
|
||||
Map<Long, Integer> position, Collection<Long> pids) {
|
||||
Map<Long, Integer> position, Collection<Long> pids) {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ResourceTable> cq = builder.createQuery(ResourceTable.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
|
@ -1549,7 +1602,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
@Override
|
||||
public void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation,
|
||||
EntityManager entityManager, FhirContext context, IDao theDao) {
|
||||
EntityManager entityManager, FhirContext context, IDao theDao) {
|
||||
if (theIncludePids.isEmpty()) {
|
||||
ourLog.info("The include pids are empty");
|
||||
// return;
|
||||
|
@ -1557,7 +1610,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
// Dupes will cause a crash later anyhow, but this is expensive so only do it
|
||||
// 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>();
|
||||
for (Long next : theIncludePids) {
|
||||
|
@ -1572,7 +1625,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
* but this should work too. Sigh.
|
||||
*/
|
||||
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) {
|
||||
int to = i + maxLoad;
|
||||
to = Math.min(to, pids.size());
|
||||
|
@ -1589,7 +1642,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
*/
|
||||
@Override
|
||||
public HashSet<Long> loadReverseIncludes(IDao theCallingDao, FhirContext theContext, EntityManager theEntityManager, Collection<Long> theMatches, Set<Include> theRevIncludes,
|
||||
boolean theReverseMode, DateRangeParam theLastUpdated) {
|
||||
boolean theReverseMode, DateRangeParam theLastUpdated) {
|
||||
if (theMatches.size() == 0) {
|
||||
return new HashSet<Long>();
|
||||
}
|
||||
|
@ -1599,9 +1652,9 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
String searchFieldName = theReverseMode ? "myTargetResourcePid" : "mySourceResourcePid";
|
||||
|
||||
Collection<Long> nextRoundMatches = theMatches;
|
||||
HashSet<Long> allAdded = new HashSet<Long>();
|
||||
HashSet<Long> original = new HashSet<Long>(theMatches);
|
||||
ArrayList<Include> includes = new ArrayList<Include>(theRevIncludes);
|
||||
HashSet<Long> allAdded = new HashSet<>();
|
||||
HashSet<Long> original = new HashSet<>(theMatches);
|
||||
ArrayList<Include> includes = new ArrayList<>(theRevIncludes);
|
||||
|
||||
int roundCounts = 0;
|
||||
StopWatch w = new StopWatch();
|
||||
|
@ -1610,10 +1663,10 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
do {
|
||||
roundCounts++;
|
||||
|
||||
HashSet<Long> pidsToInclude = new HashSet<Long>();
|
||||
Set<Long> nextRoundOmit = new HashSet<Long>();
|
||||
HashSet<Long> pidsToInclude = new HashSet<>();
|
||||
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();
|
||||
if (nextInclude.isRecurse() == false) {
|
||||
iter.remove();
|
||||
|
@ -1712,7 +1765,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
nextRoundMatches = pidsToInclude;
|
||||
} while (includes.size() > 0 && nextRoundMatches.size() > 0 && addedSomeThisRound);
|
||||
|
||||
ourLog.info("Loaded {} {} in {} rounds and {} ms", new Object[] { allAdded.size(), theReverseMode ? "_revincludes" : "_includes", roundCounts, w.getMillisAndRestart() });
|
||||
ourLog.info("Loaded {} {} in {} rounds and {} ms", new Object[]{allAdded.size(), theReverseMode ? "_revincludes" : "_includes", roundCounts, w.getMillisAndRestart()});
|
||||
|
||||
return allAdded;
|
||||
}
|
||||
|
@ -1788,49 +1841,49 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
RuntimeSearchParam nextParamDef = mySearchParamRegistry.getActiveSearchParam(theResourceName, theParamName);
|
||||
if (nextParamDef != null) {
|
||||
switch (nextParamDef.getParamType()) {
|
||||
case DATE:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateDate(theResourceName, theParamName, nextAnd);
|
||||
}
|
||||
break;
|
||||
case QUANTITY:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateQuantity(theResourceName, theParamName, nextAnd);
|
||||
}
|
||||
break;
|
||||
case REFERENCE:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateReference(theResourceName, theParamName, nextAnd);
|
||||
}
|
||||
break;
|
||||
case STRING:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateString(theResourceName, theParamName, nextAnd);
|
||||
}
|
||||
break;
|
||||
case TOKEN:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateToken(theResourceName, theParamName, nextAnd);
|
||||
}
|
||||
break;
|
||||
case NUMBER:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateNumber(theResourceName, theParamName, nextAnd);
|
||||
}
|
||||
break;
|
||||
case COMPOSITE:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateComposite(theResourceName, nextParamDef, nextAnd);
|
||||
}
|
||||
break;
|
||||
case URI:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateUri(theResourceName, theParamName, nextAnd);
|
||||
}
|
||||
break;
|
||||
case HAS:
|
||||
// should not happen
|
||||
break;
|
||||
case DATE:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateDate(theResourceName, theParamName, nextAnd);
|
||||
}
|
||||
break;
|
||||
case QUANTITY:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateQuantity(theResourceName, theParamName, nextAnd);
|
||||
}
|
||||
break;
|
||||
case REFERENCE:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateReference(theResourceName, theParamName, nextAnd);
|
||||
}
|
||||
break;
|
||||
case STRING:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateString(theResourceName, theParamName, nextAnd);
|
||||
}
|
||||
break;
|
||||
case TOKEN:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateToken(theResourceName, theParamName, nextAnd);
|
||||
}
|
||||
break;
|
||||
case NUMBER:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateNumber(theResourceName, theParamName, nextAnd);
|
||||
}
|
||||
break;
|
||||
case COMPOSITE:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateComposite(theResourceName, nextParamDef, nextAnd);
|
||||
}
|
||||
break;
|
||||
case URI:
|
||||
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
|
||||
addPredicateUri(theResourceName, theParamName, nextAnd);
|
||||
}
|
||||
break;
|
||||
case HAS:
|
||||
// should not happen
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (Constants.PARAM_CONTENT.equals(theParamName) || Constants.PARAM_TEXT.equals(theParamName)) {
|
||||
|
@ -1851,35 +1904,35 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
private IQueryParameterType toParameterType(RuntimeSearchParam theParam) {
|
||||
IQueryParameterType qp;
|
||||
switch (theParam.getParamType()) {
|
||||
case DATE:
|
||||
qp = new DateParam();
|
||||
break;
|
||||
case NUMBER:
|
||||
qp = new NumberParam();
|
||||
break;
|
||||
case QUANTITY:
|
||||
qp = new QuantityParam();
|
||||
break;
|
||||
case STRING:
|
||||
qp = new StringParam();
|
||||
break;
|
||||
case TOKEN:
|
||||
qp = new TokenParam();
|
||||
break;
|
||||
case COMPOSITE:
|
||||
List<RuntimeSearchParam> compositeOf = theParam.getCompositeOf();
|
||||
if (compositeOf.size() != 2) {
|
||||
throw new InternalErrorException("Parameter " + theParam.getName() + " has " + compositeOf.size() + " composite parts. Don't know how handlt this.");
|
||||
}
|
||||
IQueryParameterType leftParam = toParameterType(compositeOf.get(0));
|
||||
IQueryParameterType rightParam = toParameterType(compositeOf.get(1));
|
||||
qp = new CompositeParam<IQueryParameterType, IQueryParameterType>(leftParam, rightParam);
|
||||
break;
|
||||
case REFERENCE:
|
||||
qp = new ReferenceParam();
|
||||
break;
|
||||
default:
|
||||
throw new InternalErrorException("Don't know how to convert param type: " + theParam.getParamType());
|
||||
case DATE:
|
||||
qp = new DateParam();
|
||||
break;
|
||||
case NUMBER:
|
||||
qp = new NumberParam();
|
||||
break;
|
||||
case QUANTITY:
|
||||
qp = new QuantityParam();
|
||||
break;
|
||||
case STRING:
|
||||
qp = new StringParam();
|
||||
break;
|
||||
case TOKEN:
|
||||
qp = new TokenParam();
|
||||
break;
|
||||
case COMPOSITE:
|
||||
List<RuntimeSearchParam> compositeOf = theParam.getCompositeOf();
|
||||
if (compositeOf.size() != 2) {
|
||||
throw new InternalErrorException("Parameter " + theParam.getName() + " has " + compositeOf.size() + " composite parts. Don't know how handlt this.");
|
||||
}
|
||||
IQueryParameterType leftParam = toParameterType(compositeOf.get(0));
|
||||
IQueryParameterType rightParam = toParameterType(compositeOf.get(1));
|
||||
qp = new CompositeParam<IQueryParameterType, IQueryParameterType>(leftParam, rightParam);
|
||||
break;
|
||||
case REFERENCE:
|
||||
qp = new ReferenceParam();
|
||||
break;
|
||||
default:
|
||||
throw new InternalErrorException("Don't know how to convert param type: " + theParam.getParamType());
|
||||
}
|
||||
return qp;
|
||||
}
|
||||
|
@ -1918,11 +1971,11 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
if (theLastUpdated != null) {
|
||||
if (theLastUpdated.getLowerBoundAsInstant() != null) {
|
||||
ourLog.info("LastUpdated lower bound: {}", new InstantDt(theLastUpdated.getLowerBoundAsInstant()));
|
||||
Predicate predicateLower = builder.greaterThanOrEqualTo(from.<Date> get("myUpdated"), theLastUpdated.getLowerBoundAsInstant());
|
||||
Predicate predicateLower = builder.greaterThanOrEqualTo(from.<Date>get("myUpdated"), theLastUpdated.getLowerBoundAsInstant());
|
||||
lastUpdatedPredicates.add(predicateLower);
|
||||
}
|
||||
if (theLastUpdated.getUpperBoundAsInstant() != null) {
|
||||
Predicate predicateUpper = builder.lessThanOrEqualTo(from.<Date> get("myUpdated"), theLastUpdated.getUpperBoundAsInstant());
|
||||
Predicate predicateUpper = builder.lessThanOrEqualTo(from.<Date>get("myUpdated"), theLastUpdated.getUpperBoundAsInstant());
|
||||
lastUpdatedPredicates.add(predicateUpper);
|
||||
}
|
||||
}
|
||||
|
@ -1934,7 +1987,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
private static Predicate createResourceLinkPathPredicate(IDao theCallingDao, FhirContext theContext, String theParamName, From<?, ? extends ResourceLink> theFrom,
|
||||
String theResourceType) {
|
||||
String theResourceType) {
|
||||
RuntimeResourceDefinition resourceDef = theContext.getResourceDefinition(theResourceType);
|
||||
RuntimeSearchParam param = theCallingDao.getSearchParamByName(resourceDef, theParamName);
|
||||
List<String> path = param.getPathsSplit();
|
||||
|
@ -1958,10 +2011,37 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
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) {
|
||||
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> {
|
||||
|
||||
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 Set<Long> myPidSet = new HashSet<Long>();
|
||||
private boolean myFirst = true;
|
||||
private IncludesIterator myIncludesIterator;
|
||||
private Long myNext;
|
||||
private final Set<Long> myPidSet = new HashSet<Long>();
|
||||
private Iterator<Long> myPreResultsIterator;
|
||||
private Iterator<Long> myResultsIterator;
|
||||
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()) {
|
||||
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) {
|
||||
PeriodDt nextValue = (PeriodDt) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getStart(), nextValue.getEnd());
|
||||
nextEntity = new ResourceIndexedSearchParamDate(nextSpDef.getName(), nextValue.getStart(), nextValue.getEnd(), nextValue.getStartElement().getValueAsString());
|
||||
} else {
|
||||
if (!multiType) {
|
||||
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 {
|
||||
// nothing yet
|
||||
|
||||
@Override
|
||||
protected void refreshCacheIfNecessary() {
|
||||
// nothing yet
|
||||
}
|
||||
}
|
||||
|
|
|
@ -201,7 +201,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
|
||||
public Set<Include> getRevIncludes() {
|
||||
if (myRevIncludes == null) {
|
||||
myRevIncludes = new HashSet<Include>();
|
||||
myRevIncludes = new HashSet<>();
|
||||
}
|
||||
return myRevIncludes;
|
||||
}
|
||||
|
@ -210,6 +210,22 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
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
|
||||
* 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;
|
||||
|
||||
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.isNotBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -11,9 +31,9 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
@ -22,27 +42,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
* #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> {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoSearchParameterDstu3.class);
|
||||
|
@ -56,19 +55,21 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
|
|||
protected void markAffectedResources(SearchParameter theResource) {
|
||||
if (theResource != null) {
|
||||
String expression = theResource.getExpression();
|
||||
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);
|
||||
if (isNotBlank(expression)) {
|
||||
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);
|
||||
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
|
||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||
int updatedCount = txTemplate.execute(new TransactionCallback<Integer>() {
|
||||
@Override
|
||||
public Integer doInTransaction(TransactionStatus theStatus) {
|
||||
return myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
|
||||
}
|
||||
});
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
|
||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||
int updatedCount = txTemplate.execute(new TransactionCallback<Integer>() {
|
||||
@Override
|
||||
public Integer doInTransaction(TransactionStatus theStatus) {
|
||||
return myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
|
||||
}
|
||||
});
|
||||
|
||||
ourLog.info("Marked {} resources for reindexing", updatedCount);
|
||||
ourLog.info("Marked {} resources for reindexing", updatedCount);
|
||||
}
|
||||
}
|
||||
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
|
@ -126,43 +127,47 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
|
|||
}
|
||||
|
||||
String expression = theResource.getExpression();
|
||||
if (isBlank(expression)) {
|
||||
if (theResource.getType() == Enumerations.SearchParamType.COMPOSITE && isBlank(expression)) {
|
||||
|
||||
// this is ok
|
||||
|
||||
} else if (isBlank(expression)) {
|
||||
|
||||
throw new UnprocessableEntityException("SearchParameter.expression is missing");
|
||||
}
|
||||
|
||||
if (ElementUtil.isEmpty(theResource.getBase())) {
|
||||
throw new UnprocessableEntityException("SearchParameter.base is missing");
|
||||
}
|
||||
} else {
|
||||
|
||||
expression = expression.trim();
|
||||
theResource.setExpression(expression);
|
||||
expression = expression.trim();
|
||||
theResource.setExpression(expression);
|
||||
|
||||
String[] expressionSplit = BaseSearchParamExtractor.SPLIT.split(expression);
|
||||
String allResourceName = null;
|
||||
for (String nextPath : expressionSplit) {
|
||||
nextPath = nextPath.trim();
|
||||
String[] expressionSplit = BaseSearchParamExtractor.SPLIT.split(expression);
|
||||
String allResourceName = null;
|
||||
for (String nextPath : expressionSplit) {
|
||||
nextPath = nextPath.trim();
|
||||
|
||||
int dotIdx = nextPath.indexOf('.');
|
||||
if (dotIdx == -1) {
|
||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". Must start with a resource name");
|
||||
}
|
||||
|
||||
String resourceName = nextPath.substring(0, dotIdx);
|
||||
try {
|
||||
getContext().getResourceDefinition(resourceName);
|
||||
} catch (DataFormatException e) {
|
||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\": " + e.getMessage());
|
||||
}
|
||||
|
||||
if (allResourceName == null) {
|
||||
allResourceName = resourceName;
|
||||
} else {
|
||||
if (!allResourceName.equals(resourceName)) {
|
||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". All paths in a single SearchParameter must match the same resource type");
|
||||
int dotIdx = nextPath.indexOf('.');
|
||||
if (dotIdx == -1) {
|
||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". Must start with a resource name");
|
||||
}
|
||||
|
||||
String resourceName = nextPath.substring(0, dotIdx);
|
||||
try {
|
||||
getContext().getResourceDefinition(resourceName);
|
||||
} catch (DataFormatException e) {
|
||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\": " + e.getMessage());
|
||||
}
|
||||
|
||||
if (allResourceName == null) {
|
||||
allResourceName = resourceName;
|
||||
} else {
|
||||
if (!allResourceName.equals(resourceName)) {
|
||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". All paths in a single SearchParameter must match the same resource type");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} // if have expression
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -133,29 +133,33 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
|
|||
if (nextValue.isEmpty()) {
|
||||
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) {
|
||||
Period nextValue = (Period) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
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) {
|
||||
Timing nextValue = (Timing) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
TreeSet<Date> dates = new TreeSet<Date>();
|
||||
String firstValue = null;
|
||||
TreeSet<Date> dates = new TreeSet<>();
|
||||
for (DateTimeType nextEvent : nextValue.getEvent()) {
|
||||
if (nextEvent.getValue() != null) {
|
||||
dates.add(nextEvent.getValue());
|
||||
if (firstValue == null) {
|
||||
firstValue = nextEvent.getValueAsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dates.isEmpty()) {
|
||||
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) {
|
||||
// CarePlan.activitydate can be a string
|
||||
continue;
|
||||
|
|
|
@ -26,11 +26,15 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
import java.util.*;
|
||||
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.hl7.fhir.dstu3.model.CodeType;
|
||||
import org.hl7.fhir.dstu3.model.Extension;
|
||||
import org.hl7.fhir.dstu3.model.SearchParameter;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
|
@ -42,6 +46,7 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|||
|
||||
public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
|
||||
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;
|
||||
|
||||
|
@ -62,33 +67,33 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
|
|||
|
||||
@Override
|
||||
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() {
|
||||
refreshCacheIfNeccesary();
|
||||
refreshCacheIfNecessary();
|
||||
return myActiveSearchParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
|
||||
refreshCacheIfNeccesary();
|
||||
refreshCacheIfNecessary();
|
||||
return myActiveSearchParams.get(theResourceName);
|
||||
}
|
||||
|
||||
private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
|
||||
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
|
||||
if (retVal == null) {
|
||||
retVal = new HashMap<String, RuntimeSearchParam>();
|
||||
retVal = new HashMap<>();
|
||||
searchParams.put(theResourceName, retVal);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void refreshCacheIfNeccesary() {
|
||||
protected void refreshCacheIfNecessary() {
|
||||
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
|
||||
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
|
||||
synchronized (this) {
|
||||
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
|
||||
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 (RuntimeSearchParam nextParam : nextBuiltInEntry.getValue().values()) {
|
||||
String nextResourceName = nextBuiltInEntry.getKey();
|
||||
|
@ -97,40 +102,41 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
|
|||
}
|
||||
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLoadSynchronous(true);
|
||||
params.setLoadSynchronousUpTo(MAX_MANAGED_PARAM_COUNT);
|
||||
|
||||
IBundleProvider allSearchParamsBp = mySpDao.search(params);
|
||||
int size = allSearchParamsBp.size();
|
||||
|
||||
// Just in case..
|
||||
if (size > 10000) {
|
||||
ourLog.warn("Unable to support >10000 search params!");
|
||||
size = 10000;
|
||||
if (size > MAX_MANAGED_PARAM_COUNT) {
|
||||
ourLog.warn("Unable to support >" + MAX_MANAGED_PARAM_COUNT + " search params!");
|
||||
size = MAX_MANAGED_PARAM_COUNT;
|
||||
}
|
||||
|
||||
List<IBaseResource> allSearchParams = allSearchParamsBp.getResources(0, size);
|
||||
for (IBaseResource nextResource : allSearchParams) {
|
||||
SearchParameter nextSp = (SearchParameter) nextResource;
|
||||
RuntimeSearchParam runtimeSp = toRuntimeSp(nextSp);
|
||||
JpaRuntimeSearchParam runtimeSp = toRuntimeSp(nextSp);
|
||||
if (runtimeSp == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int dotIdx = runtimeSp.getPath().indexOf('.');
|
||||
if (dotIdx == -1) {
|
||||
ourLog.warn("Can not determine resource type of {}", runtimeSp.getPath());
|
||||
continue;
|
||||
}
|
||||
String resourceType = runtimeSp.getPath().substring(0, dotIdx);
|
||||
for (org.hl7.fhir.dstu3.model.CodeType nextBaseName : nextSp.getBase()) {
|
||||
String resourceType = nextBaseName.getValue();
|
||||
if (isBlank(resourceType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, resourceType);
|
||||
String name = runtimeSp.getName();
|
||||
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
|
||||
searchParamMap.put(name, runtimeSp);
|
||||
}
|
||||
|
||||
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, resourceType);
|
||||
String name = runtimeSp.getName();
|
||||
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
|
||||
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 (RuntimeSearchParam nextSp : nextEntry.getValue().values()) {
|
||||
String nextName = nextSp.getName();
|
||||
|
@ -155,6 +161,8 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
|
|||
|
||||
myActiveSearchParams = activeSearchParams;
|
||||
|
||||
super.populateActiveSearchParams(activeSearchParams);
|
||||
|
||||
myLastRefresh = System.currentTimeMillis();
|
||||
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 description = theNextSp.getDescription();
|
||||
String path = theNextSp.getExpression();
|
||||
|
@ -215,12 +223,31 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
|
|||
Set<String> targets = toStrings(theNextSp.getTarget());
|
||||
|
||||
if (isBlank(name) || isBlank(path) || paramType == null) {
|
||||
return null;
|
||||
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
IIdType id = theNextSp.getIdElement();
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -24,6 +25,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
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.SearchParameter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -56,19 +58,21 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchPa
|
|||
protected void markAffectedResources(SearchParameter theResource) {
|
||||
if (theResource != null) {
|
||||
String expression = theResource.getExpression();
|
||||
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);
|
||||
if (isNotBlank(expression)) {
|
||||
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);
|
||||
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
|
||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||
int updatedCount = txTemplate.execute(new TransactionCallback<Integer>() {
|
||||
@Override
|
||||
public Integer doInTransaction(TransactionStatus theStatus) {
|
||||
return myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
|
||||
}
|
||||
});
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
|
||||
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
||||
int updatedCount = txTemplate.execute(new TransactionCallback<Integer>() {
|
||||
@Override
|
||||
public Integer doInTransaction(TransactionStatus theStatus) {
|
||||
return myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
|
||||
}
|
||||
});
|
||||
|
||||
ourLog.info("Marked {} resources for reindexing", updatedCount);
|
||||
ourLog.info("Marked {} resources for reindexing", updatedCount);
|
||||
}
|
||||
}
|
||||
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
|
@ -125,44 +129,52 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchPa
|
|||
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())) {
|
||||
throw new UnprocessableEntityException("SearchParameter.base is missing");
|
||||
}
|
||||
|
||||
expression = expression.trim();
|
||||
theResource.setExpression(expression);
|
||||
String expression = theResource.getExpression();
|
||||
if (theResource.getType() == Enumerations.SearchParamType.COMPOSITE && isBlank(expression)) {
|
||||
|
||||
String[] expressionSplit = BaseSearchParamExtractor.SPLIT.split(expression);
|
||||
String allResourceName = null;
|
||||
for (String nextPath : expressionSplit) {
|
||||
nextPath = nextPath.trim();
|
||||
// this is ok
|
||||
|
||||
int dotIdx = nextPath.indexOf('.');
|
||||
if (dotIdx == -1) {
|
||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". Must start with a resource name");
|
||||
}
|
||||
} else if (isBlank(expression)) {
|
||||
|
||||
String resourceName = nextPath.substring(0, dotIdx);
|
||||
try {
|
||||
getContext().getResourceDefinition(resourceName);
|
||||
} catch (DataFormatException e) {
|
||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\": " + e.getMessage());
|
||||
}
|
||||
throw new UnprocessableEntityException("SearchParameter.expression is missing");
|
||||
|
||||
if (allResourceName == null) {
|
||||
allResourceName = resourceName;
|
||||
} else {
|
||||
if (!allResourceName.equals(resourceName)) {
|
||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". All paths in a single SearchParameter must match the same resource type");
|
||||
} else {
|
||||
|
||||
expression = expression.trim();
|
||||
theResource.setExpression(expression);
|
||||
|
||||
String[] expressionSplit = BaseSearchParamExtractor.SPLIT.split(expression);
|
||||
String allResourceName = null;
|
||||
for (String nextPath : expressionSplit) {
|
||||
nextPath = nextPath.trim();
|
||||
|
||||
int dotIdx = nextPath.indexOf('.');
|
||||
if (dotIdx == -1) {
|
||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". Must start with a resource name");
|
||||
}
|
||||
|
||||
String resourceName = nextPath.substring(0, dotIdx);
|
||||
try {
|
||||
getContext().getResourceDefinition(resourceName);
|
||||
} catch (DataFormatException e) {
|
||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\": " + e.getMessage());
|
||||
}
|
||||
|
||||
if (allResourceName == null) {
|
||||
allResourceName = resourceName;
|
||||
} else {
|
||||
if (!allResourceName.equals(resourceName)) {
|
||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". All paths in a single SearchParameter must match the same resource type");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} // if have expression
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -133,29 +133,33 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
|||
if (nextValue.isEmpty()) {
|
||||
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) {
|
||||
Period nextValue = (Period) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
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) {
|
||||
Timing nextValue = (Timing) nextObject;
|
||||
if (nextValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
TreeSet<Date> dates = new TreeSet<Date>();
|
||||
TreeSet<Date> dates = new TreeSet<>();
|
||||
String firstValue = null;
|
||||
for (DateTimeType nextEvent : nextValue.getEvent()) {
|
||||
if (nextEvent.getValue() != null) {
|
||||
dates.add(nextEvent.getValue());
|
||||
if (firstValue == null) {
|
||||
firstValue = nextEvent.getValueAsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dates.isEmpty()) {
|
||||
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) {
|
||||
// CarePlan.activitydate can be a string
|
||||
continue;
|
||||
|
@ -450,7 +454,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
|||
*/
|
||||
@Override
|
||||
public Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IBaseResource theResource) {
|
||||
HashSet<BaseResourceIndexedSearchParam> retVal = new HashSet<BaseResourceIndexedSearchParam>();
|
||||
HashSet<BaseResourceIndexedSearchParam> retVal = new HashSet<>();
|
||||
|
||||
String useSystem = null;
|
||||
if (theResource instanceof CodeSystem) {
|
||||
|
@ -477,16 +481,6 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
|||
List<String> systems = 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)) {
|
||||
|
||||
if (nextObject == null) {
|
||||
|
|
|
@ -20,14 +20,19 @@ package ca.uhn.fhir.jpa.dao.r4;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.util.*;
|
||||
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.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
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.instance.model.api.IBaseResource;
|
||||
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 {
|
||||
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;
|
||||
|
||||
|
@ -62,33 +68,33 @@ public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
|
|||
|
||||
@Override
|
||||
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() {
|
||||
refreshCacheIfNeccesary();
|
||||
refreshCacheIfNecessary();
|
||||
return myActiveSearchParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
|
||||
refreshCacheIfNeccesary();
|
||||
refreshCacheIfNecessary();
|
||||
return myActiveSearchParams.get(theResourceName);
|
||||
}
|
||||
|
||||
private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
|
||||
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
|
||||
if (retVal == null) {
|
||||
retVal = new HashMap<String, RuntimeSearchParam>();
|
||||
retVal = new HashMap<>();
|
||||
searchParams.put(theResourceName, retVal);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void refreshCacheIfNeccesary() {
|
||||
protected void refreshCacheIfNecessary() {
|
||||
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
|
||||
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
|
||||
synchronized (this) {
|
||||
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
|
||||
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 (RuntimeSearchParam nextParam : nextBuiltInEntry.getValue().values()) {
|
||||
String nextResourceName = nextBuiltInEntry.getKey();
|
||||
|
@ -97,15 +103,15 @@ public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
|
|||
}
|
||||
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLoadSynchronous(true);
|
||||
params.setLoadSynchronousUpTo(MAX_MANAGED_PARAM_COUNT);
|
||||
|
||||
IBundleProvider allSearchParamsBp = mySpDao.search(params);
|
||||
int size = allSearchParamsBp.size();
|
||||
|
||||
// Just in case..
|
||||
if (size > 10000) {
|
||||
ourLog.warn("Unable to support >10000 search params!");
|
||||
size = 10000;
|
||||
if (size >= MAX_MANAGED_PARAM_COUNT) {
|
||||
ourLog.warn("Unable to support >" + MAX_MANAGED_PARAM_COUNT + " search params!");
|
||||
size = MAX_MANAGED_PARAM_COUNT;
|
||||
}
|
||||
|
||||
List<IBaseResource> allSearchParams = allSearchParamsBp.getResources(0, size);
|
||||
|
@ -116,21 +122,22 @@ public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
|
|||
continue;
|
||||
}
|
||||
|
||||
int dotIdx = runtimeSp.getPath().indexOf('.');
|
||||
if (dotIdx == -1) {
|
||||
ourLog.warn("Can not determine resource type of {}", runtimeSp.getPath());
|
||||
continue;
|
||||
}
|
||||
String resourceType = runtimeSp.getPath().substring(0, dotIdx);
|
||||
for (CodeType nextBaseName : nextSp.getBase()) {
|
||||
String resourceType = nextBaseName.getValue();
|
||||
if (isBlank(resourceType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, resourceType);
|
||||
String name = runtimeSp.getName();
|
||||
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
|
||||
searchParamMap.put(name, runtimeSp);
|
||||
}
|
||||
|
||||
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, resourceType);
|
||||
String name = runtimeSp.getName();
|
||||
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
|
||||
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 (RuntimeSearchParam nextSp : nextEntry.getValue().values()) {
|
||||
String nextName = nextSp.getName();
|
||||
|
@ -155,6 +162,8 @@ public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
|
|||
|
||||
myActiveSearchParams = activeSearchParams;
|
||||
|
||||
super.populateActiveSearchParams(activeSearchParams);
|
||||
|
||||
myLastRefresh = System.currentTimeMillis();
|
||||
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());
|
||||
|
||||
if (isBlank(name) || isBlank(path) || paramType == null) {
|
||||
return null;
|
||||
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
IIdType id = theNextSp.getIdElement();
|
||||
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;
|
||||
}
|
||||
|
||||
private Set<String> toStrings(List<CodeType> theTarget) {
|
||||
HashSet<String> retVal = new HashSet<String>();
|
||||
HashSet<String> retVal = new HashSet<>();
|
||||
for (CodeType next : theTarget) {
|
||||
if (isNotBlank(next.getValue())) {
|
||||
retVal.add(next.getValue());
|
||||
|
|
|
@ -20,14 +20,14 @@ package ca.uhn.fhir.jpa.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import org.hibernate.search.annotations.ContainedIn;
|
||||
import org.hibernate.search.annotations.Field;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@MappedSuperclass
|
||||
public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
||||
|
||||
|
@ -67,10 +67,19 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
|||
return myParamName;
|
||||
}
|
||||
|
||||
public void setParamName(String theName) {
|
||||
myParamName = theName;
|
||||
}
|
||||
|
||||
public ResourceTable getResource() {
|
||||
return myResource;
|
||||
}
|
||||
|
||||
public void setResource(ResourceTable theResource) {
|
||||
myResource = theResource;
|
||||
myResourceType = theResource.getResourceType();
|
||||
}
|
||||
|
||||
public Long getResourcePid() {
|
||||
return myResourcePid;
|
||||
}
|
||||
|
@ -83,6 +92,10 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
|||
return myUpdated;
|
||||
}
|
||||
|
||||
public void setUpdated(Date theUpdated) {
|
||||
myUpdated = theUpdated;
|
||||
}
|
||||
|
||||
public boolean isMissing() {
|
||||
return Boolean.TRUE.equals(myMissing);
|
||||
}
|
||||
|
@ -91,17 +104,5 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
|||
myMissing = theMissing;
|
||||
}
|
||||
|
||||
public void setParamName(String theName) {
|
||||
myParamName = theName;
|
||||
}
|
||||
|
||||
public void setResource(ResourceTable theResource) {
|
||||
myResource = theResource;
|
||||
myResourceType = theResource.getResourceType();
|
||||
}
|
||||
|
||||
public void setUpdated(Date theUpdated) {
|
||||
myUpdated = theUpdated;
|
||||
}
|
||||
|
||||
public abstract IQueryParameterType toQueryParameterType();
|
||||
}
|
||||
|
|
|
@ -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.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.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
@ -99,6 +101,11 @@ public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchP
|
|||
return myId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IQueryParameterType toQueryParameterType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public double getLatitude() {
|
||||
return myLatitude;
|
||||
}
|
||||
|
|
|
@ -20,54 +20,48 @@ package ca.uhn.fhir.jpa.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
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 javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
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
|
||||
@Entity
|
||||
@Table(name = "HFJ_SPIDX_DATE", indexes= {
|
||||
@Table(name = "HFJ_SPIDX_DATE", indexes = {
|
||||
@Index(name = "IDX_SP_DATE", columnList = "RES_TYPE,SP_NAME,SP_VALUE_LOW,SP_VALUE_HIGH"),
|
||||
@Index(name = "IDX_SP_DATE_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_DATE_RESID", columnList = "RES_ID")
|
||||
@Index(name = "IDX_SP_DATE_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_DATE_RESID", columnList = "RES_ID")
|
||||
})
|
||||
public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchParam {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@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;
|
||||
@Transient
|
||||
private transient String myOriginalValue;
|
||||
|
||||
@Column(name = "SP_VALUE_HIGH", nullable = true)
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Field
|
||||
public Date myValueHigh;
|
||||
|
||||
@Column(name = "SP_VALUE_LOW", nullable = true)
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Field
|
||||
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
|
||||
|
@ -78,10 +72,11 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
|||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResourceIndexedSearchParamDate(String theName, Date theLow, Date theHigh) {
|
||||
public ResourceIndexedSearchParamDate(String theName, Date theLow, Date theHigh, String theOriginalValue) {
|
||||
setParamName(theName);
|
||||
setValueLow(theLow);
|
||||
setValueHigh(theHigh);
|
||||
myOriginalValue = theOriginalValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -113,10 +108,18 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
|||
return myValueHigh;
|
||||
}
|
||||
|
||||
public void setValueHigh(Date theValueHigh) {
|
||||
myValueHigh = theValueHigh;
|
||||
}
|
||||
|
||||
public Date getValueLow() {
|
||||
return myValueLow;
|
||||
}
|
||||
|
||||
public void setValueLow(Date theValueLow) {
|
||||
myValueLow = theValueLow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder b = new HashCodeBuilder();
|
||||
|
@ -127,12 +130,13 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
|
|||
return b.toHashCode();
|
||||
}
|
||||
|
||||
public void setValueHigh(Date theValueHigh) {
|
||||
myValueHigh = theValueHigh;
|
||||
}
|
||||
|
||||
public void setValueLow(Date theValueLow) {
|
||||
myValueLow = theValueLow;
|
||||
@Override
|
||||
public IQueryParameterType toQueryParameterType() {
|
||||
DateTimeType value = new DateTimeType(myOriginalValue);
|
||||
if (value.getPrecision().ordinal() > TemporalPrecisionEnum.DAY.ordinal()) {
|
||||
value.setTimeZoneZulu(true);
|
||||
}
|
||||
return new DateParam(value.getValueAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,18 +20,9 @@ package ca.uhn.fhir.jpa.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.persistence.Column;
|
||||
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 ca.uhn.fhir.jpa.util.BigDecimalNumericFieldBridge;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.NumberParam;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
@ -40,32 +31,31 @@ import org.hibernate.search.annotations.Field;
|
|||
import org.hibernate.search.annotations.FieldBridge;
|
||||
import org.hibernate.search.annotations.NumericField;
|
||||
|
||||
import ca.uhn.fhir.jpa.util.BigDecimalNumericFieldBridge;
|
||||
import javax.persistence.*;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
//@formatter:off
|
||||
@Embeddable
|
||||
@Entity
|
||||
@Table(name = "HFJ_SPIDX_NUMBER", indexes= {
|
||||
@Table(name = "HFJ_SPIDX_NUMBER", indexes = {
|
||||
@Index(name = "IDX_SP_NUMBER", columnList = "RES_TYPE,SP_NAME,SP_VALUE"),
|
||||
@Index(name = "IDX_SP_NUMBER_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_NUMBER_RESID", columnList = "RES_ID")
|
||||
@Index(name = "IDX_SP_NUMBER_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_NUMBER_RESID", columnList = "RES_ID")
|
||||
})
|
||||
//@formatter:on
|
||||
public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchParam {
|
||||
|
||||
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)
|
||||
@Field
|
||||
@NumericField
|
||||
@FieldBridge(impl = BigDecimalNumericFieldBridge.class)
|
||||
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() {
|
||||
}
|
||||
|
@ -103,6 +93,10 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
|
|||
return myValue;
|
||||
}
|
||||
|
||||
public void setValue(BigDecimal theValue) {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder b = new HashCodeBuilder();
|
||||
|
@ -112,8 +106,9 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
|
|||
return b.toHashCode();
|
||||
}
|
||||
|
||||
public void setValue(BigDecimal theValue) {
|
||||
myValue = theValue;
|
||||
@Override
|
||||
public IQueryParameterType toQueryParameterType() {
|
||||
return new NumberParam(myValue.toPlainString());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,18 +20,9 @@ package ca.uhn.fhir.jpa.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import javax.persistence.Column;
|
||||
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 ca.uhn.fhir.jpa.util.BigDecimalNumericFieldBridge;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
@ -40,15 +31,16 @@ import org.hibernate.search.annotations.Field;
|
|||
import org.hibernate.search.annotations.FieldBridge;
|
||||
import org.hibernate.search.annotations.NumericField;
|
||||
|
||||
import ca.uhn.fhir.jpa.util.BigDecimalNumericFieldBridge;
|
||||
import javax.persistence.*;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
//@formatter:off
|
||||
@Embeddable
|
||||
@Entity
|
||||
@Table(name = "HFJ_SPIDX_QUANTITY", indexes = {
|
||||
@Index(name = "IDX_SP_QUANTITY", columnList = "RES_TYPE,SP_NAME,SP_SYSTEM,SP_UNITS,SP_VALUE"),
|
||||
@Index(name = "IDX_SP_QUANTITY_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_QUANTITY_RESID", columnList = "RES_ID")
|
||||
@Index(name = "IDX_SP_QUANTITY_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_QUANTITY_RESID", columnList = "RES_ID")
|
||||
})
|
||||
//@formatter:on
|
||||
public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearchParam {
|
||||
|
@ -56,26 +48,22 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
|||
private static final int MAX_LENGTH = 200;
|
||||
|
||||
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)
|
||||
@Field
|
||||
public String mySystem;
|
||||
|
||||
@Column(name = "SP_UNITS", nullable = true, length = MAX_LENGTH)
|
||||
@Field
|
||||
public String myUnits;
|
||||
|
||||
@Column(name = "SP_VALUE", nullable = true)
|
||||
@Field
|
||||
@NumericField
|
||||
@FieldBridge(impl = BigDecimalNumericFieldBridge.class)
|
||||
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() {
|
||||
// nothing
|
||||
|
@ -118,14 +106,26 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
|||
return mySystem;
|
||||
}
|
||||
|
||||
public void setSystem(String theSystem) {
|
||||
mySystem = theSystem;
|
||||
}
|
||||
|
||||
public String getUnits() {
|
||||
return myUnits;
|
||||
}
|
||||
|
||||
public void setUnits(String theUnits) {
|
||||
myUnits = theUnits;
|
||||
}
|
||||
|
||||
public BigDecimal getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
public void setValue(BigDecimal theValue) {
|
||||
myValue = theValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder b = new HashCodeBuilder();
|
||||
|
@ -137,16 +137,9 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
|||
return b.toHashCode();
|
||||
}
|
||||
|
||||
public void setSystem(String theSystem) {
|
||||
mySystem = theSystem;
|
||||
}
|
||||
|
||||
public void setUnits(String theUnits) {
|
||||
myUnits = theUnits;
|
||||
}
|
||||
|
||||
public void setValue(BigDecimal theValue) {
|
||||
myValue = theValue;
|
||||
@Override
|
||||
public IQueryParameterType toQueryParameterType() {
|
||||
return new QuantityParam(null, getValue(), getSystem(), getUnits());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,39 +20,25 @@ package ca.uhn.fhir.jpa.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
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 ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.hibernate.search.annotations.Analyze;
|
||||
import org.hibernate.search.annotations.Analyzer;
|
||||
import org.hibernate.search.annotations.ContainedIn;
|
||||
import org.hibernate.search.annotations.Field;
|
||||
import org.hibernate.search.annotations.Fields;
|
||||
import org.hibernate.search.annotations.Indexed;
|
||||
import org.hibernate.search.annotations.Store;
|
||||
import org.hibernate.search.annotations.*;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.persistence.Index;
|
||||
|
||||
//@formatter:off
|
||||
@Embeddable
|
||||
@Entity
|
||||
@Table(name = "HFJ_SPIDX_STRING", indexes = {
|
||||
@Index(name = "IDX_SP_STRING", columnList = "RES_TYPE,SP_NAME,SP_VALUE_NORMALIZED"),
|
||||
@Index(name = "IDX_SP_STRING_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_STRING_RESID", columnList = "RES_ID")
|
||||
@Table(name = "HFJ_SPIDX_STRING", indexes = {
|
||||
@Index(name = "IDX_SP_STRING", columnList = "RES_TYPE,SP_NAME,SP_VALUE_NORMALIZED"),
|
||||
@Index(name = "IDX_SP_STRING_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_STRING_RESID", columnList = "RES_ID")
|
||||
})
|
||||
@Indexed()
|
||||
//@AnalyzerDefs({
|
||||
|
@ -109,13 +95,13 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
|||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@SequenceGenerator(name="SEQ_SPIDX_STRING", sequenceName="SEQ_SPIDX_STRING")
|
||||
@SequenceGenerator(name = "SEQ_SPIDX_STRING", sequenceName = "SEQ_SPIDX_STRING")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_STRING")
|
||||
@Column(name = "SP_ID")
|
||||
private Long myId;
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumn(name = "RES_ID", referencedColumnName="RES_ID", insertable=false, updatable=false, foreignKey=@ForeignKey(name="FK_SPIDXSTR_RESOURCE"))
|
||||
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", insertable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_SPIDXSTR_RESOURCE"))
|
||||
@ContainedIn
|
||||
private ResourceTable myResourceTable;
|
||||
|
||||
|
@ -169,10 +155,24 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
|||
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() {
|
||||
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
|
||||
public int hashCode() {
|
||||
HashCodeBuilder b = new HashCodeBuilder();
|
||||
|
@ -182,18 +182,9 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
|||
return b.toHashCode();
|
||||
}
|
||||
|
||||
public void setValueExact(String theValueExact) {
|
||||
if (StringUtils.defaultString(theValueExact).length() > MAX_LENGTH) {
|
||||
throw new IllegalArgumentException("Value is too long: " + theValueExact.length());
|
||||
}
|
||||
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
|
||||
public IQueryParameterType toQueryParameterType() {
|
||||
return new StringParam(getValueExact());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,16 +20,8 @@ package ca.uhn.fhir.jpa.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import javax.persistence.Column;
|
||||
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 ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
|
@ -37,14 +29,16 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
|
|||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.hibernate.search.annotations.Field;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
//@formatter:off
|
||||
@Embeddable
|
||||
@Entity
|
||||
@Table(name = "HFJ_SPIDX_TOKEN", indexes = {
|
||||
@Index(name = "IDX_SP_TOKEN", columnList = "RES_TYPE,SP_NAME,SP_SYSTEM,SP_VALUE"),
|
||||
@Index(name = "IDX_SP_TOKEN_UNQUAL", columnList = "RES_TYPE,SP_NAME,SP_VALUE"),
|
||||
@Index(name = "IDX_SP_TOKEN_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_TOKEN_RESID", columnList = "RES_ID")
|
||||
@Index(name = "IDX_SP_TOKEN_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_TOKEN_RESID", columnList = "RES_ID")
|
||||
})
|
||||
//@formatter:on
|
||||
public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchParam {
|
||||
|
@ -52,21 +46,18 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
public static final int MAX_LENGTH = 200;
|
||||
|
||||
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
|
||||
@SequenceGenerator(name = "SEQ_SPIDX_TOKEN", sequenceName = "SEQ_SPIDX_TOKEN")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_TOKEN")
|
||||
@Column(name = "SP_ID")
|
||||
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() {
|
||||
}
|
||||
|
||||
|
@ -105,10 +96,18 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
return mySystem;
|
||||
}
|
||||
|
||||
public void setSystem(String theSystem) {
|
||||
mySystem = StringUtils.defaultIfBlank(theSystem, null);
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
public void setValue(String theValue) {
|
||||
myValue = StringUtils.defaultIfBlank(theValue, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder b = new HashCodeBuilder();
|
||||
|
@ -119,12 +118,9 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
return b.toHashCode();
|
||||
}
|
||||
|
||||
public void setSystem(String theSystem) {
|
||||
mySystem = StringUtils.defaultIfBlank(theSystem, null);
|
||||
}
|
||||
|
||||
public void setValue(String theValue) {
|
||||
myValue = StringUtils.defaultIfBlank(theValue, null);
|
||||
@Override
|
||||
public IQueryParameterType toQueryParameterType() {
|
||||
return new TokenParam(getSystem(), getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,30 +20,24 @@ package ca.uhn.fhir.jpa.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import javax.persistence.Column;
|
||||
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 ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.hibernate.search.annotations.Field;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
//@formatter:off
|
||||
@Embeddable
|
||||
@Entity
|
||||
@Table(name = "HFJ_SPIDX_URI", indexes = {
|
||||
@Index(name = "IDX_SP_URI", columnList = "RES_TYPE,SP_NAME,SP_URI"),
|
||||
@Index(name = "IDX_SP_URI_RESTYPE_NAME", columnList = "RES_TYPE,SP_NAME"),
|
||||
@Index(name = "IDX_SP_URI_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_URI_COORDS", columnList = "RES_ID")
|
||||
@Table(name = "HFJ_SPIDX_URI", indexes = {
|
||||
@Index(name = "IDX_SP_URI", columnList = "RES_TYPE,SP_NAME,SP_URI"),
|
||||
@Index(name = "IDX_SP_URI_RESTYPE_NAME", columnList = "RES_TYPE,SP_NAME"),
|
||||
@Index(name = "IDX_SP_URI_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_URI_COORDS", columnList = "RES_ID")
|
||||
})
|
||||
//@formatter:on
|
||||
public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchParam {
|
||||
|
@ -54,16 +48,14 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
|||
public static final int MAX_LENGTH = 255;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@SequenceGenerator(name="SEQ_SPIDX_URI", sequenceName="SEQ_SPIDX_URI")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator="SEQ_SPIDX_URI")
|
||||
@Column(name = "SP_ID")
|
||||
private Long myId;
|
||||
|
||||
@Column(name = "SP_URI", nullable = true, length = MAX_LENGTH)
|
||||
@Field()
|
||||
public String myUri;
|
||||
@Id
|
||||
@SequenceGenerator(name = "SEQ_SPIDX_URI", sequenceName = "SEQ_SPIDX_URI")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_URI")
|
||||
@Column(name = "SP_ID")
|
||||
private Long myId;
|
||||
|
||||
public ResourceIndexedSearchParamUri() {
|
||||
}
|
||||
|
@ -101,6 +93,10 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
|||
return myUri;
|
||||
}
|
||||
|
||||
public void setUri(String theUri) {
|
||||
myUri = StringUtils.defaultIfBlank(theUri, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder b = new HashCodeBuilder();
|
||||
|
@ -110,8 +106,9 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
|||
return b.toHashCode();
|
||||
}
|
||||
|
||||
public void setUri(String theUri) {
|
||||
myUri = StringUtils.defaultIfBlank(theUri, null);
|
||||
@Override
|
||||
public IQueryParameterType toQueryParameterType() {
|
||||
return new UriParam(getUri());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,27 +19,11 @@ package ca.uhn.fhir.jpa.entity;
|
|||
* limitations under the License.
|
||||
* #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.ToStringStyle;
|
||||
import org.apache.lucene.analysis.core.LowerCaseFilterFactory;
|
||||
|
@ -52,58 +36,53 @@ import org.apache.lucene.analysis.phonetic.PhoneticFilterFactory;
|
|||
import org.apache.lucene.analysis.snowball.SnowballPorterFilterFactory;
|
||||
import org.apache.lucene.analysis.standard.StandardFilterFactory;
|
||||
import org.apache.lucene.analysis.standard.StandardTokenizerFactory;
|
||||
import org.hibernate.search.annotations.Analyze;
|
||||
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.*;
|
||||
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 ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import javax.persistence.*;
|
||||
import javax.persistence.Index;
|
||||
import java.io.Serializable;
|
||||
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
|
||||
@Indexed(interceptor=IndexNonDeletedInterceptor.class)
|
||||
@Indexed(interceptor = IndexNonDeletedInterceptor.class)
|
||||
@Entity
|
||||
@Table(name = "HFJ_RESOURCE", uniqueConstraints = {}, indexes= {
|
||||
@Index(name = "IDX_RES_DATE", columnList="RES_UPDATED"),
|
||||
@Index(name = "IDX_RES_LANG", columnList="RES_TYPE,RES_LANGUAGE"),
|
||||
@Index(name = "IDX_RES_PROFILE", columnList="RES_PROFILE"),
|
||||
@Index(name = "IDX_RES_TYPE", columnList="RES_TYPE"),
|
||||
@Index(name = "IDX_INDEXSTATUS", columnList="SP_INDEX_STATUS")
|
||||
@Table(name = "HFJ_RESOURCE", uniqueConstraints = {}, indexes = {
|
||||
@Index(name = "IDX_RES_DATE", columnList = "RES_UPDATED"),
|
||||
@Index(name = "IDX_RES_LANG", columnList = "RES_TYPE,RES_LANGUAGE"),
|
||||
@Index(name = "IDX_RES_PROFILE", columnList = "RES_PROFILE"),
|
||||
@Index(name = "IDX_RES_TYPE", columnList = "RES_TYPE"),
|
||||
@Index(name = "IDX_INDEXSTATUS", columnList = "SP_INDEX_STATUS")
|
||||
})
|
||||
@AnalyzerDefs({
|
||||
@AnalyzerDef(name = "autocompleteEdgeAnalyzer",
|
||||
tokenizer = @TokenizerDef(factory = PatternTokenizerFactory.class, params= {
|
||||
@Parameter(name="pattern", value="(.*)"),
|
||||
@Parameter(name="group", value="1")
|
||||
tokenizer = @TokenizerDef(factory = PatternTokenizerFactory.class, params = {
|
||||
@Parameter(name = "pattern", value = "(.*)"),
|
||||
@Parameter(name = "group", value = "1")
|
||||
}),
|
||||
filters = {
|
||||
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
|
||||
@TokenFilterDef(factory = StopFilterFactory.class),
|
||||
@TokenFilterDef(factory = EdgeNGramFilterFactory.class, params = {
|
||||
@Parameter(name = "minGramSize", value = "3"),
|
||||
@Parameter(name = "maxGramSize", value = "50")
|
||||
}),
|
||||
@Parameter(name = "maxGramSize", value = "50")
|
||||
}),
|
||||
}),
|
||||
@AnalyzerDef(name = "autocompletePhoneticAnalyzer",
|
||||
tokenizer = @TokenizerDef(factory=StandardTokenizerFactory.class),
|
||||
tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
|
||||
filters = {
|
||||
@TokenFilterDef(factory=StandardFilterFactory.class),
|
||||
@TokenFilterDef(factory=StopFilterFactory.class),
|
||||
@TokenFilterDef(factory=PhoneticFilterFactory.class, params = {
|
||||
@Parameter(name="encoder", value="DoubleMetaphone")
|
||||
@TokenFilterDef(factory = StandardFilterFactory.class),
|
||||
@TokenFilterDef(factory = StopFilterFactory.class),
|
||||
@TokenFilterDef(factory = PhoneticFilterFactory.class, params = {
|
||||
@Parameter(name = "encoder", value = "DoubleMetaphone")
|
||||
}),
|
||||
@TokenFilterDef(factory=SnowballPorterFilterFactory.class, params = {
|
||||
@Parameter(name="language", value="English")
|
||||
@TokenFilterDef(factory = SnowballPorterFilterFactory.class, params = {
|
||||
@Parameter(name = "language", value = "English")
|
||||
})
|
||||
}),
|
||||
@AnalyzerDef(name = "autocompleteNGramAnalyzer",
|
||||
|
@ -113,7 +92,7 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
|||
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
|
||||
@TokenFilterDef(factory = NGramFilterFactory.class, params = {
|
||||
@Parameter(name = "minGramSize", value = "3"),
|
||||
@Parameter(name = "maxGramSize", value = "20")
|
||||
@Parameter(name = "maxGramSize", value = "20")
|
||||
}),
|
||||
}),
|
||||
@AnalyzerDef(name = "standardAnalyzer",
|
||||
|
@ -125,21 +104,18 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
|||
tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
|
||||
filters = {
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
//@formatter:on
|
||||
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_PROFILE_LENGTH = 200;
|
||||
|
||||
static final int RESTYPE_LEN = 30;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Holds the narrative text only - Used for Fulltext searching but not directly stored in the DB
|
||||
*/
|
||||
//@formatter:off
|
||||
@Transient()
|
||||
@Fields({
|
||||
@Field(name = "myContentText", index = org.hibernate.search.annotations.Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "standardAnalyzer")),
|
||||
|
@ -147,10 +123,9 @@ 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 = "myContentTextPhonetic", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompletePhoneticAnalyzer"))
|
||||
})
|
||||
//@formatter:on
|
||||
private String myContentText;
|
||||
|
||||
@Column(name = "HASH_SHA256", length=64, nullable=true)
|
||||
@Column(name = "HASH_SHA256", length = 64, nullable = true)
|
||||
private String myHashSha256;
|
||||
|
||||
@Column(name = "SP_HAS_LINKS")
|
||||
|
@ -227,24 +202,22 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
|
||||
@Column(name = "RES_PROFILE", length = MAX_PROFILE_LENGTH, nullable = true)
|
||||
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)
|
||||
@IndexedEmbedded()
|
||||
private Collection<ResourceLink> myResourceLinks;
|
||||
|
||||
@Column(name = "RES_TYPE", length = RESTYPE_LEN)
|
||||
@Field
|
||||
private String myResourceType;
|
||||
|
||||
@OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
|
||||
private Collection<SearchParamPresent> mySearchParamPresents;
|
||||
|
||||
@OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
|
||||
private Set<ResourceTag> myTags;
|
||||
|
||||
@Transient
|
||||
private transient boolean myUnchangedInCurrentOperation;
|
||||
|
||||
@Column(name = "RES_VER")
|
||||
private long myVersion;
|
||||
|
||||
|
@ -264,11 +237,19 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
return myHashSha256;
|
||||
}
|
||||
|
||||
public void setHashSha256(String theHashSha256) {
|
||||
myHashSha256 = theHashSha256;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
public void setId(Long theId) {
|
||||
myId = theId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdDt getIdDt() {
|
||||
if (getForcedId() == null) {
|
||||
|
@ -283,157 +264,14 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
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) {
|
||||
myIndexStatus = theIndexStatus;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return myLanguage;
|
||||
}
|
||||
|
||||
public void setLanguage(String theLanguage) {
|
||||
if (defaultString(theLanguage).length() > MAX_LANGUAGE_LENGTH) {
|
||||
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;
|
||||
}
|
||||
|
||||
public void setNarrativeTextParsedIntoWords(String theNarrativeText) {
|
||||
myNarrativeText = theNarrativeText;
|
||||
public Collection<ResourceIndexedCompositeStringUnique> getParamsCompositeStringUnique() {
|
||||
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) {
|
||||
|
@ -453,8 +305,11 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
getParamsCoords().addAll(theParamsCoords);
|
||||
}
|
||||
|
||||
public void setParamsCoordsPopulated(boolean theParamsCoordsPopulated) {
|
||||
myParamsCoordsPopulated = theParamsCoordsPopulated;
|
||||
public Collection<ResourceIndexedSearchParamDate> getParamsDate() {
|
||||
if (myParamsDate == null) {
|
||||
myParamsDate = new ArrayList<>();
|
||||
}
|
||||
return myParamsDate;
|
||||
}
|
||||
|
||||
public void setParamsDate(Collection<ResourceIndexedSearchParamDate> theParamsDate) {
|
||||
|
@ -465,8 +320,11 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
getParamsDate().addAll(theParamsDate);
|
||||
}
|
||||
|
||||
public void setParamsDatePopulated(boolean theParamsDatePopulated) {
|
||||
myParamsDatePopulated = theParamsDatePopulated;
|
||||
public Collection<ResourceIndexedSearchParamNumber> getParamsNumber() {
|
||||
if (myParamsNumber == null) {
|
||||
myParamsNumber = new ArrayList<>();
|
||||
}
|
||||
return myParamsNumber;
|
||||
}
|
||||
|
||||
public void setParamsNumber(Collection<ResourceIndexedSearchParamNumber> theNumberParams) {
|
||||
|
@ -477,8 +335,11 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
getParamsNumber().addAll(theNumberParams);
|
||||
}
|
||||
|
||||
public void setParamsNumberPopulated(boolean theParamsNumberPopulated) {
|
||||
myParamsNumberPopulated = theParamsNumberPopulated;
|
||||
public Collection<ResourceIndexedSearchParamQuantity> getParamsQuantity() {
|
||||
if (myParamsQuantity == null) {
|
||||
myParamsQuantity = new ArrayList<>();
|
||||
}
|
||||
return myParamsQuantity;
|
||||
}
|
||||
|
||||
public void setParamsQuantity(Collection<ResourceIndexedSearchParamQuantity> theQuantityParams) {
|
||||
|
@ -489,8 +350,11 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
getParamsQuantity().addAll(theQuantityParams);
|
||||
}
|
||||
|
||||
public void setParamsQuantityPopulated(boolean theParamsQuantityPopulated) {
|
||||
myParamsQuantityPopulated = theParamsQuantityPopulated;
|
||||
public Collection<ResourceIndexedSearchParamString> getParamsString() {
|
||||
if (myParamsString == null) {
|
||||
myParamsString = new ArrayList<>();
|
||||
}
|
||||
return myParamsString;
|
||||
}
|
||||
|
||||
public void setParamsString(Collection<ResourceIndexedSearchParamString> theParamsString) {
|
||||
|
@ -501,8 +365,11 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
getParamsString().addAll(theParamsString);
|
||||
}
|
||||
|
||||
public void setParamsStringPopulated(boolean theParamsStringPopulated) {
|
||||
myParamsStringPopulated = theParamsStringPopulated;
|
||||
public Collection<ResourceIndexedSearchParamToken> getParamsToken() {
|
||||
if (myParamsToken == null) {
|
||||
myParamsToken = new ArrayList<>();
|
||||
}
|
||||
return myParamsToken;
|
||||
}
|
||||
|
||||
public void setParamsToken(Collection<ResourceIndexedSearchParamToken> theParamsToken) {
|
||||
|
@ -513,8 +380,11 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
getParamsToken().addAll(theParamsToken);
|
||||
}
|
||||
|
||||
public void setParamsTokenPopulated(boolean theParamsTokenPopulated) {
|
||||
myParamsTokenPopulated = theParamsTokenPopulated;
|
||||
public Collection<ResourceIndexedSearchParamUri> getParamsUri() {
|
||||
if (myParamsUri == null) {
|
||||
myParamsUri = new ArrayList<>();
|
||||
}
|
||||
return myParamsUri;
|
||||
}
|
||||
|
||||
public void setParamsUri(Collection<ResourceIndexedSearchParamUri> theParamsUri) {
|
||||
|
@ -525,8 +395,8 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
getParamsUri().addAll(theParamsUri);
|
||||
}
|
||||
|
||||
public void setParamsUriPopulated(boolean theParamsUriPopulated) {
|
||||
myParamsUriPopulated = theParamsUriPopulated;
|
||||
public String getProfile() {
|
||||
return myProfile;
|
||||
}
|
||||
|
||||
public void setProfile(String theProfile) {
|
||||
|
@ -536,6 +406,13 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
myProfile = theProfile;
|
||||
}
|
||||
|
||||
public Collection<ResourceLink> getResourceLinks() {
|
||||
if (myResourceLinks == null) {
|
||||
myResourceLinks = new ArrayList<>();
|
||||
}
|
||||
return myResourceLinks;
|
||||
}
|
||||
|
||||
public void setResourceLinks(Collection<ResourceLink> theLinks) {
|
||||
if (!isHasLinks() && theLinks.isEmpty()) {
|
||||
return;
|
||||
|
@ -544,10 +421,112 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
getResourceLinks().addAll(theLinks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceType() {
|
||||
return myResourceType;
|
||||
}
|
||||
|
||||
public void setResourceType(String 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
|
||||
* and was not re-saved in the database
|
||||
|
@ -556,8 +535,12 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
|||
myUnchangedInCurrentOperation = theUnchangedInCurrentOperation;
|
||||
}
|
||||
|
||||
public void setVersion(long theVersion) {
|
||||
myVersion = theVersion;
|
||||
public void setContentTextParsedIntoWords(String theContentText) {
|
||||
myContentText = theContentText;
|
||||
}
|
||||
|
||||
public void setNarrativeTextParsedIntoWords(String theNarrativeText) {
|
||||
myNarrativeText = theNarrativeText;
|
||||
}
|
||||
|
||||
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) {
|
||||
ourLog.error("Exceeded maximum wait for connection", e);
|
||||
logGetConnectionStackTrace();
|
||||
if ("true".equals(System.getProperty("ci"))) {
|
||||
fail("Exceeded maximum wait for connection: " + e.toString());
|
||||
}
|
||||
System.exit(1);
|
||||
// if ("true".equals(System.getProperty("ci"))) {
|
||||
fail("Exceeded maximum wait for connection: " + e.toString());
|
||||
// }
|
||||
// System.exit(1);
|
||||
retVal = null;
|
||||
}
|
||||
|
||||
|
|
|
@ -236,6 +236,7 @@ public abstract class BaseJpaTest {
|
|||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamToken.class.getSimpleName() + " d").executeUpdate();
|
||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamUri.class.getSimpleName() + " d").executeUpdate();
|
||||
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamCoords.class.getSimpleName() + " d").executeUpdate();
|
||||
entityManager.createQuery("DELETE from " + ResourceIndexedCompositeStringUnique.class.getSimpleName() + " d").executeUpdate();
|
||||
entityManager.createQuery("DELETE from " + ResourceLink.class.getSimpleName() + " d").executeUpdate();
|
||||
entityManager.createQuery("DELETE from " + SearchResult.class.getSimpleName() + " d").executeUpdate();
|
||||
entityManager.createQuery("DELETE from " + SearchInclude.class.getSimpleName() + " d").executeUpdate();
|
||||
|
|
|
@ -57,8 +57,8 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
private static JpaValidationSupportChainDstu3 ourJpaValidationSupportChainDstu3;
|
||||
private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao;
|
||||
|
||||
// @Autowired
|
||||
// protected HapiWorkerContext myHapiWorkerContext;
|
||||
@Autowired
|
||||
protected IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
|
||||
@Autowired
|
||||
@Qualifier("myAllergyIntoleranceDaoDstu3")
|
||||
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 java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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.IValidationSupport;
|
||||
import org.hl7.fhir.dstu3.model.Observation;
|
||||
|
@ -53,6 +55,16 @@ public class SearchParamExtractorDstu3Test {
|
|||
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
|
||||
public void forceRefresh() {
|
||||
// nothing
|
||||
|
|
|
@ -47,17 +47,15 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
|||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
||||
//@formatter:off
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes= {TestR4Config.class})
|
||||
//@formatter:on
|
||||
public abstract class BaseJpaR4Test extends BaseJpaTest {
|
||||
|
||||
private static JpaValidationSupportChainR4 ourJpaValidationSupportChainR4;
|
||||
private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao;
|
||||
|
||||
// @Autowired
|
||||
// protected HapiWorkerContext myHapiWorkerContext;
|
||||
@Autowired
|
||||
protected IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
|
||||
@Autowired
|
||||
@Qualifier("myAllergyIntoleranceDaoR4")
|
||||
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 ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
|
||||
import org.hl7.fhir.r4.hapi.ctx.DefaultProfileValidationSupport;
|
||||
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
|
@ -45,6 +46,16 @@ public class SearchParamExtractorR4Test {
|
|||
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
|
||||
public void forceRefresh() {
|
||||
// nothing
|
||||
|
|
|
@ -49,7 +49,7 @@ public interface IServerOperationInterceptor extends IServerInterceptor {
|
|||
* User code may call this method to indicate to an interceptor that
|
||||
* 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
|
||||
void resourceUpdated(RequestDetails theRequest, IBaseResource theResource);
|
||||
|
|
|
@ -349,6 +349,12 @@
|
|||
HAPI FHIR 2.5, but this was not documented. This variable has now been
|
||||
documented as a part of the available features.
|
||||
</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 version="2.5" date="2017-06-08">
|
||||
<action type="fix">
|
||||
|
|
Loading…
Reference in New Issue