Work on multitenancy
This commit is contained in:
parent
e0fcbe1df2
commit
219332e9e3
|
@ -143,3 +143,4 @@ ca.uhn.fhir.jpa.dao.partition.RequestPartitionHelperService.blacklistedResourceT
|
|||
ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderReference.invalidTargetTypeForChain=Resource type "{0}" is not a valid target type for reference search parameter: {1}
|
||||
ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderReference.invalidResourceType=Invalid/unsupported resource type: "{0}"
|
||||
|
||||
ca.uhn.fhir.jpa.dao.index.IdHelperService.nonUniqueForcedId=Non-unique ID specified, can not process request
|
||||
|
|
|
@ -14,5 +14,8 @@ None of the limitations listed here are considered permanent. Over time the HAPI
|
|||
* ValueSet
|
||||
* CodeSystem
|
||||
* ConceptMap
|
||||
|
||||
* **Search Parameters are not partitioned**: There is only one set of SearchParameter resources for the entire system, and any search parameters will apply to resources in all partitions. All SearchParameter resources must be stored in the default partition.
|
||||
|
||||
* **Bulk Operations are not partition aware**: Bulk export operations will export data across all partitions.
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import ca.uhn.fhir.jpa.bulk.BulkDataExportProvider;
|
|||
import ca.uhn.fhir.jpa.bulk.BulkDataExportSvcImpl;
|
||||
import ca.uhn.fhir.jpa.bulk.IBulkDataExportSvc;
|
||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.partition.RequestPartitionHelperService;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.graphql.JpaStorageServices;
|
||||
import ca.uhn.fhir.jpa.interceptor.JpaConsentContextServices;
|
||||
|
@ -33,8 +34,6 @@ import ca.uhn.fhir.jpa.search.cache.ISearchResultCacheSvc;
|
|||
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
||||
import ca.uhn.fhir.jpa.search.reindex.ResourceReindexingSvcImpl;
|
||||
import ca.uhn.fhir.jpa.searchparam.config.SearchParamConfig;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.interceptor.consent.IConsentContextServices;
|
||||
import org.hibernate.jpa.HibernatePersistenceProvider;
|
||||
|
@ -229,8 +228,7 @@ public abstract class BaseConfig {
|
|||
@Bean
|
||||
@Lazy
|
||||
public TerminologyUploaderProvider terminologyUploaderProvider() {
|
||||
TerminologyUploaderProvider retVal = new TerminologyUploaderProvider();
|
||||
return retVal;
|
||||
return new TerminologyUploaderProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -30,6 +30,9 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
|||
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
|
||||
import ca.uhn.fhir.jpa.api.model.DeleteConflictList;
|
||||
import ca.uhn.fhir.jpa.api.model.DeleteMethodOutcome;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
|
||||
import ca.uhn.fhir.jpa.dao.partition.RequestPartitionHelperService;
|
||||
import ca.uhn.fhir.jpa.delete.DeleteConflictService;
|
||||
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseHasResource;
|
||||
|
@ -46,8 +49,6 @@ import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
|||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
|
||||
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
|
||||
import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils;
|
||||
|
@ -1006,7 +1007,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
// Verify that the resource is for the correct partition
|
||||
if (partitionId != null) {
|
||||
if (entity.getPartitionId() != null) {
|
||||
if (partitionId.getPartitionId() == null) {
|
||||
if (entity.getPartitionId() != null) {
|
||||
ourLog.debug("Performing a read for PartitionId={} but entity has partition: {}", partitionId, entity.getPartitionId());
|
||||
entity = null;
|
||||
}
|
||||
} else if (entity.getPartitionId() != null) {
|
||||
if (!entity.getPartitionId().getPartitionId().equals(partitionId.getPartitionId())) {
|
||||
ourLog.debug("Performing a read for PartitionId={} but entity has partition: {}", partitionId, entity.getPartitionId());
|
||||
entity = null;
|
||||
|
|
|
@ -39,6 +39,9 @@ public interface IForcedIdDao extends JpaRepository<ForcedId, Long> {
|
|||
@Query("SELECT f.myResourcePid FROM ForcedId f WHERE myResourceType = :resource_type AND myForcedId = :forced_id")
|
||||
Optional<Long> findByTypeAndForcedId(@Param("resource_type") String theResourceType, @Param("forced_id") String theForcedId);
|
||||
|
||||
@Query("SELECT f.myResourcePid FROM ForcedId f WHERE myPartitionId.myPartitionId IS NULL AND myResourceType = :resource_type AND myForcedId = :forced_id")
|
||||
Optional<Long> findByPartitionIdNullAndTypeAndForcedId(@Param("resource_type") String theResourceType, @Param("forced_id") String theForcedId);
|
||||
|
||||
@Query("SELECT f.myResourcePid FROM ForcedId f WHERE myPartitionId.myPartitionId = :partition_id AND myResourceType = :resource_type AND myForcedId = :forced_id")
|
||||
Optional<Long> findByPartitionIdAndTypeAndForcedId(@Param("partition_id") Integer thePartitionId, @Param("resource_type") String theResourceType, @Param("forced_id") String theForcedId);
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
|
|||
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
|
@ -49,6 +50,7 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
@ -96,6 +98,8 @@ public class IdHelperService {
|
|||
private DaoConfig myDaoConfig;
|
||||
@Autowired
|
||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
@Autowired
|
||||
private FhirContext myFhirCtx;
|
||||
|
||||
private Cache<String, Long> myPersistentIdCache;
|
||||
private Cache<String, IResourceLookup> myResourceLookupCache;
|
||||
|
@ -141,7 +145,7 @@ public class IdHelperService {
|
|||
if (myDaoConfig.isDeleteEnabled()) {
|
||||
retVal = resolveResourceIdentity(thePartitionId, theResourceType, theId);
|
||||
} else {
|
||||
String key = thePartitionId + "/" + theResourceType + "/" + theId;
|
||||
String key = thePartitionId.getPartitionIdStringOrNullString() + "/" + theResourceType + "/" + theId;
|
||||
retVal = myPersistentIdCache.get(key, t -> resolveResourceIdentity(thePartitionId, theResourceType, theId));
|
||||
}
|
||||
|
||||
|
@ -254,9 +258,23 @@ public class IdHelperService {
|
|||
private Long resolveResourceIdentity(@Nullable PartitionId thePartitionId, @Nonnull String theResourceType, @Nonnull String theId) {
|
||||
Optional<Long> pid;
|
||||
if (thePartitionId != null) {
|
||||
pid = myForcedIdDao.findByPartitionIdAndTypeAndForcedId(thePartitionId.getPartitionId(), theResourceType, theId);
|
||||
if (thePartitionId.getPartitionId() == null) {
|
||||
pid = myForcedIdDao.findByPartitionIdNullAndTypeAndForcedId(theResourceType, theId);
|
||||
} else {
|
||||
pid = myForcedIdDao.findByPartitionIdAndTypeAndForcedId(thePartitionId.getPartitionId(), theResourceType, theId);
|
||||
}
|
||||
} else {
|
||||
pid = myForcedIdDao.findByTypeAndForcedId(theResourceType, theId);
|
||||
try {
|
||||
pid = myForcedIdDao.findByTypeAndForcedId(theResourceType, theId);
|
||||
} catch (IncorrectResultSizeDataAccessException e) {
|
||||
/*
|
||||
* This means that:
|
||||
* 1. There are two resources with the exact same resource type and forced id
|
||||
* 2. The unique constraint on this column-pair has been dropped
|
||||
*/
|
||||
String msg = myFhirCtx.getLocalizer().getMessage(IdHelperService.class, "nonUniqueForcedId");
|
||||
throw new PreconditionFailedException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (pid.isPresent() == false) {
|
||||
|
|
|
@ -4,7 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionId;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
|
|
@ -130,7 +130,11 @@ abstract class BasePredicateBuilder {
|
|||
|
||||
void addPredicateParamMissingForNonReference(String theResourceName, String theParamName, boolean theMissing, Join<ResourceTable, ? extends BaseResourceIndexedSearchParam> theJoin, PartitionId thePartitionId) {
|
||||
if (thePartitionId != null) {
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myPartitionIdValue"), thePartitionId.getPartitionId()));
|
||||
if (thePartitionId.getPartitionId() != null) {
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myPartitionIdValue"), thePartitionId.getPartitionId()));
|
||||
} else {
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.isNull(theJoin.get("myPartitionIdValue")));
|
||||
}
|
||||
}
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myResourceType"), theResourceName));
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myParamName"), theParamName));
|
||||
|
@ -209,7 +213,12 @@ abstract class BasePredicateBuilder {
|
|||
void addPartitionIdPredicate(PartitionId thePartitionId, Join<ResourceTable, ? extends BasePartitionable> theJoin, List<Predicate> theCodePredicates) {
|
||||
if (thePartitionId != null) {
|
||||
Integer partitionId = thePartitionId.getPartitionId();
|
||||
Predicate partitionPredicate = myCriteriaBuilder.equal(theJoin.get("myPartitionIdValue").as(Integer.class), partitionId);
|
||||
Predicate partitionPredicate;
|
||||
if (partitionId != null) {
|
||||
partitionPredicate = myCriteriaBuilder.equal(theJoin.get("myPartitionIdValue").as(Integer.class), partitionId);
|
||||
} else {
|
||||
partitionPredicate = myCriteriaBuilder.isNull(theJoin.get("myPartitionIdValue").as(Integer.class));
|
||||
}
|
||||
myQueryRoot.addPredicate(partitionPredicate);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@ package ca.uhn.fhir.jpa.dao.r4;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoSearchParameter;
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
|
|
@ -32,6 +32,7 @@ import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
|
|||
import ca.uhn.fhir.jpa.dao.IResultIterator;
|
||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
|
||||
import ca.uhn.fhir.jpa.dao.partition.RequestPartitionHelperService;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.entity.SearchInclude;
|
||||
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
|
||||
|
@ -154,6 +155,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
private boolean myCustomIsolationSupported;
|
||||
@Autowired
|
||||
private PersistedJpaBundleProviderFactory myPersistedJpaBundleProviderFactory;
|
||||
@Autowired
|
||||
private RequestPartitionHelperService myRequestPartitionHelperService;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -297,9 +300,6 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
return new ResourceGoneException(msg);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private RequestPartitionHelperService myRequestPartitionHelperService;
|
||||
|
||||
@Override
|
||||
public IBundleProvider registerSearch(final IFhirResourceDao<?> theCallingDao, final SearchParameterMap theParams, String theResourceType, CacheControlDirective theCacheControlDirective, RequestDetails theRequestDetails) {
|
||||
final String searchUuid = UUID.randomUUID().toString();
|
||||
|
|
|
@ -3,20 +3,23 @@ package ca.uhn.fhir.jpa.dao.r4;
|
|||
import ca.uhn.fhir.interceptor.api.Hook;
|
||||
import ca.uhn.fhir.interceptor.api.Interceptor;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR4;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -30,6 +33,7 @@ import org.junit.After;
|
|||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import java.time.LocalDate;
|
||||
|
@ -40,6 +44,7 @@ import java.util.List;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.hamcrest.Matchers.matchesPattern;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
@ -48,13 +53,19 @@ import static org.junit.Assert.assertThat;
|
|||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* This should be marked as DIRTIES_CONTEXT because it drops an index
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||
public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PartitioningR4Test.class);
|
||||
|
||||
private MyInterceptor myPartitionInterceptor;
|
||||
private LocalDate myPartitionDate;
|
||||
private int myPartitionId;
|
||||
private static boolean ourHaveDroppedForcedIdUniqueConstraint;
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
|
@ -80,12 +91,79 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
myPartitionInterceptor = new MyInterceptor();
|
||||
myInterceptorRegistry.registerInterceptor(myPartitionInterceptor);
|
||||
|
||||
if (!ourHaveDroppedForcedIdUniqueConstraint) {
|
||||
runInTransaction(() -> {
|
||||
myEntityManager.createNativeQuery("alter table " + ForcedId.HFJ_FORCED_ID + " drop constraint " + ForcedId.IDX_FORCEDID_TYPE_FID).executeUpdate();
|
||||
});
|
||||
ourHaveDroppedForcedIdUniqueConstraint = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSearchParameter_DefaultPartition() {
|
||||
addCreateNoPartition();
|
||||
|
||||
SearchParameter sp = new SearchParameter();
|
||||
sp.addBase("Patient");
|
||||
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
sp.setType(Enumerations.SearchParamType.REFERENCE);
|
||||
sp.setCode("extpatorg");
|
||||
sp.setName("extpatorg");
|
||||
sp.setExpression("Patient.extension('http://patext').value.as(Reference)");
|
||||
Long id = mySearchParameterDao.create(sp).getId().getIdPartAsLong();
|
||||
|
||||
runInTransaction(() -> {
|
||||
ResourceTable resourceTable = myResourceTableDao.findById(id).orElseThrow(IllegalArgumentException::new);
|
||||
assertNull(resourceTable.getPartitionId());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSearchParameter_DefaultPartitionWithDate() {
|
||||
addCreateNoPartitionId(myPartitionDate);
|
||||
|
||||
SearchParameter sp = new SearchParameter();
|
||||
sp.addBase("Patient");
|
||||
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
sp.setType(Enumerations.SearchParamType.REFERENCE);
|
||||
sp.setCode("extpatorg");
|
||||
sp.setName("extpatorg");
|
||||
sp.setExpression("Patient.extension('http://patext').value.as(Reference)");
|
||||
Long id = mySearchParameterDao.create(sp).getId().getIdPartAsLong();
|
||||
|
||||
runInTransaction(() -> {
|
||||
// HFJ_RESOURCE
|
||||
ResourceTable resourceTable = myResourceTableDao.findById(id).orElseThrow(IllegalArgumentException::new);
|
||||
assertEquals(myPartitionId, resourceTable.getPartitionId().getPartitionId().intValue());
|
||||
assertEquals(myPartitionDate, resourceTable.getPartitionId().getPartitionDate());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateResourceNoPartition() {
|
||||
addCreatePartition(null, null);
|
||||
public void testCreateSearchParameter_NonDefaultPartition() {
|
||||
addCreatePartition(myPartitionId, myPartitionDate);
|
||||
|
||||
SearchParameter sp = new SearchParameter();
|
||||
sp.addBase("Patient");
|
||||
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
sp.setType(Enumerations.SearchParamType.REFERENCE);
|
||||
sp.setCode("extpatorg");
|
||||
sp.setName("extpatorg");
|
||||
sp.setExpression("Patient.extension('http://patext').value.as(Reference)");
|
||||
try {
|
||||
mySearchParameterDao.create(sp);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
assertEquals("", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_ServerId_NoPartition() {
|
||||
addCreateNoPartition();
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("system").setValue("value");
|
||||
|
@ -100,7 +178,7 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
|
||||
@Test
|
||||
public void testCreateResourceWithPartition() {
|
||||
public void testCreate_ServerId_WithPartition() {
|
||||
createUniqueCompositeSp();
|
||||
createRequestId();
|
||||
|
||||
|
@ -185,7 +263,93 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithForcedId() {
|
||||
public void testCreate_ServerId_DefaultPartition() {
|
||||
createUniqueCompositeSp();
|
||||
createRequestId();
|
||||
|
||||
addCreateNoPartitionId(myPartitionDate);
|
||||
addCreateNoPartitionId(myPartitionDate);
|
||||
|
||||
Organization org = new Organization();
|
||||
org.setName("org");
|
||||
IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient p = new Patient();
|
||||
p.getMeta().addTag("http://system", "code", "diisplay");
|
||||
p.addName().setFamily("FAM");
|
||||
p.addIdentifier().setSystem("system").setValue("value");
|
||||
p.setBirthDate(new Date());
|
||||
p.getManagingOrganization().setReferenceElement(orgId);
|
||||
Long patientId = myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
|
||||
|
||||
runInTransaction(() -> {
|
||||
// HFJ_RESOURCE
|
||||
ResourceTable resourceTable = myResourceTableDao.findById(patientId).orElseThrow(IllegalArgumentException::new);
|
||||
assertEquals(null, resourceTable.getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, resourceTable.getPartitionId().getPartitionDate());
|
||||
|
||||
// HFJ_RES_TAG
|
||||
List<ResourceTag> tags = myResourceTagDao.findAll();
|
||||
assertEquals(1, tags.size());
|
||||
assertEquals(null, tags.get(0).getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, tags.get(0).getPartitionId().getPartitionDate());
|
||||
|
||||
// HFJ_RES_VER
|
||||
ResourceHistoryTable version = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(patientId, 1L);
|
||||
assertEquals(null, version.getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, version.getPartitionId().getPartitionDate());
|
||||
|
||||
// HFJ_HISTORY_TAG
|
||||
List<ResourceHistoryTag> historyTags = myResourceHistoryTagDao.findAll();
|
||||
assertEquals(1, historyTags.size());
|
||||
assertEquals(null, historyTags.get(0).getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, historyTags.get(0).getPartitionId().getPartitionDate());
|
||||
|
||||
// HFJ_RES_VER_PROV
|
||||
assertNotNull(version.getProvenance());
|
||||
assertEquals(null, version.getProvenance().getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, version.getProvenance().getPartitionId().getPartitionDate());
|
||||
|
||||
// HFJ_SPIDX_STRING
|
||||
List<ResourceIndexedSearchParamString> strings = myResourceIndexedSearchParamStringDao.findAllForResourceId(patientId);
|
||||
ourLog.info("\n * {}", strings.stream().map(ResourceIndexedSearchParamString::toString).collect(Collectors.joining("\n * ")));
|
||||
assertEquals(10, strings.size());
|
||||
assertEquals(null, strings.get(0).getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, strings.get(0).getPartitionId().getPartitionDate());
|
||||
|
||||
// HFJ_SPIDX_DATE
|
||||
List<ResourceIndexedSearchParamDate> dates = myResourceIndexedSearchParamDateDao.findAllForResourceId(patientId);
|
||||
ourLog.info("\n * {}", dates.stream().map(ResourceIndexedSearchParamDate::toString).collect(Collectors.joining("\n * ")));
|
||||
assertEquals(2, dates.size());
|
||||
assertEquals(null, dates.get(0).getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, dates.get(0).getPartitionId().getPartitionDate());
|
||||
assertEquals(null, dates.get(1).getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, dates.get(1).getPartitionId().getPartitionDate());
|
||||
|
||||
// HFJ_RES_LINK
|
||||
List<ResourceLink> resourceLinks = myResourceLinkDao.findAllForResourceId(patientId);
|
||||
assertEquals(1, resourceLinks.size());
|
||||
assertEquals(null, resourceLinks.get(0).getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, resourceLinks.get(0).getPartitionId().getPartitionDate());
|
||||
|
||||
// HFJ_RES_PARAM_PRESENT
|
||||
List<SearchParamPresent> presents = mySearchParamPresentDao.findAllForResource(resourceTable);
|
||||
assertEquals(3, presents.size());
|
||||
assertEquals(null, presents.get(0).getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, presents.get(0).getPartitionId().getPartitionDate());
|
||||
|
||||
// HFJ_IDX_CMP_STRING_UNIQ
|
||||
List<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAllForResourceId(patientId);
|
||||
assertEquals(1, uniques.size());
|
||||
assertEquals(null, uniques.get(0).getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, uniques.get(0).getPartitionId().getPartitionDate());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreate_ForcedId_WithPartition() {
|
||||
addCreatePartition(myPartitionId, myPartitionDate);
|
||||
addCreatePartition(myPartitionId, myPartitionDate);
|
||||
|
||||
|
@ -211,6 +375,59 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_ForcedId_NoPartition() {
|
||||
addCreateNoPartition();
|
||||
addCreateNoPartition();
|
||||
|
||||
Organization org = new Organization();
|
||||
org.setId("org");
|
||||
org.setName("org");
|
||||
IIdType orgId = myOrganizationDao.update(org).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setId("pat");
|
||||
p.getManagingOrganization().setReferenceElement(orgId);
|
||||
myPatientDao.update(p, mySrd);
|
||||
|
||||
runInTransaction(() -> {
|
||||
// HFJ_FORCED_ID
|
||||
List<ForcedId> forcedIds = myForcedIdDao.findAll();
|
||||
assertEquals(2, forcedIds.size());
|
||||
assertEquals(null, forcedIds.get(0).getPartitionId());
|
||||
assertEquals(null, forcedIds.get(1).getPartitionId());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_ForcedId_DefaultPartition() {
|
||||
addCreateNoPartitionId(myPartitionDate);
|
||||
addCreateNoPartitionId(myPartitionDate);
|
||||
|
||||
Organization org = new Organization();
|
||||
org.setId("org");
|
||||
org.setName("org");
|
||||
IIdType orgId = myOrganizationDao.update(org).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setId("pat");
|
||||
p.getManagingOrganization().setReferenceElement(orgId);
|
||||
myPatientDao.update(p, mySrd);
|
||||
|
||||
runInTransaction(() -> {
|
||||
// HFJ_FORCED_ID
|
||||
List<ForcedId> forcedIds = myForcedIdDao.findAll();
|
||||
assertEquals(2, forcedIds.size());
|
||||
assertEquals(null, forcedIds.get(0).getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, forcedIds.get(0).getPartitionId().getPartitionDate());
|
||||
assertEquals(null, forcedIds.get(1).getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, forcedIds.get(1).getPartitionId().getPartitionDate());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testUpdateResourceWithPartition() {
|
||||
createRequestId();
|
||||
|
@ -273,51 +490,116 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testReadAcrossPartitions() {
|
||||
public void testRead_PidId_AllPartitions() {
|
||||
IIdType patientId1 = createPatient(1, withActiveTrue());
|
||||
IIdType patientId2 = createPatient(2, withActiveTrue());
|
||||
|
||||
addReadPartition(null);
|
||||
IdType gotId1 = myPatientDao.read(patientId1, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientId1, gotId1);
|
||||
{
|
||||
addReadPartition(null);
|
||||
myCaptureQueriesListener.clear();
|
||||
IdType gotId1 = myPatientDao.read(patientId1, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientId1, gotId1);
|
||||
|
||||
addReadPartition(null);
|
||||
IdType gotId2 = myPatientDao.read(patientId2, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientId2, gotId2);
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
||||
// Only the read columns should be used, no criteria use partition
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as "));
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
}
|
||||
{
|
||||
addReadPartition(null);
|
||||
IdType gotId2 = myPatientDao.read(patientId2, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientId2, gotId2);
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
||||
// Only the read columns should be used, no criteria use partition
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as "));
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSpecificPartition_PidId() {
|
||||
public void testRead_PidId_SpecificPartition() {
|
||||
IIdType patientIdNull = createPatient(null, withActiveTrue());
|
||||
IIdType patientId1 = createPatient(1, withActiveTrue());
|
||||
IIdType patientId2 = createPatient(2, withActiveTrue());
|
||||
|
||||
// Read in correct Partition
|
||||
addReadPartition(1);
|
||||
IdType gotId1 = myPatientDao.read(patientId1, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientId1, gotId1);
|
||||
{
|
||||
myCaptureQueriesListener.clear();
|
||||
addReadPartition(1);
|
||||
IdType gotId1 = myPatientDao.read(patientId1, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientId1, gotId1);
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
||||
// Only the read columns should be used, no criteria use partition
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as "));
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
}
|
||||
|
||||
// Read in null Partition
|
||||
addReadPartition(1);
|
||||
try {
|
||||
myPatientDao.read(patientIdNull, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertThat(e.getMessage(), matchesPattern("Resource Patient/[0-9]+ is not known"));
|
||||
{
|
||||
addReadPartition(1);
|
||||
try {
|
||||
myPatientDao.read(patientIdNull, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertThat(e.getMessage(), matchesPattern("Resource Patient/[0-9]+ is not known"));
|
||||
}
|
||||
}
|
||||
|
||||
// Read in wrong Partition
|
||||
addReadPartition(1);
|
||||
try {
|
||||
myPatientDao.read(patientId2, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertThat(e.getMessage(), matchesPattern("Resource Patient/[0-9]+ is not known"));
|
||||
{
|
||||
addReadPartition(1);
|
||||
try {
|
||||
myPatientDao.read(patientId2, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertThat(e.getMessage(), matchesPattern("Resource Patient/[0-9]+ is not known"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadSpecificPartition_ForcedId() {
|
||||
public void testRead_PidId_DefaultPartition() {
|
||||
IIdType patientIdNull = createPatient(null, withActiveTrue());
|
||||
IIdType patientId1 = createPatient(1, withActiveTrue());
|
||||
createPatient(2, withActiveTrue());
|
||||
|
||||
// Read in correct Partition
|
||||
{
|
||||
myCaptureQueriesListener.clear();
|
||||
addDefaultReadPartition();
|
||||
IdType gotId1 = myPatientDao.read(patientIdNull, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientIdNull, gotId1);
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
||||
// Only the read columns should be used, no criteria use partition
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as "));
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
}
|
||||
|
||||
// Read in wrong Partition
|
||||
{
|
||||
addDefaultReadPartition();
|
||||
try {
|
||||
myPatientDao.read(patientId1, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertThat(e.getMessage(), matchesPattern("Resource Patient/[0-9]+ is not known"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead_ForcedId_SpecificPartition() {
|
||||
IIdType patientIdNull = createPatient(null, withActiveTrue(), withId("NULL"));
|
||||
IIdType patientId1 = createPatient(1, withActiveTrue(), withId("ONE"));
|
||||
IIdType patientId2 = createPatient(2, withActiveTrue(), withId("TWO"));
|
||||
|
@ -333,7 +615,7 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
myPatientDao.read(patientIdNull, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertThat(e.getMessage(), matchesPattern("Resource Patient/[0-9]+ is not known"));
|
||||
assertThat(e.getMessage(), matchesPattern("Resource Patient/NULL is not known"));
|
||||
}
|
||||
|
||||
// Read in wrong Partition
|
||||
|
@ -342,7 +624,79 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
myPatientDao.read(patientId2, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertThat(e.getMessage(), matchesPattern("Resource Patient/[0-9]+ is not known"));
|
||||
assertThat(e.getMessage(), matchesPattern("Resource Patient/TWO is not known"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead_ForcedId_DefaultPartition() {
|
||||
IIdType patientIdNull = createPatient(null, withActiveTrue(), withId("NULL"));
|
||||
IIdType patientId1 = createPatient(1, withActiveTrue(), withId("ONE"));
|
||||
IIdType patientId2 = createPatient(2, withActiveTrue(), withId("TWO"));
|
||||
|
||||
// Read in correct Partition
|
||||
addDefaultReadPartition();
|
||||
IdType gotId1 = myPatientDao.read(patientIdNull, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientIdNull, gotId1);
|
||||
|
||||
// Read in null Partition
|
||||
addDefaultReadPartition();
|
||||
try {
|
||||
myPatientDao.read(patientId1, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertThat(e.getMessage(), matchesPattern("Resource Patient/ONE is not known"));
|
||||
}
|
||||
|
||||
// Read in wrong Partition
|
||||
addDefaultReadPartition();
|
||||
try {
|
||||
myPatientDao.read(patientId2, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
assertThat(e.getMessage(), matchesPattern("Resource Patient/TWO is not known"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead_ForcedId_AllPartition() {
|
||||
IIdType patientIdNull = createPatient(null, withActiveTrue(), withId("NULL"));
|
||||
IIdType patientId1 = createPatient(1, withActiveTrue(), withId("ONE"));
|
||||
IIdType patientId2 = createPatient(2, withActiveTrue(), withId("TWO"));
|
||||
{
|
||||
addReadPartition(null);
|
||||
IdType gotId1 = myPatientDao.read(patientIdNull, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientIdNull, gotId1);
|
||||
}
|
||||
{
|
||||
addReadPartition(null);
|
||||
IdType gotId1 = myPatientDao.read(patientId1, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientId1, gotId1);
|
||||
}
|
||||
{
|
||||
// Read in wrong Partition
|
||||
addReadPartition(null);
|
||||
IdType gotId1 = myPatientDao.read(patientId2, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientId2, gotId1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead_ForcedId_AllPartition_WithDuplicate() {
|
||||
IIdType patientIdNull = createPatient(null, withActiveTrue(), withId("FOO"));
|
||||
IIdType patientId1 = createPatient(1, withActiveTrue(), withId("FOO"));
|
||||
IIdType patientId2 = createPatient(2, withActiveTrue(), withId("FOO"));
|
||||
assertEquals(patientIdNull, patientId1);
|
||||
assertEquals(patientIdNull, patientId2);
|
||||
|
||||
{
|
||||
addReadPartition(null);
|
||||
try {
|
||||
myPatientDao.read(patientIdNull, mySrd);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
assertEquals("Non-unique ID specified, can not process request", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -387,31 +741,6 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_MissingParamReference_SearchAllPartitions() {
|
||||
IIdType patientIdNull = createPatient(null, withFamily("FAMILY"));
|
||||
IIdType patientId1 = createPatient(1, withFamily("FAMILY"));
|
||||
IIdType patientId2 = createPatient(2, withFamily("FAMILY"));
|
||||
|
||||
// :missing=true
|
||||
{
|
||||
addReadPartition(null);
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Patient.SP_GENERAL_PRACTITIONER, new StringParam().setMissing(true));
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HFJ_RES_PARAM_PRESENT"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE='1919227773735728687'"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearch_MissingParamString_SearchOnePartition() {
|
||||
|
@ -454,6 +783,72 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_MissingParamString_SearchDefaultPartition() {
|
||||
IIdType patientIdNull = createPatient(null, withFamily("FAMILY"));
|
||||
createPatient(1, withFamily("FAMILY"));
|
||||
createPatient(2, withFamily("FAMILY"));
|
||||
|
||||
// :missing=true
|
||||
{
|
||||
addDefaultReadPartition();
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Patient.SP_ACTIVE, new StringParam().setMissing(true));
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING='true'"));
|
||||
}
|
||||
|
||||
// :missing=false
|
||||
{
|
||||
addDefaultReadPartition();
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Patient.SP_FAMILY, new StringParam().setMissing(false));
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING='false'"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_MissingParamReference_SearchAllPartitions() {
|
||||
IIdType patientIdNull = createPatient(null, withFamily("FAMILY"));
|
||||
IIdType patientId1 = createPatient(1, withFamily("FAMILY"));
|
||||
IIdType patientId2 = createPatient(2, withFamily("FAMILY"));
|
||||
|
||||
// :missing=true
|
||||
{
|
||||
addReadPartition(null);
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Patient.SP_GENERAL_PRACTITIONER, new StringParam().setMissing(true));
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HFJ_RES_PARAM_PRESENT"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE='1919227773735728687'"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_MissingParamReference_SearchOnePartition() {
|
||||
createPatient(null, withFamily("FAMILY"));
|
||||
|
@ -481,6 +876,32 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearch_MissingParamReference_SearchDefaultPartition() {
|
||||
IIdType patientIdDefault = createPatient(null, withFamily("FAMILY"));
|
||||
createPatient(1, withFamily("FAMILY"));
|
||||
createPatient(2, withFamily("FAMILY"));
|
||||
|
||||
// :missing=true
|
||||
{
|
||||
addDefaultReadPartition();
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Patient.SP_GENERAL_PRACTITIONER, new StringParam().setMissing(true));
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdDefault));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "mysearchpa1_.PARTITION_ID is null"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HFJ_RES_PARAM_PRESENT"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE='1919227773735728687'"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearch_NoParams_SearchAllPartitions() {
|
||||
|
@ -544,6 +965,30 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_VALUE_NORMALIZED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_StringParam_SearchDefaultPartition() {
|
||||
IIdType patientIdNull = createPatient(null, withFamily("FAMILY"));
|
||||
createPatient(1, withFamily("FAMILY"));
|
||||
createPatient(2, withFamily("FAMILY"));
|
||||
|
||||
addDefaultReadPartition();
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Patient.SP_FAMILY, new StringParam("FAMILY"));
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
searchSql = searchSql.toUpperCase();
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID IS NULL"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_VALUE_NORMALIZED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_StringParam_SearchOnePartition() {
|
||||
createPatient(null, withFamily("FAMILY"));
|
||||
|
@ -747,10 +1192,17 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
|
||||
private void addCreatePartition(Integer thePartitionId, LocalDate thePartitionDate) {
|
||||
PartitionId partitionId = null;
|
||||
if (thePartitionId != null) {
|
||||
partitionId = new PartitionId(thePartitionId, thePartitionDate);
|
||||
}
|
||||
Validate.notNull(thePartitionId);
|
||||
PartitionId partitionId = new PartitionId(thePartitionId, thePartitionDate);
|
||||
myPartitionInterceptor.addCreatePartition(partitionId);
|
||||
}
|
||||
|
||||
private void addCreateNoPartition() {
|
||||
myPartitionInterceptor.addCreatePartition(null);
|
||||
}
|
||||
|
||||
private void addCreateNoPartitionId(LocalDate thePartitionDate) {
|
||||
PartitionId partitionId = new PartitionId(null, thePartitionDate);
|
||||
myPartitionInterceptor.addCreatePartition(partitionId);
|
||||
}
|
||||
|
||||
|
@ -762,15 +1214,29 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest {
|
|||
myPartitionInterceptor.addReadPartition(partitionId);
|
||||
}
|
||||
|
||||
private void addDefaultReadPartition() {
|
||||
PartitionId partitionId = new PartitionId(null, null);
|
||||
myPartitionInterceptor.addReadPartition(partitionId);
|
||||
}
|
||||
|
||||
public IIdType createPatient(Integer thePartitionId, Consumer<Patient>... theModifiers) {
|
||||
addCreatePartition(thePartitionId, null);
|
||||
if (thePartitionId != null) {
|
||||
addCreatePartition(thePartitionId, null);
|
||||
} else {
|
||||
addCreateNoPartition();
|
||||
}
|
||||
|
||||
|
||||
Patient p = new Patient();
|
||||
for (Consumer<Patient> next : theModifiers) {
|
||||
next.accept(p);
|
||||
}
|
||||
|
||||
return myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
|
||||
if (isNotBlank(p.getId())) {
|
||||
return myPatientDao.update(p, mySrd).getId().toUnqualifiedVersionless();
|
||||
} else {
|
||||
return myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
}
|
||||
|
||||
public void createRequestId() {
|
||||
|
|
|
@ -25,7 +25,7 @@ import org.hibernate.annotations.ColumnDefault;
|
|||
import javax.persistence.*;
|
||||
|
||||
@Entity()
|
||||
@Table(name = "HFJ_FORCED_ID", uniqueConstraints = {
|
||||
@Table(name = ForcedId.HFJ_FORCED_ID, uniqueConstraints = {
|
||||
@UniqueConstraint(name = "IDX_FORCEDID_RESID", columnNames = {"RESOURCE_PID"}),
|
||||
@UniqueConstraint(name = ForcedId.IDX_FORCEDID_TYPE_FID, columnNames = {"RESOURCE_TYPE", "FORCED_ID"})
|
||||
}, indexes = {
|
||||
|
@ -40,6 +40,7 @@ public class ForcedId {
|
|||
|
||||
public static final int MAX_FORCED_ID_LENGTH = 100;
|
||||
public static final String IDX_FORCEDID_TYPE_FID = "IDX_FORCEDID_TYPE_FID";
|
||||
public static final String HFJ_FORCED_ID = "HFJ_FORCED_ID";
|
||||
|
||||
@Column(name = "FORCED_ID", nullable = false, length = MAX_FORCED_ID_LENGTH, updatable = false)
|
||||
private String myForcedId;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.model.entity;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import java.time.LocalDate;
|
||||
|
@ -27,26 +27,27 @@ public class PartitionId implements Cloneable {
|
|||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public PartitionId(int thePartitionId, LocalDate thePartitionDate) {
|
||||
public PartitionId(@Nullable Integer thePartitionId, @Nullable LocalDate thePartitionDate) {
|
||||
setPartitionId(thePartitionId);
|
||||
setPartitionDate(thePartitionDate);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Nullable
|
||||
public Integer getPartitionId() {
|
||||
return myPartitionId;
|
||||
}
|
||||
|
||||
public PartitionId setPartitionId(@Nonnull Integer thePartitionId) {
|
||||
public PartitionId setPartitionId(@Nullable Integer thePartitionId) {
|
||||
myPartitionId = thePartitionId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LocalDate getPartitionDate() {
|
||||
return myPartitionDate;
|
||||
}
|
||||
|
||||
public PartitionId setPartitionDate(LocalDate thePartitionDate) {
|
||||
public PartitionId setPartitionDate(@Nullable LocalDate thePartitionDate) {
|
||||
myPartitionDate = thePartitionDate;
|
||||
return this;
|
||||
}
|
||||
|
@ -61,6 +62,13 @@ public class PartitionId implements Cloneable {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getPartitionIdStringOrNullString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the partition ID (numeric) as a string, or the string "null"
|
||||
*/
|
||||
public String getPartitionIdStringOrNullString() {
|
||||
return defaultIfNull(myPartitionId, "null").toString();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue