Include includes on revincludes (#2902)

* Include includes on revincludes
This commit is contained in:
James Agnew 2021-08-19 12:31:29 -04:00 committed by GitHub
parent 17af21e351
commit 85517c47e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 108 additions and 20 deletions

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 2902
jira: SMILE-3000
backport: cust_fmc_5_3
title: "Fixed a bug wherein includes were not being included on revincludes."

View File

@ -886,7 +886,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
} }
IRestfulServerDefaults server = theRequestDetails.getServer(); IRestfulServerDefaults server = theRequestDetails.getServer();
IPagingProvider pagingProvider = server.getPagingProvider(); IPagingProvider pagingProvider = server.getPagingProvider();
return pagingProvider instanceof DatabaseBackedPagingProvider; return pagingProvider != null;
} }
protected void markResourcesMatchingExpressionAsNeedingReindexing(Boolean theCurrentlyReindexing, String theExpression) { protected void markResourcesMatchingExpressionAsNeedingReindexing(Boolean theCurrentlyReindexing, String theExpression) {

View File

@ -41,6 +41,7 @@ import ca.uhn.fhir.jpa.partition.RequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.search.cache.ISearchCacheSvc; import ca.uhn.fhir.jpa.search.cache.ISearchCacheSvc;
import ca.uhn.fhir.jpa.search.cache.SearchCacheStatusEnum; import ca.uhn.fhir.jpa.search.cache.SearchCacheStatusEnum;
import ca.uhn.fhir.jpa.util.InterceptorUtil; import ca.uhn.fhir.jpa.util.InterceptorUtil;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
import ca.uhn.fhir.jpa.util.MemoryCacheService; import ca.uhn.fhir.jpa.util.MemoryCacheService;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
@ -74,6 +75,7 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
public class PersistedJpaBundleProvider implements IBundleProvider { public class PersistedJpaBundleProvider implements IBundleProvider {
@ -409,19 +411,25 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
// Note: Leave as protected, HSPC depends on this // Note: Leave as protected, HSPC depends on this
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected List<IBaseResource> toResourceList(ISearchBuilder theSearchBuilder, List<ResourcePersistentId> thePids) { protected List<IBaseResource> toResourceList(ISearchBuilder theSearchBuilder, List<ResourcePersistentId> thePids) {
Set<ResourcePersistentId> includedPids = new HashSet<>();
List<ResourcePersistentId> includedPidList = new ArrayList<>();
if (mySearchEntity.getSearchType() == SearchTypeEnum.SEARCH) { if (mySearchEntity.getSearchType() == SearchTypeEnum.SEARCH) {
Integer maxIncludes = myDaoConfig.getMaximumIncludesToLoadPerPage(); Integer maxIncludes = myDaoConfig.getMaximumIncludesToLoadPerPage();
includedPids.addAll(theSearchBuilder.loadIncludes(myContext, myEntityManager, thePids, mySearchEntity.toRevIncludesList(), true, mySearchEntity.getLastUpdated(), myUuid, myRequest, maxIncludes));
// Load _revincludes
Set<ResourcePersistentId> includedPids = theSearchBuilder.loadIncludes(myContext, myEntityManager, thePids, mySearchEntity.toRevIncludesList(), true, mySearchEntity.getLastUpdated(), myUuid, myRequest, maxIncludes);
if (maxIncludes != null) { if (maxIncludes != null) {
maxIncludes -= includedPids.size(); maxIncludes -= includedPids.size();
} }
includedPids.addAll(theSearchBuilder.loadIncludes(myContext, myEntityManager, thePids, mySearchEntity.toIncludesList(), false, mySearchEntity.getLastUpdated(), myUuid, myRequest, maxIncludes)); thePids.addAll(includedPids);
} includedPidList.addAll(includedPids);
List<ResourcePersistentId> includedPidList = new ArrayList<>(includedPids); // Load _includes
thePids.addAll(includedPidList); Set<ResourcePersistentId> revIncludedPids = theSearchBuilder.loadIncludes(myContext, myEntityManager, thePids, mySearchEntity.toIncludesList(), false, mySearchEntity.getLastUpdated(), myUuid, myRequest, maxIncludes);
thePids.addAll(revIncludedPids);
includedPidList.addAll(revIncludedPids);
}
// Execute the query and make sure we return distinct results // Execute the query and make sure we return distinct results
List<IBaseResource> resources = new ArrayList<>(); List<IBaseResource> resources = new ArrayList<>();

View File

@ -536,19 +536,22 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
* individually for pages as we return them to clients * individually for pages as we return them to clients
*/ */
// _includes
Integer maxIncludes = myDaoConfig.getMaximumIncludesToLoadPerPage(); Integer maxIncludes = myDaoConfig.getMaximumIncludesToLoadPerPage();
final Set<ResourcePersistentId> includedPids = theSb.loadIncludes(myContext, myEntityManager, pids, theParams.getRevIncludes(), true, theParams.getLastUpdated(), "(synchronous)", theRequestDetails, maxIncludes); final Set<ResourcePersistentId> includedPids = theSb.loadIncludes(myContext, myEntityManager, pids, theParams.getRevIncludes(), true, theParams.getLastUpdated(), "(synchronous)", theRequestDetails, maxIncludes);
if (maxIncludes != null) { if (maxIncludes != null) {
maxIncludes -= includedPids.size(); maxIncludes -= includedPids.size();
} }
pids.addAll(includedPids);
if (theParams.getEverythingMode() == null && (maxIncludes == null || maxIncludes > 0)) {
includedPids.addAll(theSb.loadIncludes(myContext, myEntityManager, pids, theParams.getIncludes(), false, theParams.getLastUpdated(), "(synchronous)", theRequestDetails, maxIncludes));
}
List<ResourcePersistentId> includedPidsList = new ArrayList<>(includedPids); List<ResourcePersistentId> includedPidsList = new ArrayList<>(includedPids);
pids.addAll(includedPidsList);
// _revincludes
if (theParams.getEverythingMode() == null && (maxIncludes == null || maxIncludes > 0)) {
Set<ResourcePersistentId> revIncludedPids = theSb.loadIncludes(myContext, myEntityManager, pids, theParams.getIncludes(), false, theParams.getLastUpdated(), "(synchronous)", theRequestDetails, maxIncludes);
includedPids.addAll(revIncludedPids);
pids.addAll(revIncludedPids);
includedPidsList.addAll(revIncludedPids);
}
List<IBaseResource> resources = new ArrayList<>(); List<IBaseResource> resources = new ArrayList<>();
theSb.loadResourcesByPid(pids, includedPidsList, resources, false, theRequestDetails); theSb.loadResourcesByPid(pids, includedPidsList, resources, false, theRequestDetails);

View File

@ -103,6 +103,7 @@ import org.hl7.fhir.r4.model.AllergyIntolerance;
import org.hl7.fhir.r4.model.Appointment; import org.hl7.fhir.r4.model.Appointment;
import org.hl7.fhir.r4.model.AuditEvent; import org.hl7.fhir.r4.model.AuditEvent;
import org.hl7.fhir.r4.model.Binary; import org.hl7.fhir.r4.model.Binary;
import org.hl7.fhir.r4.model.BodyStructure;
import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.CapabilityStatement; import org.hl7.fhir.r4.model.CapabilityStatement;
import org.hl7.fhir.r4.model.CarePlan; import org.hl7.fhir.r4.model.CarePlan;
@ -371,6 +372,9 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
@Qualifier("myBinaryDaoR4") @Qualifier("myBinaryDaoR4")
protected IFhirResourceDao<Binary> myBinaryDao; protected IFhirResourceDao<Binary> myBinaryDao;
@Autowired @Autowired
@Qualifier("myBodyStructureDaoR4")
protected IFhirResourceDao<BodyStructure> myBodyStructureDao;
@Autowired
@Qualifier("myDocumentReferenceDaoR4") @Qualifier("myDocumentReferenceDaoR4")
protected IFhirResourceDao<DocumentReference> myDocumentReferenceDao; protected IFhirResourceDao<DocumentReference> myDocumentReferenceDao;
@Autowired @Autowired

View File

@ -929,7 +929,7 @@ public class FhirResourceDaoR4LegacySearchBuilderTest extends BaseJpaR4Test {
List<IIdType> actual = toUnqualifiedVersionlessIds(resp); List<IIdType> actual = toUnqualifiedVersionlessIds(resp);
myCaptureQueriesListener.logSelectQueriesForCurrentThread(); myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(actual, containsInAnyOrder(orgId, medId, patId, moId, patId2)); assertThat(actual, containsInAnyOrder(orgId, medId, patId, moId, patId2));
assertEquals(6, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); assertEquals(1, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
// Specific patient ID with linked stuff // Specific patient ID with linked stuff
request = mock(HttpServletRequest.class); request = mock(HttpServletRequest.class);

View File

@ -1,17 +1,25 @@
package ca.uhn.fhir.jpa.dao.r4; package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.hamcrest.collection.IsIterableContainingInAnyOrder; import org.hamcrest.collection.IsIterableContainingInAnyOrder;
import org.hl7.fhir.r4.model.BodyStructure;
import org.hl7.fhir.r4.model.CarePlan; import org.hl7.fhir.r4.model.CarePlan;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.EpisodeOfCare; import org.hl7.fhir.r4.model.EpisodeOfCare;
import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Procedure;
import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.SearchParameter;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -23,7 +31,10 @@ import java.util.stream.IntStream;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hl7.fhir.r4.model.ResourceType.BodyStructure;
import static org.hl7.fhir.r4.model.ResourceType.Patient; import static org.hl7.fhir.r4.model.ResourceType.Patient;
import static org.hl7.fhir.r4.model.ResourceType.Procedure;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
@SuppressWarnings({"unchecked", "Duplicates"}) @SuppressWarnings({"unchecked", "Duplicates"})
@ -114,6 +125,61 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test {
} }
} }
@Test
public void testRevIncludeOnIncludedResource() {
SearchParameter sp = new SearchParameter();
sp.addBase("Procedure");
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.setCode("focalAccess");
sp.setType(Enumerations.SearchParamType.REFERENCE);
sp.setExpression("Procedure.extension('http://fkcfhir.org/fhir/cs/CS1MachNumber')");
sp.addTarget("BodyStructure");
mySearchParameterDao.create(sp, mySrd);
mySearchParamRegistry.forceRefresh();
BodyStructure bs = new BodyStructure();
bs.setId("B51936689");
bs.setDescription("Foo");
myBodyStructureDao.update(bs, mySrd);
Procedure p = new Procedure();
p.setId("PRA8780542726");
p.setStatus(org.hl7.fhir.r4.model.Procedure.ProcedureStatus.COMPLETED);
myProcedureDao.update(p, mySrd);
p = new Procedure();
p.setId("PRA8780542785");
p.addPartOf().setReference("Procedure/PRA8780542726");
p.setStatus(org.hl7.fhir.r4.model.Procedure.ProcedureStatus.COMPLETED);
p.addExtension("http://fkcfhir.org/fhir/cs/CS1MachNumber", new Reference("BodyStructure/B51936689"));
myProcedureDao.update(p, mySrd);
logAllResources();
logAllResourceLinks();
// Non synchronous
SearchParameterMap map = new SearchParameterMap();
map.add("_id", new TokenParam("PRA8780542726"));
map.addRevInclude(new Include("Procedure:part-of"));
map.addInclude(new Include("Procedure:focalAccess").asRecursive());
IBundleProvider outcome = myProcedureDao.search(map, mySrd);
assertEquals(PersistedJpaSearchFirstPageBundleProvider.class, outcome.getClass());
List<String> ids = toUnqualifiedVersionlessIdValues(outcome);
assertThat(ids.toString(), ids, Matchers.containsInAnyOrder("Procedure/PRA8780542726", "Procedure/PRA8780542785", "BodyStructure/B51936689"));
// Synchronous
map = new SearchParameterMap().setLoadSynchronous(true);
map.add("_id", new TokenParam("PRA8780542726"));
map.addRevInclude(new Include("Procedure:part-of"));
map.addInclude(new Include("Procedure:focalAccess").asRecursive());
outcome = myProcedureDao.search(map, mySrd);
assertEquals(SimpleBundleProvider.class, outcome.getClass());
ids = toUnqualifiedVersionlessIdValues(outcome);
assertThat(ids.toString(), ids, Matchers.containsInAnyOrder("Procedure/PRA8780542726", "Procedure/PRA8780542785", "BodyStructure/B51936689"));
}
@Test @Test
public void testRevIncludesPaged_AsyncSearch() { public void testRevIncludesPaged_AsyncSearch() {
int eocCount = 10; int eocCount = 10;

View File

@ -33,6 +33,7 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseR4SearchLastN {
// Set up search parameters that will return 75 Observations. // Set up search parameters that will return 75 Observations.
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronous(true);
ReferenceParam subjectParam1 = new ReferenceParam("Patient", "", patient0Id.getValue()); ReferenceParam subjectParam1 = new ReferenceParam("Patient", "", patient0Id.getValue());
ReferenceParam subjectParam2 = new ReferenceParam("Patient", "", patient1Id.getValue()); ReferenceParam subjectParam2 = new ReferenceParam("Patient", "", patient1Id.getValue());
ReferenceParam subjectParam3 = new ReferenceParam("Patient", "", patient2Id.getValue()); ReferenceParam subjectParam3 = new ReferenceParam("Patient", "", patient2Id.getValue());

View File

@ -778,7 +778,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
List<IIdType> actual = toUnqualifiedVersionlessIds(resp); List<IIdType> actual = toUnqualifiedVersionlessIds(resp);
myCaptureQueriesListener.logSelectQueriesForCurrentThread(); myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(actual, containsInAnyOrder(orgId, medId, patId, moId, patId2)); assertThat(actual, containsInAnyOrder(orgId, medId, patId, moId, patId2));
assertEquals(5, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); assertEquals(1, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
// Specific patient ID with linked stuff // Specific patient ID with linked stuff
request = mock(HttpServletRequest.class); request = mock(HttpServletRequest.class);

View File

@ -41,7 +41,7 @@ public class EmailSubscriptionDstu2Test extends BaseResourceProviderDstu2Test {
private static final Logger ourLog = LoggerFactory.getLogger(EmailSubscriptionDstu2Test.class); private static final Logger ourLog = LoggerFactory.getLogger(EmailSubscriptionDstu2Test.class);
@RegisterExtension @RegisterExtension
static GreenMailExtension ourGreenMail = new GreenMailExtension(ServerSetupTest.SMTP); static GreenMailExtension ourGreenMail = new GreenMailExtension(ServerSetupTest.SMTP.withPort(0));
private List<IIdType> mySubscriptionIds = new ArrayList<>(); private List<IIdType> mySubscriptionIds = new ArrayList<>();
@ -148,7 +148,7 @@ public class EmailSubscriptionDstu2Test extends BaseResourceProviderDstu2Test {
private MailConfig withMailConfig() { private MailConfig withMailConfig() {
return new MailConfig() return new MailConfig()
.setSmtpHostname(ServerSetupTest.SMTP.getBindAddress()) .setSmtpHostname(ServerSetupTest.SMTP.getBindAddress())
.setSmtpPort(ServerSetupTest.SMTP.getPort()); .setSmtpPort(ourGreenMail.getSmtp().getPort());
} }
} }

View File

@ -42,7 +42,7 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(EmailSubscriptionDstu3Test.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(EmailSubscriptionDstu3Test.class);
@RegisterExtension @RegisterExtension
static GreenMailExtension ourGreenMail = new GreenMailExtension(ServerSetupTest.SMTP); static GreenMailExtension ourGreenMail = new GreenMailExtension(ServerSetupTest.SMTP.withPort(0));
@Autowired @Autowired
private SubscriptionTestUtil mySubscriptionTestUtil; private SubscriptionTestUtil mySubscriptionTestUtil;
@ -261,7 +261,7 @@ public class EmailSubscriptionDstu3Test extends BaseResourceProviderDstu3Test {
private MailConfig withMailConfig() { private MailConfig withMailConfig() {
return new MailConfig() return new MailConfig()
.setSmtpHostname(ServerSetupTest.SMTP.getBindAddress()) .setSmtpHostname(ServerSetupTest.SMTP.getBindAddress())
.setSmtpPort(ServerSetupTest.SMTP.getPort()); .setSmtpPort(ourGreenMail.getSmtp().getPort());
} }