implements IDao,
}
}
-
/**
* This method is called when an update to an existing resource detects that the resource supplied for update is missing a tag/profile/security label that the currently persisted resource holds.
*
@@ -1334,11 +1110,6 @@ public abstract class BaseHapiFhirDao implements IDao,
return false;
}
- @PostConstruct
- public void startClearCaches() {
- myResourceTypeToDao = null;
- }
-
private ExpungeOutcome toExpungeOutcome(ExpungeOptions theExpungeOptions, AtomicInteger theRemainingCount) {
return new ExpungeOutcome()
.setDeletedCount(theExpungeOptions.getLimit() - theRemainingCount.get());
@@ -1452,7 +1223,6 @@ public abstract class BaseHapiFhirDao implements IDao,
return retVal;
}
- @Override
public String toResourceName(Class extends IBaseResource> theResourceType) {
return myContext.getResourceDefinition(theResourceType).getName();
}
@@ -1466,16 +1236,6 @@ public abstract class BaseHapiFhirDao implements IDao,
return new SliceImpl<>(Collections.singletonList(theVersion.getId()));
}
- @Override
- public Long translateForcedIdToPid(String theResourceName, String theResourceId) {
- return translateForcedIdToPids(getConfig(), new IdDt(theResourceName, theResourceId), myForcedIdDao).get(0);
- }
-
- protected List translateForcedIdToPids(IIdType theId) {
- return translateForcedIdToPids(getConfig(), theId, myForcedIdDao);
- }
-
-
@SuppressWarnings("unchecked")
protected ResourceTable updateEntity(RequestDetails theRequest, final IBaseResource theResource, ResourceTable
theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
@@ -1507,14 +1267,14 @@ public abstract class BaseHapiFhirDao implements IDao,
theEntity.setPublished(theUpdateTime);
}
- ResourceIndexedSearchParams existingParams = new ResourceIndexedSearchParams(this, theEntity);
+ ResourceIndexedSearchParams existingParams = new ResourceIndexedSearchParams(theEntity);
ResourceIndexedSearchParams newParams = null;
EncodedResource changed;
if (theDeletedTimestampOrNull != null) {
-
- newParams = new ResourceIndexedSearchParams(this);
+
+ newParams = new ResourceIndexedSearchParams();
theEntity.setDeleted(theDeletedTimestampOrNull);
theEntity.setUpdated(theDeletedTimestampOrNull);
@@ -1530,7 +1290,8 @@ public abstract class BaseHapiFhirDao implements IDao,
if (thePerformIndexing) {
- newParams = new ResourceIndexedSearchParams(this, theUpdateTime, theEntity, theResource, existingParams);
+ newParams = new ResourceIndexedSearchParams();
+ mySearchParamExtractorService.populateFromResource(newParams, this, theUpdateTime, theEntity, theResource, existingParams);
changed = populateResourceIntoEntity(theRequest, theResource, theEntity, true);
@@ -1540,10 +1301,10 @@ public abstract class BaseHapiFhirDao implements IDao,
} else {
theEntity.setLanguage(((IAnyResource) theResource).getLanguageElement().getValue());
}
-
- newParams.setParams(theEntity);
+
+ newParams.setParamsOn(theEntity);
theEntity.setIndexStatus(INDEX_STATUS_INDEXED);
- populateFullTextFields(theResource, theEntity);
+ populateFullTextFields(myContext, theResource, theEntity);
} else {
changed = populateResourceIntoEntity(theRequest, theResource, theEntity, false);
@@ -1616,7 +1377,6 @@ public abstract class BaseHapiFhirDao implements IDao,
*/
if (thePerformIndexing) {
Map presentSearchParams = new HashMap<>();
- // TODO KHS null check?
for (String nextKey : newParams.getPopulatedResourceLinkParameters()) {
presentSearchParams.put(nextKey, Boolean.TRUE);
}
@@ -1635,8 +1395,7 @@ public abstract class BaseHapiFhirDao implements IDao,
* Indexing
*/
if (thePerformIndexing) {
- newParams.removeCommon(theEntity, existingParams);
-
+ mySearchParamExtractorService.removeCommon(newParams, theEntity, existingParams);
} // if thePerformIndexing
if (theResource != null) {
@@ -1836,6 +1595,50 @@ public abstract class BaseHapiFhirDao implements IDao,
}
+ @Override
+ public ISearchParamRegistry getSearchParamRegistry() {
+ return mySearchParamRegistry;
+ }
+
+ public static void clearRequestAsProcessingSubRequest(ServletRequestDetails theRequestDetails) {
+ if (theRequestDetails != null) {
+ theRequestDetails.getUserData().remove(PROCESSING_SUB_REQUEST);
+ }
+ }
+
+ public static void markRequestAsProcessingSubRequest(ServletRequestDetails theRequestDetails) {
+ if (theRequestDetails != null) {
+ theRequestDetails.getUserData().put(PROCESSING_SUB_REQUEST, Boolean.TRUE);
+ }
+ }
+
+ public static String parseContentTextIntoWords(FhirContext theContext, IBaseResource theResource) {
+ StringBuilder retVal = new StringBuilder();
+ @SuppressWarnings("rawtypes")
+ List childElements = theContext.newTerser().getAllPopulatedChildElementsOfType(theResource, IPrimitiveType.class);
+ for (@SuppressWarnings("rawtypes")
+ IPrimitiveType nextType : childElements) {
+ if (nextType instanceof StringDt || nextType.getClass().getSimpleName().equals("StringType")) {
+ String nextValue = nextType.getValueAsString();
+ if (isNotBlank(nextValue)) {
+ retVal.append(nextValue.replace("\n", " ").replace("\r", " "));
+ retVal.append("\n");
+ }
+ }
+ }
+ return retVal.toString();
+ }
+
+ public static void populateFullTextFields(final FhirContext theContext, final IBaseResource theResource, ResourceTable theEntity) {
+ if (theEntity.getDeleted() != null) {
+ theEntity.setNarrativeTextParsedIntoWords(null);
+ theEntity.setContentTextParsedIntoWords(null);
+ } else {
+ theEntity.setNarrativeTextParsedIntoWords(parseNarrativeTextIntoWords(theResource));
+ theEntity.setContentTextParsedIntoWords(parseContentTextIntoWords(theContext, theResource));
+ }
+ }
+
public static String decodeResource(byte[] theResourceBytes, ResourceEncodingEnum theResourceEncoding) {
String resourceText = null;
switch (theResourceEncoding) {
@@ -1875,44 +1678,6 @@ public abstract class BaseHapiFhirDao implements IDao,
return bytes;
}
- protected static boolean isValidPid(IIdType theId) {
- if (theId == null || theId.getIdPart() == null) {
- return false;
- }
- String idPart = theId.getIdPart();
- for (int i = 0; i < idPart.length(); i++) {
- char nextChar = idPart.charAt(i);
- if (nextChar < '0' || nextChar > '9') {
- return false;
- }
- }
- return true;
- }
-
- @CoverageIgnore
- protected static IQueryParameterAnd> newInstanceAnd(String chain) {
- IQueryParameterAnd> type;
- Class extends IQueryParameterAnd>> clazz = RESOURCE_META_AND_PARAMS.get(chain);
- try {
- type = clazz.newInstance();
- } catch (Exception e) {
- throw new InternalErrorException("Failure creating instance of " + clazz, e);
- }
- return type;
- }
-
- @CoverageIgnore
- protected static IQueryParameterType newInstanceType(String chain) {
- IQueryParameterType type;
- Class extends IQueryParameterType> clazz = RESOURCE_META_PARAMS.get(chain);
- try {
- type = clazz.newInstance();
- } catch (Exception e) {
- throw new InternalErrorException("Failure creating instance of " + clazz, e);
- }
- return type;
- }
-
public static String normalizeString(String theString) {
CharArrayWriter outBuffer = new CharArrayWriter(theString.length());
@@ -1995,169 +1760,10 @@ public abstract class BaseHapiFhirDao implements IDao,
return retVal;
}
- protected static Long translateForcedIdToPid(DaoConfig theDaoConfig, String theResourceName, String theResourceId, IForcedIdDao
- theForcedIdDao) {
- return translateForcedIdToPids(theDaoConfig, new IdDt(theResourceName, theResourceId), theForcedIdDao).get(0);
- }
-
- static List translateForcedIdToPids(DaoConfig theDaoConfig, IIdType theId, IForcedIdDao theForcedIdDao) {
- Validate.isTrue(theId.hasIdPart());
-
- if (theDaoConfig.getResourceClientIdStrategy() != DaoConfig.ClientIdStrategyEnum.ANY && isValidPid(theId)) {
- return Collections.singletonList(theId.getIdPartAsLong());
- } else {
- List forcedId;
- if (theId.hasResourceType()) {
- forcedId = theForcedIdDao.findByTypeAndForcedId(theId.getResourceType(), theId.getIdPart());
- } else {
- forcedId = theForcedIdDao.findByForcedId(theId.getIdPart());
- }
-
- if (forcedId.isEmpty() == false) {
- List retVal = new ArrayList<>(forcedId.size());
- for (ForcedId next : forcedId) {
- retVal.add(next.getResourcePid());
- }
- return retVal;
- } else {
- throw new ResourceNotFoundException(theId);
- }
- }
- }
-
- public static SearchParameterMap translateMatchUrl(IDao theCallingDao, FhirContext theContext, String
- theMatchUrl, RuntimeResourceDefinition resourceDef) {
- SearchParameterMap paramMap = new SearchParameterMap();
- List parameters = translateMatchUrl(theMatchUrl);
-
- ArrayListMultimap nameToParamLists = ArrayListMultimap.create();
- for (NameValuePair next : parameters) {
- if (isBlank(next.getValue())) {
- continue;
- }
-
- String paramName = next.getName();
- String qualifier = null;
- for (int i = 0; i < paramName.length(); i++) {
- switch (paramName.charAt(i)) {
- case '.':
- case ':':
- qualifier = paramName.substring(i);
- paramName = paramName.substring(0, i);
- i = Integer.MAX_VALUE - 1;
- break;
- }
- }
-
- QualifiedParamList paramList = QualifiedParamList.splitQueryStringByCommasIgnoreEscape(qualifier, next.getValue());
- nameToParamLists.put(paramName, paramList);
- }
-
- for (String nextParamName : nameToParamLists.keySet()) {
- List paramList = nameToParamLists.get(nextParamName);
- if (Constants.PARAM_LASTUPDATED.equals(nextParamName)) {
- if (paramList != null && paramList.size() > 0) {
- if (paramList.size() > 2) {
- throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Can not have more than 2 " + Constants.PARAM_LASTUPDATED + " parameter repetitions");
- } else {
- DateRangeParam p1 = new DateRangeParam();
- p1.setValuesAsQueryTokens(theContext, nextParamName, paramList);
- paramMap.setLastUpdated(p1);
- }
- }
- continue;
- }
-
- if (Constants.PARAM_HAS.equals(nextParamName)) {
- IQueryParameterAnd> param = ParameterUtil.parseQueryParams(theContext, RestSearchParameterTypeEnum.HAS, nextParamName, paramList);
- paramMap.add(nextParamName, param);
- continue;
- }
-
- if (Constants.PARAM_COUNT.equals(nextParamName)) {
- if (paramList.size() > 0 && paramList.get(0).size() > 0) {
- String intString = paramList.get(0).get(0);
- try {
- paramMap.setCount(Integer.parseInt(intString));
- } catch (NumberFormatException e) {
- throw new InvalidRequestException("Invalid " + Constants.PARAM_COUNT + " value: " + intString);
- }
- }
- continue;
- }
-
- if (RESOURCE_META_PARAMS.containsKey(nextParamName)) {
- if (isNotBlank(paramList.get(0).getQualifier()) && paramList.get(0).getQualifier().startsWith(".")) {
- throw new InvalidRequestException("Invalid parameter chain: " + nextParamName + paramList.get(0).getQualifier());
- }
- IQueryParameterAnd> type = newInstanceAnd(nextParamName);
- type.setValuesAsQueryTokens(theContext, nextParamName, (paramList));
- paramMap.add(nextParamName, type);
- } else if (nextParamName.startsWith("_")) {
- // ignore these since they aren't search params (e.g. _sort)
- } else {
- 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);
- }
-
- IQueryParameterAnd> param = ParameterUtil.parseQueryParams(theContext, paramDef, nextParamName, paramList);
- paramMap.add(nextParamName, param);
- }
- }
- return paramMap;
- }
-
- public static List translateMatchUrl(String theMatchUrl) {
- List parameters;
- String matchUrl = theMatchUrl;
- int questionMarkIndex = matchUrl.indexOf('?');
- if (questionMarkIndex != -1) {
- matchUrl = matchUrl.substring(questionMarkIndex + 1);
- }
- matchUrl = matchUrl.replace("|", "%7C");
- matchUrl = matchUrl.replace("=>=", "=%3E%3D");
- matchUrl = matchUrl.replace("=<=", "=%3C%3D");
- matchUrl = matchUrl.replace("=>", "=%3E");
- matchUrl = matchUrl.replace("=<", "=%3C");
- if (matchUrl.contains(" ")) {
- throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - URL is invalid (must not contain spaces)");
- }
-
- parameters = URLEncodedUtils.parse((matchUrl), Constants.CHARSET_UTF8, '&');
- return parameters;
- }
-
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());
}
}
-
- @Override
- public ISearchParamExtractor getSearchParamExtractor() {
- return mySearchParamExtractor;
- }
-
- @Override
- public ISearchParamRegistry getSearchParamRegistry() {
- return mySearchParamRegistry;
- }
-
- @Override
- public EntityManager getEntityManager() {
- return myEntityManager;
- }
-
- @Override
- public Map, IFhirResourceDao>> getResourceTypeToDao() {
- return myResourceTypeToDao;
- }
-
- @Override
- public IForcedIdDao getForcedIdDao() {
- return myForcedIdDao;
- }
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
index f06d76d588e..a6e203009cd 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
@@ -25,7 +25,6 @@ import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.data.IResourceLinkDao;
-import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
@@ -75,8 +74,6 @@ public abstract class BaseHapiFhirResourceDao extends B
protected PlatformTransactionManager myPlatformTransactionManager;
@Autowired(required = false)
protected IFulltextSearchSvc mySearchDao;
- @Autowired()
- protected ISearchResultDao mySearchResultDao;
@Autowired
protected DaoConfig myDaoConfig;
@Autowired
@@ -86,6 +83,8 @@ public abstract class BaseHapiFhirResourceDao extends B
private String mySecondaryPrimaryKeyParamName;
@Autowired
private ISearchParamRegistry mySearchParamRegistry;
+ @Autowired
+ private MatchUrlService myMatchUrlService;
@Override
public void addTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
@@ -272,7 +271,7 @@ public abstract class BaseHapiFhirResourceDao extends B
public DeleteMethodOutcome deleteByUrl(String theUrl, List deleteConflicts, RequestDetails theRequest) {
StopWatch w = new StopWatch();
- Set resource = processMatchUrl(theUrl, myResourceType);
+ Set resource = myMatchUrlService.processMatchUrl(theUrl, myResourceType);
if (resource.size() > 1) {
if (myDaoConfig.isAllowMultipleDelete() == false) {
throw new PreconditionFailedException(getContext().getLocalizer().getMessage(BaseHapiFhirDao.class, "transactionOperationWithMultipleMatchFailure", "DELETE", theUrl, resource.size()));
@@ -372,7 +371,7 @@ public abstract class BaseHapiFhirResourceDao extends B
entity.setResourceType(toResourceName(theResource));
if (isNotBlank(theIfNoneExist)) {
- Set match = processMatchUrl(theIfNoneExist, myResourceType);
+ Set match = myMatchUrlService.processMatchUrl(theIfNoneExist, myResourceType);
if (match.size() > 1) {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirDao.class, "transactionOperationWithMultipleMatchFailure", "CREATE", theIfNoneExist, match.size());
throw new PreconditionFailedException(msg);
@@ -793,7 +792,7 @@ public abstract class BaseHapiFhirResourceDao extends B
myResourceName = def.getName();
if (mySecondaryPrimaryKeyParamName != null) {
- RuntimeSearchParam sp = getSearchParamByName(def, mySecondaryPrimaryKeyParamName);
+ RuntimeSearchParam sp = mySearchParamRegistry.getSearchParamByName(def, mySecondaryPrimaryKeyParamName);
if (sp == null) {
throw new ConfigurationException("Unknown search param on resource[" + myResourceName + "] for secondary key[" + mySecondaryPrimaryKeyParamName + "]");
}
@@ -849,7 +848,7 @@ public abstract class BaseHapiFhirResourceDao extends B
@Override
public Set processMatchUrl(String theMatchUrl) {
- return processMatchUrl(theMatchUrl, getResourceType());
+ return myMatchUrlService.processMatchUrl(theMatchUrl, getResourceType());
}
@Override
@@ -911,7 +910,7 @@ public abstract class BaseHapiFhirResourceDao extends B
public BaseHasResource readEntity(IIdType theId, boolean theCheckForForcedId) {
validateResourceTypeAndThrowIllegalArgumentException(theId);
- Long pid = translateForcedIdToPid(getResourceName(), theId.getIdPart());
+ Long pid = myIdHelperService.translateForcedIdToPid(getResourceName(), theId.getIdPart());
BaseHasResource entity = myEntityManager.find(ResourceTable.class, pid);
if (entity == null) {
@@ -951,7 +950,7 @@ public abstract class BaseHapiFhirResourceDao extends B
}
protected ResourceTable readEntityLatestVersion(IIdType theId) {
- ResourceTable entity = myEntityManager.find(ResourceTable.class, translateForcedIdToPid(getResourceName(), theId.getIdPart()));
+ ResourceTable entity = myEntityManager.find(ResourceTable.class, myIdHelperService.translateForcedIdToPid(getResourceName(), theId.getIdPart()));
if (entity == null) {
throw new ResourceNotFoundException(theId);
}
@@ -1192,7 +1191,7 @@ public abstract class BaseHapiFhirResourceDao extends B
// Should not be null since the check above would have caught it
RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(myResourceName);
- RuntimeSearchParam paramDef = getSearchParamByName(resourceDef, qualifiedParamName.getParamName());
+ RuntimeSearchParam paramDef = mySearchParamRegistry.getSearchParamByName(resourceDef, qualifiedParamName.getParamName());
for (String nextValue : theSource.get(nextParamName)) {
if (isNotBlank(nextValue)) {
@@ -1232,7 +1231,7 @@ public abstract class BaseHapiFhirResourceDao extends B
IIdType resourceId;
if (isNotBlank(theMatchUrl)) {
StopWatch sw = new StopWatch();
- Set match = processMatchUrl(theMatchUrl, myResourceType);
+ Set match = myMatchUrlService.processMatchUrl(theMatchUrl, myResourceType);
if (match.size() > 1) {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirDao.class, "transactionOperationWithMultipleMatchFailure", "UPDATE", theMatchUrl, match.size());
throw new PreconditionFailedException(msg);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java
index 830d5abbfc1..fd9be143c36 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java
@@ -1,7 +1,6 @@
package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
-import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.dao.data.ITermConceptDao;
import ca.uhn.fhir.jpa.util.ExpungeOptions;
import ca.uhn.fhir.jpa.util.ExpungeOutcome;
@@ -51,8 +50,6 @@ public abstract class BaseHapiFhirSystemDao extends BaseHapiFhirDao extends BaseHapiFhirDao implemen
protected abstract RuntimeSearchParam toRuntimeSp(SP theNextSp);
+ @Override
+ public RuntimeSearchParam getSearchParamByName(RuntimeResourceDefinition theResourceDef, String theParamName) {
+ Map params = getActiveSearchParams(theResourceDef.getName());
+ return params.get(theParamName);
+ }
+
+ @Override
+ public Collection getSearchParamsByResourceType(RuntimeResourceDefinition theResourceDef) {
+ return getActiveSearchParams(theResourceDef.getName()).values();
+ }
+
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java
index 5bc6c0879f1..e648969279f 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java
@@ -156,6 +156,7 @@ public class DaoConfig {
private List mySearchPreFetchThresholds = Arrays.asList(500, 2000, -1);
private List myWarmCacheEntries = new ArrayList<>();
private boolean myDisableHashBasedSearches;
+ private boolean myEnableInMemorySubscriptionMatching = true;
private ClientIdStrategyEnum myResourceClientIdStrategy = ClientIdStrategyEnum.ALPHANUMERIC;
/**
@@ -1448,6 +1449,50 @@ public class DaoConfig {
myDisableHashBasedSearches = theDisableHashBasedSearches;
}
+ /**
+ * If set to false
(default is true) the server will not use
+ * in-memory subscription searching and instead use the database matcher for all subscription
+ * criteria matching.
+ *
+ * When there are subscriptions registered
+ * on the server, the default behaviour is to compare the changed resource to the
+ * subscription criteria directly in-memory without going out to the database.
+ * Certain types of subscription criteria, e.g. chained references of queries with
+ * qualifiers or prefixes, are not supported by the in-memory matcher and will fall back
+ * to a database matcher.
+ *
+ * The database matcher performs a query against the
+ * database by prepending ?id=XYZ to the subscription criteria where XYZ is the id of the changed entity
+ *
+ * @since 3.6.1
+ */
+
+ public boolean isEnableInMemorySubscriptionMatching() {
+ return myEnableInMemorySubscriptionMatching;
+ }
+
+ /**
+ * If set to false
(default is true) the server will not use
+ * in-memory subscription searching and instead use the database matcher for all subscription
+ * criteria matching.
+ *
+ * When there are subscriptions registered
+ * on the server, the default behaviour is to compare the changed resource to the
+ * subscription criteria directly in-memory without going out to the database.
+ * Certain types of subscription criteria, e.g. chained references of queries with
+ * qualifiers or prefixes, are not supported by the in-memory matcher and will fall back
+ * to a database matcher.
+ *
+ * The database matcher performs a query against the
+ * database by prepending ?id=XYZ to the subscription criteria where XYZ is the id of the changed entity
+ *
+ * @since 3.6.1
+ */
+
+ public void setEnableInMemorySubscriptionMatching(boolean theEnableInMemorySubscriptionMatching) {
+ myEnableInMemorySubscriptionMatching = theEnableInMemorySubscriptionMatching;
+ }
+
public enum IndexEnabledEnum {
ENABLED,
DISABLED
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoRegistry.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoRegistry.java
index 613e76202a5..055aadf7951 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoRegistry.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoRegistry.java
@@ -23,22 +23,27 @@ package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
+import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
+@Component("myDaoRegistry")
public class DaoRegistry implements ApplicationContextAware {
private ApplicationContext myAppCtx;
@Autowired
- private FhirContext myCtx;
+ private FhirContext myContext;
+
private volatile Map> myResourceNameToResourceDao;
private volatile IFhirSystemDao, ?> mySystemDao;
@@ -47,8 +52,8 @@ public class DaoRegistry implements ApplicationContextAware {
myAppCtx = theApplicationContext;
}
- public IFhirSystemDao, ?> getSystemDao() {
- IFhirSystemDao, ?> retVal = mySystemDao;
+ public IFhirSystemDao getSystemDao() {
+ IFhirSystemDao retVal = mySystemDao;
if (retVal == null) {
retVal = myAppCtx.getBean(IFhirSystemDao.class);
mySystemDao = retVal;
@@ -56,10 +61,11 @@ public class DaoRegistry implements ApplicationContextAware {
return retVal;
}
- public IFhirResourceDao> getResourceDao(String theResourceName) {
- IFhirResourceDao> retVal = getResourceNameToResourceDao().get(theResourceName);
+ public IFhirResourceDao getResourceDao(String theResourceName) {
+ init();
+ IFhirResourceDao retVal = myResourceNameToResourceDao.get(theResourceName);
if (retVal == null) {
- List supportedResourceTypes = getResourceNameToResourceDao()
+ List supportedResourceTypes = myResourceNameToResourceDao
.keySet()
.stream()
.sorted()
@@ -67,26 +73,54 @@ public class DaoRegistry implements ApplicationContextAware {
throw new InvalidRequestException("Unable to process request, this server does not know how to handle resources of type " + theResourceName + " - Can handle: " + supportedResourceTypes);
}
return retVal;
-
}
- public IFhirResourceDao getResourceDao(Class theResourceType) {
- String resourceName = myCtx.getResourceDefinition(theResourceType).getName();
+ public IFhirResourceDao getResourceDao(Class theResourceType) {
+ IFhirResourceDao retVal = getResourceDaoIfExists(theResourceType);
+ Validate.notNull(retVal, "No DAO exists for resource type %s - Have: %s", theResourceType, myResourceNameToResourceDao);
+ return retVal;
+ }
+
+ public IFhirResourceDao getResourceDaoIfExists(Class theResourceType) {
+ String resourceName = myContext.getResourceDefinition(theResourceType).getName();
return (IFhirResourceDao) getResourceDao(resourceName);
}
- private Map> getResourceNameToResourceDao() {
- Map> retVal = myResourceNameToResourceDao;
- if (retVal == null || retVal.isEmpty()) {
- retVal = new HashMap<>();
- Map resourceDaos = myAppCtx.getBeansOfType(IFhirResourceDao.class);
- for (IFhirResourceDao nextResourceDao : resourceDaos.values()) {
- RuntimeResourceDefinition nextResourceDef = myCtx.getResourceDefinition(nextResourceDao.getResourceType());
- retVal.put(nextResourceDef.getName(), nextResourceDao);
- }
- myResourceNameToResourceDao = retVal;
+ private void init() {
+ if (myResourceNameToResourceDao != null && !myResourceNameToResourceDao.isEmpty()) {
+ return;
+ }
+
+ Map resourceDaos = myAppCtx.getBeansOfType(IFhirResourceDao.class);
+
+ initializeMaps(resourceDaos.values());
+ }
+
+ private void initializeMaps(Collection theResourceDaos) {
+
+ myResourceNameToResourceDao = new HashMap<>();
+
+ for (IFhirResourceDao nextResourceDao : theResourceDaos) {
+ RuntimeResourceDefinition nextResourceDef = myContext.getResourceDefinition(nextResourceDao.getResourceType());
+ myResourceNameToResourceDao.put(nextResourceDef.getName(), nextResourceDao);
+ }
+ }
+
+ public IFhirResourceDao getDaoOrThrowException(Class extends IBaseResource> theClass) {
+ IFhirResourceDao retVal = getResourceDao(theClass);
+ if (retVal == null) {
+ List supportedResourceNames = myResourceNameToResourceDao
+ .keySet()
+ .stream()
+ .map(t -> myContext.getResourceDefinition(t).getName())
+ .sorted()
+ .collect(Collectors.toList());
+ throw new InvalidRequestException("Unable to process request, this server does not know how to handle resources of type " + myContext.getResourceDefinition(theClass).getName() + " - Can handle: " + supportedResourceNames);
}
return retVal;
}
+ public void setResourceDaos(Collection theResourceDaos) {
+ initializeMaps(theResourceDaos);
+ }
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java
index 4c2dcf968bc..1c583a4c665 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java
@@ -68,7 +68,6 @@ import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
-import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import javax.persistence.TypedQuery;
@@ -82,6 +81,8 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
@Autowired
private PlatformTransactionManager myTxManager;
@Autowired
+ private MatchUrlService myMatchUrlService;
+ @Autowired
private DaoRegistry myDaoRegistry;
private Bundle batch(final RequestDetails theRequestDetails, Bundle theRequest) {
@@ -243,7 +244,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao {
requestDetails.setParameters(new HashMap());
if (qIndex != -1) {
String params = url.substring(qIndex);
- List parameters = translateMatchUrl(params);
+ List parameters = myMatchUrlService.translateMatchUrl(params);
for (NameValuePair next : parameters) {
paramValues.put(next.getName(), next.getValue());
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
index 7b0f0930f2f..2250da4d406 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
@@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
import ca.uhn.fhir.jpa.entity.ResourceTable;
+import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.param.StringParam;
@@ -41,7 +42,6 @@ import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.query.dsl.BooleanJunction;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hl7.fhir.dstu3.model.BaseResource;
-import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
@@ -65,11 +65,14 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
@Autowired
protected IForcedIdDao myForcedIdDao;
- private Boolean ourDisabled;
-
@Autowired
private DaoConfig myDaoConfig;
+ @Autowired
+ private IdHelperService myIdHelperService;
+
+ private Boolean ourDisabled;
+
/**
* Constructor
*/
@@ -225,7 +228,7 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
StringParam idParm = (StringParam) idParam;
idParamValue = idParm.getValue();
}
- pid = BaseHapiFhirDao.translateForcedIdToPid(myDaoConfig, theResourceName, idParamValue, myForcedIdDao);
+ pid = myIdHelperService.translateForcedIdToPid(theResourceName, idParamValue);
}
Long referencingPid = pid;
@@ -278,7 +281,7 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
if (contextParts.length != 3 || "Patient".equals(contextParts[0]) == false || "$everything".equals(contextParts[2]) == false) {
throw new InvalidRequestException("Invalid context: " + theContext);
}
- Long pid = BaseHapiFhirDao.translateForcedIdToPid( myDaoConfig, contextParts[0], contextParts[1], myForcedIdDao);
+ Long pid = myIdHelperService.translateForcedIdToPid(contextParts[0], contextParts[1]);
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IDao.java
index 81c67874815..29a3b295ed8 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IDao.java
@@ -1,17 +1,13 @@
package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.context.RuntimeResourceDefinition;
-import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.IBaseResourceEntity;
-import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.ResourceTag;
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.util.Collection;
-import java.util.Set;
/*
* #%L
@@ -41,10 +37,6 @@ public interface IDao {
FhirContext getContext();
- RuntimeSearchParam getSearchParamByName(RuntimeResourceDefinition theResourceDef, String theParamName);
-
- Collection getSearchParamsByResourceType(RuntimeResourceDefinition theResourceDef);
-
/**
* Populate all of the runtime dependencies that a bundle provider requires in order to work
*/
@@ -52,12 +44,9 @@ public interface IDao {
ISearchBuilder newSearchBuilder();
- void populateFullTextFields(IBaseResource theResource, ResourceTable theEntity);
-
- Set processMatchUrl(String theMatchUrl, Class theResourceType);
-
IBaseResource toResource(BaseHasResource theEntity, boolean theForHistoryOperation);
R toResource(Class theResourceType, IBaseResourceEntity theEntity, Collection