Work on multitenancy

This commit is contained in:
jamesagnew 2020-04-15 22:12:28 -04:00
parent d822867a4a
commit cd06137745
10 changed files with 143 additions and 48 deletions

76
2q Normal file
View File

@ -0,0 +1,76 @@
Merge branch 'master' into ja_20200206_multitenancy
# Conflicts:
# hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java
# hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java
# hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceLinkExtractor.java
# hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InlineResourceLinkResolver.java
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
# .git/MERGE_HEAD
# and try again.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch ja_20200206_multitenancy
# Your branch is up to date with 'origin/ja_20200206_multitenancy'.
#
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
# new file: hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseErrorHandler.java
# modified: hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java
# modified: hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java
# modified: hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/LenientErrorHandler.java
# modified: hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParseLocation.java
# modified: hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java
# modified: hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/StrictErrorHandler.java
# modified: hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/json/jackson/JacksonStructure.java
# new file: hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1793-report-attr-name-in-json-parse-errors.yaml
# new file: hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1801-allow-parsing-contained-in-jpa.yaml
# new file: hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/1801-handle-persisted-leading-decimals.yaml
# modified: hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java
# modified: hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
# new file: hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TolerantJsonParser.java
# modified: hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolver.java
# modified: hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java
# modified: hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/xmlpatch/XmlPatchUtils.java
# new file: hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/TolerantJsonParserR4Test.java
# modified: hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java
# modified: hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchCustomSearchParamTest.java
# modified: hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchCustomSearchParamTest.java
# modified: hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java
# modified: hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java
# modified: hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java
# modified: hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParams.java
# deleted: hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceLinkExtractor.java
# modified: hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java
# modified: hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/IndexedSearchParamExtractor.java
# deleted: hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InlineResourceLinkResolver.java
# modified: hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/SearchParamMatcher.java
# modified: hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2_1Test.java
# modified: hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2_1Test.java
# modified: hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/validation/ResourceValidatorDstu2Test.java
# modified: hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java
# modified: hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java
# modified: hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestHl7OrgDstu2.java
# modified: hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/JsonParserR4Test.java
# modified: hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseSizeCapturingInterceptorTest.java
# new file: hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/ProxyUtil.java
# modified: hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu2016may/hapi/validation/ResourceValidatorDstu2_1Test.java
# modified: hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/ResourceValidatorDstu3Test.java
#
# Changes not staged for commit:
# modified: hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
# modified: hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java
# modified: hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java
# modified: hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningR4Test.java
# modified: hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/config/PartitionConfig.java
# modified: hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PartitionId.java
# modified: hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java
# modified: hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/IndexedSearchParamExtractor.java
# modified: hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/SearchParamMatcher.java
#

View File

@ -998,7 +998,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
if (thePerformIndexing) { if (thePerformIndexing) {
newParams = new ResourceIndexedSearchParams(); newParams = new ResourceIndexedSearchParams();
mySearchParamWithInlineReferencesExtractor.populateFromResource(myPartitionConfig, newParams, theUpdateTime, entity, theResource, existingParams, theRequest); mySearchParamWithInlineReferencesExtractor.populateFromResource(newParams, theUpdateTime, entity, theResource, existingParams, theRequest);
changed = populateResourceIntoEntity(theRequest, theResource, entity, true); changed = populateResourceIntoEntity(theRequest, theResource, entity, true);
if (changed.isChanged()) { if (changed.isChanged()) {

View File

@ -63,7 +63,6 @@ import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -146,7 +145,7 @@ public class IdHelperService {
if (myDaoConfig.isDeleteEnabled()) { if (myDaoConfig.isDeleteEnabled()) {
retVal = resolveResourceIdentity(thePartitionId, theResourceType, theId); retVal = resolveResourceIdentity(thePartitionId, theResourceType, theId);
} else { } else {
String key = stringifyForKey(thePartitionId) + "/" + theResourceType + "/" + theId; String key = PartitionId.stringifyForKey(thePartitionId) + "/" + theResourceType + "/" + theId;
retVal = myPersistentIdCache.get(key, t -> resolveResourceIdentity(thePartitionId, theResourceType, theId)); retVal = myPersistentIdCache.get(key, t -> resolveResourceIdentity(thePartitionId, theResourceType, theId));
} }
@ -196,7 +195,7 @@ public class IdHelperService {
for (Iterator<String> idIterator = nextIds.iterator(); idIterator.hasNext(); ) { for (Iterator<String> idIterator = nextIds.iterator(); idIterator.hasNext(); ) {
String nextId = idIterator.next(); String nextId = idIterator.next();
String key = stringifyForKey(thePartitionId) + "/" + nextResourceType + "/" + nextId; String key = PartitionId.stringifyForKey(thePartitionId) + "/" + nextResourceType + "/" + nextId;
Long nextCachedPid = myPersistentIdCache.getIfPresent(key); Long nextCachedPid = myPersistentIdCache.getIfPresent(key);
if (nextCachedPid != null) { if (nextCachedPid != null) {
idIterator.remove(); idIterator.remove();
@ -221,7 +220,7 @@ public class IdHelperService {
Long pid = (Long) nextView[1]; Long pid = (Long) nextView[1];
retVal.add(new ResourcePersistentId(pid)); retVal.add(new ResourcePersistentId(pid));
String key = stringifyForKey(thePartitionId) + "/" + nextResourceType + "/" + forcedId; String key = PartitionId.stringifyForKey(thePartitionId) + "/" + nextResourceType + "/" + forcedId;
myPersistentIdCache.put(key, pid); myPersistentIdCache.put(key, pid);
} }
} }
@ -407,14 +406,6 @@ public class IdHelperService {
.build(); .build();
} }
private static String stringifyForKey(PartitionId thePartitionId) {
String retVal = "(null)";
if (thePartitionId != null) {
retVal = thePartitionId.getPartitionIdStringOrNullString();
}
return retVal;
}
public static boolean isValidPid(IIdType theId) { public static boolean isValidPid(IIdType theId) {
if (theId == null) { if (theId == null) {
return false; return false;

View File

@ -30,6 +30,7 @@ import ca.uhn.fhir.jpa.dao.data.IResourceIndexedCompositeStringUniqueDao;
import ca.uhn.fhir.jpa.model.config.PartitionConfig; import ca.uhn.fhir.jpa.model.config.PartitionConfig;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId; import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam; import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.PartitionId;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceLink; import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.entity.ResourceTable;
@ -70,7 +71,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
@Lazy @Lazy
public class SearchParamWithInlineReferencesExtractor { public class SearchParamWithInlineReferencesExtractor {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamWithInlineReferencesExtractor.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamWithInlineReferencesExtractor.class);
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
protected EntityManager myEntityManager;
@Autowired @Autowired
private MatchResourceUrlService myMatchResourceUrlService; private MatchResourceUrlService myMatchResourceUrlService;
@Autowired @Autowired
@ -89,18 +91,17 @@ public class SearchParamWithInlineReferencesExtractor {
private DaoSearchParamSynchronizer myDaoSearchParamSynchronizer; private DaoSearchParamSynchronizer myDaoSearchParamSynchronizer;
@Autowired @Autowired
private IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao; private IResourceIndexedCompositeStringUniqueDao myResourceIndexedCompositeStringUniqueDao;
@Autowired
private PartitionConfig myPartitionConfig;
@PersistenceContext(type = PersistenceContextType.TRANSACTION) public void populateFromResource(ResourceIndexedSearchParams theParams, Date theUpdateTime, ResourceTable theEntity, IBaseResource theResource, ResourceIndexedSearchParams theExistingParams, RequestDetails theRequest) {
protected EntityManager myEntityManager;
public void populateFromResource(PartitionConfig thePartitionConfig, ResourceIndexedSearchParams theParams, Date theUpdateTime, ResourceTable theEntity, IBaseResource theResource, ResourceIndexedSearchParams theExistingParams, RequestDetails theRequest) {
extractInlineReferences(theResource, theRequest); extractInlineReferences(theResource, theRequest);
mySearchParamExtractorService.extractFromResource(theRequest, theParams, theEntity, theResource, theUpdateTime, true); mySearchParamExtractorService.extractFromResource(theEntity.getPartitionId(), theRequest, theParams, theEntity, theResource, theUpdateTime, true);
Set<Map.Entry<String, RuntimeSearchParam>> activeSearchParams = mySearchParamRegistry.getActiveSearchParams(theEntity.getResourceType()).entrySet(); Set<Map.Entry<String, RuntimeSearchParam>> activeSearchParams = mySearchParamRegistry.getActiveSearchParams(theEntity.getResourceType()).entrySet();
if (myDaoConfig.getIndexMissingFields() == DaoConfig.IndexEnabledEnum.ENABLED) { if (myDaoConfig.getIndexMissingFields() == DaoConfig.IndexEnabledEnum.ENABLED) {
theParams.findMissingSearchParams(thePartitionConfig, myDaoConfig.getModelConfig(), theEntity, activeSearchParams); theParams.findMissingSearchParams(myPartitionConfig, myDaoConfig.getModelConfig(), theEntity, activeSearchParams);
} }
/* /*
@ -205,7 +206,6 @@ public class SearchParamWithInlineReferencesExtractor {
} }
/** /**
* Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the * Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the
* matching resource. * matching resource.

View File

@ -3,7 +3,6 @@ package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.entity.PartitionEntity; import ca.uhn.fhir.jpa.entity.PartitionEntity;
import ca.uhn.fhir.jpa.model.config.PartitionConfig; import ca.uhn.fhir.jpa.model.config.PartitionConfig;
import ca.uhn.fhir.jpa.model.entity.*; import ca.uhn.fhir.jpa.model.entity.*;
@ -81,7 +80,7 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
myPartitionConfig.setIncludePartitionInSearchHashes(new PartitionConfig().isIncludePartitionInSearchHashes()); myPartitionConfig.setIncludePartitionInSearchHashes(new PartitionConfig().isIncludePartitionInSearchHashes());
myPartitionConfig.setPartitioningEnabled(new PartitionConfig().isPartitioningEnabled()); myPartitionConfig.setPartitioningEnabled(new PartitionConfig().isPartitioningEnabled());
myPartitionConfig.setAllowReferencesAcrossPartitions(new PartitionConfig().isAllowReferencesAcrossPartitions()); myPartitionConfig.setAllowReferencesAcrossPartitions(new PartitionConfig().getAllowReferencesAcrossPartitions());
myInterceptorRegistry.unregisterInterceptorsIf(t -> t instanceof MyInterceptor); myInterceptorRegistry.unregisterInterceptorsIf(t -> t instanceof MyInterceptor);
myInterceptor = null; myInterceptor = null;
@ -140,7 +139,7 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
@Test @Test
public void testCreate_CrossPartitionReference_ByPid_Allowed() { public void testCreate_CrossPartitionReference_ByPid_Allowed() {
myPartitionConfig.setAllowReferencesAcrossPartitions(true); myPartitionConfig.setAllowReferencesAcrossPartitions(PartitionConfig.CrossPartitionReferenceMode.ALLOWED_UNQUALIFIED);
// Create patient in partition 1 // Create patient in partition 1
addCreatePartition(myPartitionId, myPartitionDate); addCreatePartition(myPartitionId, myPartitionDate);
@ -188,7 +187,7 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
@Test @Test
public void testCreate_CrossPartitionReference_ByForcedId_Allowed() { public void testCreate_CrossPartitionReference_ByForcedId_Allowed() {
myPartitionConfig.setAllowReferencesAcrossPartitions(true); myPartitionConfig.setAllowReferencesAcrossPartitions(PartitionConfig.CrossPartitionReferenceMode.ALLOWED_UNQUALIFIED);
// Create patient in partition 1 // Create patient in partition 1
addCreatePartition(myPartitionId, myPartitionDate); addCreatePartition(myPartitionId, myPartitionDate);
@ -238,8 +237,6 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
@Test @Test
public void testCreate_SamePartitionReference_DefaultPartition_ByPid() { public void testCreate_SamePartitionReference_DefaultPartition_ByPid() {
myPartitionConfig.setAllowReferencesAcrossPartitions(true);
// Create patient in partition NULL // Create patient in partition NULL
addCreateNoPartitionId(myPartitionDate); addCreateNoPartitionId(myPartitionDate);
Patient patient = new Patient(); Patient patient = new Patient();
@ -263,8 +260,6 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
@Test @Test
public void testCreate_SamePartitionReference_DefaultPartition_ByForcedId() { public void testCreate_SamePartitionReference_DefaultPartition_ByForcedId() {
myPartitionConfig.setAllowReferencesAcrossPartitions(true);
// Create patient in partition NULL // Create patient in partition NULL
addCreateNoPartitionId(myPartitionDate); addCreateNoPartitionId(myPartitionDate);
Patient patient = new Patient(); Patient patient = new Patient();

View File

@ -6,7 +6,7 @@ package ca.uhn.fhir.jpa.model.config;
public class PartitionConfig { public class PartitionConfig {
private boolean myPartitioningEnabled = false; private boolean myPartitioningEnabled = false;
private boolean myAllowReferencesAcrossPartitions = false; private CrossPartitionReferenceMode myAllowReferencesAcrossPartitions = CrossPartitionReferenceMode.NOT_ALLOWED;
private boolean myIncludePartitionInSearchHashes = true; private boolean myIncludePartitionInSearchHashes = true;
/** /**
@ -54,20 +54,36 @@ public class PartitionConfig {
} }
/** /**
* Should resources references be permitted to cross partition boundaries. Default is <code>false</code>. * Should resources references be permitted to cross partition boundaries. Default is {@link CrossPartitionReferenceMode#NOT_ALLOWED}.
* *
* @since 5.0.0 * @since 5.0.0
*/ */
public boolean isAllowReferencesAcrossPartitions() { public CrossPartitionReferenceMode getAllowReferencesAcrossPartitions() {
return myAllowReferencesAcrossPartitions; return myAllowReferencesAcrossPartitions;
} }
/** /**
* Should resources references be permitted to cross partition boundaries. Default is <code>false</code>. * Should resources references be permitted to cross partition boundaries. Default is {@link CrossPartitionReferenceMode#NOT_ALLOWED}.
* *
* @since 5.0.0 * @since 5.0.0
*/ */
public void setAllowReferencesAcrossPartitions(boolean theAllowReferencesAcrossPartitions) { public void setAllowReferencesAcrossPartitions(CrossPartitionReferenceMode theAllowReferencesAcrossPartitions) {
myAllowReferencesAcrossPartitions = theAllowReferencesAcrossPartitions; myAllowReferencesAcrossPartitions = theAllowReferencesAcrossPartitions;
} }
public enum CrossPartitionReferenceMode {
/**
* References between resources are not allowed to cross partition boundaries
*/
NOT_ALLOWED,
/**
* References can cross partition boundaries, in a way that hides the existence of partitions to the end user
*/
ALLOWED_UNQUALIFIED
}
} }

View File

@ -71,4 +71,15 @@ public class PartitionId implements Cloneable {
public String getPartitionIdStringOrNullString() { public String getPartitionIdStringOrNullString() {
return defaultIfNull(myPartitionId, "null").toString(); return defaultIfNull(myPartitionId, "null").toString();
} }
/**
* Create a string representation suitable for use as a cache key. Null aware.
*/
public static String stringifyForKey(PartitionId thePartitionId) {
String retVal = "(null)";
if (thePartitionId != null) {
retVal = thePartitionId.getPartitionIdStringOrNullString();
}
return retVal;
}
} }

View File

@ -26,6 +26,7 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.model.config.PartitionConfig;
import ca.uhn.fhir.jpa.model.cross.IResourceLookup; import ca.uhn.fhir.jpa.model.cross.IResourceLookup;
import ca.uhn.fhir.jpa.model.entity.*; import ca.uhn.fhir.jpa.model.entity.*;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage; import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
@ -64,6 +65,8 @@ public class SearchParamExtractorService {
private FhirContext myContext; private FhirContext myContext;
@Autowired @Autowired
private ISearchParamRegistry mySearchParamRegistry; private ISearchParamRegistry mySearchParamRegistry;
@Autowired
private PartitionConfig myPartitionConfig;
@Autowired(required = false) @Autowired(required = false)
private IResourceLinkResolver myResourceLinkResolver; private IResourceLinkResolver myResourceLinkResolver;
@ -71,14 +74,14 @@ public class SearchParamExtractorService {
* This method is responsible for scanning a resource for all of the search parameter instances. I.e. for all search parameters defined for * This method is responsible for scanning a resource for all of the search parameter instances. I.e. for all search parameters defined for
* a given resource type, it extracts the associated indexes and populates {@literal theParams}. * a given resource type, it extracts the associated indexes and populates {@literal theParams}.
*/ */
public void extractFromResource(RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, Date theUpdateTime, boolean theFailOnInvalidReference) { public void extractFromResource(PartitionId thePartitionId, RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, Date theUpdateTime, boolean theFailOnInvalidReference) {
IBaseResource resource = normalizeResource(theResource); IBaseResource resource = normalizeResource(theResource);
// All search parameter types except Reference // All search parameter types except Reference
extractSearchIndexParameters(theRequestDetails, theParams, resource, theEntity); extractSearchIndexParameters(theRequestDetails, theParams, resource, theEntity);
// Reference search parameters // Reference search parameters
extractResourceLinks(theParams, theEntity, resource, theUpdateTime, theFailOnInvalidReference, theRequestDetails); extractResourceLinks(thePartitionId, theParams, theEntity, resource, theUpdateTime, theFailOnInvalidReference, theRequestDetails);
theParams.setUpdatedTime(theUpdateTime); theParams.setUpdatedTime(theUpdateTime);
} }
@ -155,7 +158,7 @@ public class SearchParamExtractorService {
return theResource; return theResource;
} }
private void extractResourceLinks(ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, Date theUpdateTime, boolean theFailOnInvalidReference, RequestDetails theRequest) { private void extractResourceLinks(PartitionId thePartitionId, ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, Date theUpdateTime, boolean theFailOnInvalidReference, RequestDetails theRequest) {
String resourceName = myContext.getResourceDefinition(theResource).getName(); String resourceName = myContext.getResourceDefinition(theResource).getName();
ISearchParamExtractor.SearchParamSet<PathAndRef> refs = mySearchParamExtractor.extractResourceLinks(theResource); ISearchParamExtractor.SearchParamSet<PathAndRef> refs = mySearchParamExtractor.extractResourceLinks(theResource);
@ -164,13 +167,13 @@ public class SearchParamExtractorService {
Map<String, IResourceLookup> resourceIdToResolvedTarget = new HashMap<>(); Map<String, IResourceLookup> resourceIdToResolvedTarget = new HashMap<>();
for (PathAndRef nextPathAndRef : refs) { for (PathAndRef nextPathAndRef : refs) {
RuntimeSearchParam searchParam = mySearchParamRegistry.getActiveSearchParam(resourceName, nextPathAndRef.getSearchParamName()); RuntimeSearchParam searchParam = mySearchParamRegistry.getActiveSearchParam(resourceName, nextPathAndRef.getSearchParamName());
extractResourceLinks(theParams, theEntity, theUpdateTime, searchParam, nextPathAndRef, theFailOnInvalidReference, theRequest, resourceIdToResolvedTarget); extractResourceLinks(thePartitionId, theParams, theEntity, theUpdateTime, searchParam, nextPathAndRef, theFailOnInvalidReference, theRequest, resourceIdToResolvedTarget);
} }
theEntity.setHasLinks(theParams.myLinks.size() > 0); theEntity.setHasLinks(theParams.myLinks.size() > 0);
} }
private void extractResourceLinks(ResourceIndexedSearchParams theParams, ResourceTable theEntity, Date theUpdateTime, RuntimeSearchParam theRuntimeSearchParam, PathAndRef thePathAndRef, boolean theFailOnInvalidReference, RequestDetails theRequest, Map<String, IResourceLookup> theResourceIdToResolvedTarget) { private void extractResourceLinks(PartitionId thePartitionId, ResourceIndexedSearchParams theParams, ResourceTable theEntity, Date theUpdateTime, RuntimeSearchParam theRuntimeSearchParam, PathAndRef thePathAndRef, boolean theFailOnInvalidReference, RequestDetails theRequest, Map<String, IResourceLookup> theResourceIdToResolvedTarget) {
IBaseReference nextReference = thePathAndRef.getRef(); IBaseReference nextReference = thePathAndRef.getRef();
IIdType nextId = nextReference.getReferenceElement(); IIdType nextId = nextReference.getReferenceElement();
String path = thePathAndRef.getPath(); String path = thePathAndRef.getPath();
@ -255,7 +258,7 @@ public class SearchParamExtractorService {
if (theFailOnInvalidReference) { if (theFailOnInvalidReference) {
myResourceLinkResolver.validateTypeOrThrowException(type); myResourceLinkResolver.validateTypeOrThrowException(type);
resourceLink = resolveTargetAndCreateResourceLinkOrReturnNull(theEntity, theUpdateTime, theRuntimeSearchParam, path, thePathAndRef, nextId, typeString, type, nextReference, theRequest, theResourceIdToResolvedTarget); resourceLink = resolveTargetAndCreateResourceLinkOrReturnNull(thePartitionId, theEntity, theUpdateTime, theRuntimeSearchParam, path, thePathAndRef, nextId, typeString, type, nextReference, theRequest, theResourceIdToResolvedTarget);
if (resourceLink == null) { if (resourceLink == null) {
return; return;
} }
@ -272,7 +275,7 @@ public class SearchParamExtractorService {
theParams.myLinks.add(resourceLink); theParams.myLinks.add(resourceLink);
} }
private ResourceLink resolveTargetAndCreateResourceLinkOrReturnNull(ResourceTable theEntity, Date theUpdateTime, RuntimeSearchParam nextSpDef, String theNextPathsUnsplit, PathAndRef nextPathAndRef, IIdType theNextId, String theTypeString, Class<? extends IBaseResource> theType, IBaseReference theReference, RequestDetails theRequest, Map<String, IResourceLookup> theResourceIdToResolvedTarget) { private ResourceLink resolveTargetAndCreateResourceLinkOrReturnNull(PartitionId thePartitionId, ResourceTable theEntity, Date theUpdateTime, RuntimeSearchParam nextSpDef, String theNextPathsUnsplit, PathAndRef nextPathAndRef, IIdType theNextId, String theTypeString, Class<? extends IBaseResource> theType, IBaseReference theReference, RequestDetails theRequest, Map<String, IResourceLookup> theResourceIdToResolvedTarget) {
/* /*
* We keep a cache of resolved target resources. This is good since for some resource types, there * We keep a cache of resolved target resources. This is good since for some resource types, there
* are multiple search parameters that map to the same element path within a resource (e.g. * are multiple search parameters that map to the same element path within a resource (e.g.
@ -280,16 +283,22 @@ public class SearchParamExtractorService {
* target any more times than we have to. * target any more times than we have to.
*/ */
IResourceLookup targetResource = theResourceIdToResolvedTarget.get(theNextId.getValue()); PartitionId targetPartitionId = thePartitionId;
if (myPartitionConfig.isPartitioningEnabled() && myPartitionConfig.getAllowReferencesAcrossPartitions() == PartitionConfig.CrossPartitionReferenceMode.ALLOWED_UNQUALIFIED) {
targetPartitionId = null;
}
String key = PartitionId.stringifyForKey(targetPartitionId) + "/" + theNextId.getValue();
IResourceLookup targetResource = theResourceIdToResolvedTarget.get(key);
if (targetResource == null) { if (targetResource == null) {
targetResource = myResourceLinkResolver.findTargetResource(nextSpDef, theNextPathsUnsplit, theNextId, theTypeString, theType, theReference, theRequest); targetResource = myResourceLinkResolver.findTargetResource(targetPartitionId, nextSpDef, theNextPathsUnsplit, theNextId, theTypeString, theType, theReference, theRequest);
} }
if (targetResource == null) { if (targetResource == null) {
return null; return null;
} }
theResourceIdToResolvedTarget.put(theNextId.getValue(), targetResource); theResourceIdToResolvedTarget.put(key, targetResource);
String targetResourceType = targetResource.getResourceType(); String targetResourceType = targetResource.getResourceType();
Long targetResourcePid = targetResource.getResourceId(); Long targetResourcePid = targetResource.getResourceId();

View File

@ -39,7 +39,7 @@ public class IndexedSearchParamExtractor {
String resourceType = myContext.getResourceDefinition(theResource).getName(); String resourceType = myContext.getResourceDefinition(theResource).getName();
entity.setResourceType(resourceType); entity.setResourceType(resourceType);
ResourceIndexedSearchParams resourceIndexedSearchParams = new ResourceIndexedSearchParams(); ResourceIndexedSearchParams resourceIndexedSearchParams = new ResourceIndexedSearchParams();
mySearchParamExtractorService.extractFromResource(theRequest, resourceIndexedSearchParams, entity, theResource, theResource.getMeta().getLastUpdated(), false); mySearchParamExtractorService.extractFromResource(null, theRequest, resourceIndexedSearchParams, entity, theResource, theResource.getMeta().getLastUpdated(), false);
return resourceIndexedSearchParams; return resourceIndexedSearchParams;
} }
} }

View File

@ -24,7 +24,6 @@ import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
public class SearchParamMatcher { public class SearchParamMatcher {
@Autowired @Autowired
@ -33,9 +32,7 @@ public class SearchParamMatcher {
private InMemoryResourceMatcher myInMemoryResourceMatcher; private InMemoryResourceMatcher myInMemoryResourceMatcher;
public InMemoryMatchResult match(String theCriteria, IBaseResource theResource, RequestDetails theRequest) { public InMemoryMatchResult match(String theCriteria, IBaseResource theResource, RequestDetails theRequest) {
ResourceIndexedSearchParams resourceIndexedSearchParams = myIndexedSearchParamExtractor.extractIndexedSearchParams(theResource, theRequest); ResourceIndexedSearchParams resourceIndexedSearchParams = myIndexedSearchParamExtractor.extractIndexedSearchParams(theResource, theRequest);
return myInMemoryResourceMatcher.match(theCriteria, theResource, resourceIndexedSearchParams); return myInMemoryResourceMatcher.match(theCriteria, theResource, resourceIndexedSearchParams);
} }
} }