Sandbox code
This commit is contained in:
parent
e63b137e17
commit
6c45135645
|
@ -108,6 +108,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -121,7 +122,6 @@ import java.util.Optional;
|
|||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static ca.uhn.fhir.jpa.util.QueryParameterUtils.fromOperation;
|
||||
import static ca.uhn.fhir.jpa.util.QueryParameterUtils.getChainedPart;
|
||||
|
@ -2283,6 +2283,17 @@ public class QueryStack {
|
|||
return null;
|
||||
}
|
||||
|
||||
// fixme jm: handle following switch params
|
||||
if ( theSearchContainedMode != SearchContainedModeEnum.FALSE) {
|
||||
return createContainedModePredicateSearchParameter(
|
||||
theSourceJoinColumn,
|
||||
theResourceName,
|
||||
theParamName,
|
||||
theAndOrParams,
|
||||
theRequest,
|
||||
theRequestPartitionId);
|
||||
}
|
||||
|
||||
switch (theParamName) {
|
||||
case IAnyResource.SP_RES_ID:
|
||||
return createPredicateResourceId(
|
||||
|
@ -2321,6 +2332,50 @@ public class QueryStack {
|
|||
}
|
||||
}
|
||||
|
||||
private Condition createContainedModePredicateSearchParameter(
|
||||
DbColumn theSourceJoinColumn, String theResourceName, String theParamName,
|
||||
List<List<IQueryParameterType>> theAndOrParams, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
|
||||
|
||||
// extract reference params which target is theResourceName
|
||||
List<RuntimeSearchParam> resourceTypeReferencingParams = getResourceTypeReferencingParams(theResourceName);
|
||||
|
||||
List<Condition> conditionList = new ArrayList<>();
|
||||
for (RuntimeSearchParam param : resourceTypeReferencingParams) {
|
||||
for (String paramBaseResource : param.getBase()) {
|
||||
Condition paramCondition = createPredicateSearchParameter(
|
||||
theSourceJoinColumn,
|
||||
paramBaseResource,
|
||||
param.getName(),
|
||||
theAndOrParams,
|
||||
theRequest,
|
||||
theRequestPartitionId);
|
||||
conditionList.add(paramCondition);
|
||||
}
|
||||
}
|
||||
return toAndPredicate(conditionList);
|
||||
}
|
||||
|
||||
// fixme jm: improve and move to util
|
||||
private List<RuntimeSearchParam> getResourceTypeReferencingParams(String theResourceName) {
|
||||
Map<String, String> referenceParameterNamesByResourceType = new HashMap<>();
|
||||
myFhirContext.getResourceTypes().forEach( resType -> {
|
||||
mySearchParamRegistry.getActiveSearchParams(resType).getReferenceSearchParamNames().forEach(
|
||||
paramName -> referenceParameterNamesByResourceType.put(resType, paramName));
|
||||
});
|
||||
|
||||
List<RuntimeSearchParam> allReferenceParameters = new ArrayList<>();
|
||||
referenceParameterNamesByResourceType.forEach((resType, paramName) -> {
|
||||
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam(resType, paramName);
|
||||
if (param.getTargets().contains(theResourceName)) {
|
||||
allReferenceParameters.add(param);
|
||||
}
|
||||
});
|
||||
|
||||
// fixme jm: convert to original parameter types???
|
||||
|
||||
return allReferenceParameters;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Condition createPredicateSearchParameter(
|
||||
@Nullable DbColumn theSourceJoinColumn,
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ReadOnlySearchParamCache;
|
||||
import ca.uhn.fhir.jpa.test.config.TestR4Config;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
|
||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.hibernate.dialect.PostgreSQL10Dialect;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.CarePlan;
|
||||
|
@ -35,18 +41,24 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
|
||||
@ContextConfiguration(classes = {ResourceProviderR4SearchContainedTest.OverriddenR4Config.class})
|
||||
public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR4Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4SearchContainedTest.class);
|
||||
|
@ -58,15 +70,15 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
|
|||
@Override
|
||||
@AfterEach
|
||||
public void after() throws Exception {
|
||||
super.after();
|
||||
|
||||
myStorageSettings.setAllowMultipleDelete(new JpaStorageSettings().isAllowMultipleDelete());
|
||||
myStorageSettings.setAllowExternalReferences(new JpaStorageSettings().isAllowExternalReferences());
|
||||
myStorageSettings.setReuseCachedSearchResultsForMillis(new JpaStorageSettings().getReuseCachedSearchResultsForMillis());
|
||||
myStorageSettings.setCountSearchResultsUpTo(new JpaStorageSettings().getCountSearchResultsUpTo());
|
||||
myStorageSettings.setSearchPreFetchThresholds(new JpaStorageSettings().getSearchPreFetchThresholds());
|
||||
myStorageSettings.setAllowContainsSearches(new JpaStorageSettings().isAllowContainsSearches());
|
||||
myStorageSettings.setIndexMissingFields(new JpaStorageSettings().getIndexMissingFields());
|
||||
// super.after();
|
||||
//
|
||||
// myStorageSettings.setAllowMultipleDelete(new JpaStorageSettings().isAllowMultipleDelete());
|
||||
// myStorageSettings.setAllowExternalReferences(new JpaStorageSettings().isAllowExternalReferences());
|
||||
// myStorageSettings.setReuseCachedSearchResultsForMillis(new JpaStorageSettings().getReuseCachedSearchResultsForMillis());
|
||||
// myStorageSettings.setCountSearchResultsUpTo(new JpaStorageSettings().getCountSearchResultsUpTo());
|
||||
// myStorageSettings.setSearchPreFetchThresholds(new JpaStorageSettings().getSearchPreFetchThresholds());
|
||||
// myStorageSettings.setAllowContainsSearches(new JpaStorageSettings().isAllowContainsSearches());
|
||||
// myStorageSettings.setIndexMissingFields(new JpaStorageSettings().getIndexMissingFields());
|
||||
|
||||
myClient.unregisterInterceptor(myCapturingInterceptor);
|
||||
myStorageSettings.setIndexOnContainedResources(false);
|
||||
|
@ -360,13 +372,123 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sandboxContainedParameterTest() throws IOException {
|
||||
// Some useful values
|
||||
final String patientFamily = "VanHouten";
|
||||
final String patientGiven = "Milhouse";
|
||||
|
||||
// Create a discrete Patient
|
||||
final IIdType discretePatientId;
|
||||
{
|
||||
Patient discretePatient = new Patient();
|
||||
discretePatient.addName().setFamily(patientFamily).addGiven(patientGiven);
|
||||
discretePatientId = myPatientDao.create(discretePatient, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
ourLog.debug("\nInput - Discrete Patient:\n{}",
|
||||
myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(discretePatient));
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a discrete Observation, which includes a contained Patient
|
||||
* - The contained Patient is otherwise identical to the discrete Patient above
|
||||
*/
|
||||
final IIdType discreteObservationId;
|
||||
final String containedPatientId = "contained-patient-1";
|
||||
{
|
||||
Patient containedPatient = new Patient();
|
||||
containedPatient.setId(containedPatientId);
|
||||
containedPatient.addName().setFamily(patientFamily).addGiven(patientGiven);
|
||||
|
||||
Observation discreteObservation = new Observation();
|
||||
discreteObservation.getContained().add(containedPatient);
|
||||
discreteObservation.getSubject().setReference("#" + containedPatientId);
|
||||
discreteObservationId = myObservationDao.create(discreteObservation, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
ourLog.debug("\nInput - Discrete Observation with contained Patient:\n{}",
|
||||
myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(discreteObservation));
|
||||
}
|
||||
|
||||
{
|
||||
/*
|
||||
* Case 3
|
||||
* When: we search for Patient with `_contained=true`
|
||||
* - `_contained=true` means we should search and return only contained resources; not discrete resources
|
||||
* - Note that we are searching by `/Patient?`, not `/Observation?`
|
||||
* - When `_containedType` is absent, the default value of `_containedType=container` should be used
|
||||
* - We should return the container resources
|
||||
*/
|
||||
String queryUrl = myServerBase + "/Patient?family=" + patientFamily + "&given=" + patientGiven + "&_contained=true";
|
||||
|
||||
// to obtain quantities for all resources
|
||||
// myFhirContext.getResourceTypes().stream().sorted().forEach(
|
||||
// rt -> getContainingResourceIds(rt, patientFamily, patientGiven));
|
||||
getContainingResourceIds("Patient", patientFamily, patientGiven);
|
||||
|
||||
// Then: we should get the Observation that is containing that Patient
|
||||
// List<String> resourceIds = searchAndReturnUnqualifiedVersionlessIdValues(queryUrl);
|
||||
// assertEquals(1L, resourceIds.size());
|
||||
// assertThat(resourceIds, contains(discreteObservationId));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void getContainingResourceIds(String theResourceType, String theFamily, String theGiven) {
|
||||
|
||||
// fixme jm: try obtaining the desired result using chained params from every possible referencing resource
|
||||
|
||||
// get reference SPs to resourceType
|
||||
ReadOnlySearchParamCache spCache = mySearchParamRegistry.getActiveSearchParams();
|
||||
List<RuntimeSearchParam> refSPs = spCache.getSearchParamStream()
|
||||
.filter(sp -> sp.getParamType() == RestSearchParameterTypeEnum.REFERENCE)
|
||||
.filter(sp -> sp.getTargets().contains(theResourceType))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 241 SPs for Patient
|
||||
|
||||
// obtain possible referrers for each ref SP obtained,
|
||||
|
||||
List<RuntimeSearchParam> level2SPs = new ArrayList<>();
|
||||
for (RuntimeSearchParam referringSP : refSPs) {
|
||||
for (String referringSPBase : referringSP.getBase()) {
|
||||
level2SPs.addAll(
|
||||
spCache.getSearchParamStream()
|
||||
.filter(sp -> sp.getParamType() == RestSearchParameterTypeEnum.REFERENCE)
|
||||
.filter(sp -> sp.getTargets().contains(referringSPBase))
|
||||
.toList());
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.info(String.format("Found %1$,3d lvl1 and %2$,7d lvl2 params for resource type: %3$s", refSPs.size(), level2SPs.size(), theResourceType));
|
||||
|
||||
|
||||
// Parameters params = new Parameters();
|
||||
//// params.addParameter().setName("family").setValue(new StringType(theFamily));
|
||||
//// params.addParameter().setName("given").setValue(new StringType(theGiven));
|
||||
//
|
||||
// for (RuntimeSearchParam refSP : refSPs) {
|
||||
// if (refSP.getBase().contains("Observation") && refSP.getName().equals("subject")) {
|
||||
// params.addParameter().setName(refSP.getName()).setValue(new StringType(theFamily));
|
||||
// params.addParameter().setName(refSP.getName()).setValue(new StringType(theGiven));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//// myPatientResourceProvider.search()
|
||||
//
|
||||
//
|
||||
// Bundle result = myClient.operation().onServer().named("search").withParameters(params).returnResourceType(Bundle.class).execute();
|
||||
//
|
||||
assertNotNull(refSPs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unit test with multiple cases to illustrate expected behaviour of <code>_contained</code> and <code>_containedType</code> with chaining
|
||||
* <p>
|
||||
* Although this test is in R4, the R5 specification for these parameters is much clearer:
|
||||
* - <a href="https://www.hl7.org/fhir/search.html#contained">_contained & _containedType</a>
|
||||
* <p>
|
||||
* It seems as though the initial implementation conflated the use of searching contained resources via chaining with
|
||||
* It seems as though the initial implementation `conflated` the use of searching contained resources via chaining with
|
||||
* the use of the <code>_contained</code> search parameter. All the existing tests use <code>_contained</code> with chaining but neglect to
|
||||
* search by the contained resource type (i.e. they search by the container resource type). This test corrects that
|
||||
* with several cases.
|
||||
|
@ -384,7 +506,7 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
|
|||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
// @Test
|
||||
public void testContainedParameterBehaviourWithChain() throws IOException {
|
||||
// Some useful values
|
||||
final String patientFamily = "VanHouten";
|
||||
|
@ -1390,4 +1512,30 @@ public class ResourceProviderR4SearchContainedTest extends BaseResourceProviderR
|
|||
return ids;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class OverriddenR4Config extends TestR4Config {
|
||||
|
||||
@Override
|
||||
public void setConnectionProperties(BasicDataSource theDataSource) {
|
||||
theDataSource.setDriver(new org.postgresql.Driver());
|
||||
theDataSource.setUrl("jdbc:postgresql://localhost/test-db");
|
||||
theDataSource.setMaxWaitMillis(-1); // indefinite
|
||||
theDataSource.setUsername("cdr");
|
||||
theDataSource.setPassword("cdr");
|
||||
theDataSource.setMaxTotal(ourMaxThreads);
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHibernateDialect() {
|
||||
return PostgreSQL10Dialect.class.getName();
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public ProxyDataSourceBuilder.SingleQueryExecution getMandatoryTransactionListener() {
|
||||
// return getNoopTXListener();
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,13 +28,13 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
// TODO: JA remove default methods
|
||||
public interface ISearchParamRegistry {
|
||||
|
|
Loading…
Reference in New Issue