Support double _has expressions (#1939)

* Support nested _has queries

* Add changelog
This commit is contained in:
James Agnew 2020-06-23 17:58:47 -04:00 committed by GitHub
parent 6825d2fcf0
commit e65c264927
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 93 additions and 21 deletions

View File

@ -0,0 +1,5 @@
---
type: add
issue: 1939
title: "The JPA server is now able to support _has queries where the linked search expression on the right hand side of the
_has parameter is a second _has query."

View File

@ -62,6 +62,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.HasOrListParam;
import ca.uhn.fhir.rest.param.HasParam;
import ca.uhn.fhir.rest.param.NumberParam;
import ca.uhn.fhir.rest.param.ParameterUtil;
@ -944,30 +945,46 @@ class PredicateBuilderReference extends BasePredicateBuilder {
throw new InvalidRequestException("Invalid resource type: " + targetResourceType);
}
//Ensure that the name of the search param
// (e.g. the `code` in Patient?_has:Observation:subject:code=sys|val)
// exists on the target resource type.
RuntimeSearchParam owningParameterDef = mySearchParamRegistry.getSearchParamByName(targetResourceDefinition, paramName);
if (owningParameterDef == null) {
throw new InvalidRequestException("Unknown parameter name: " + targetResourceType + ':' + parameterName);
}
//Ensure that the name of the back-referenced search param on the target (e.g. the `subject` in Patient?_has:Observation:subject:code=sys|val)
//exists on the target resource.
owningParameterDef = mySearchParamRegistry.getSearchParamByName(targetResourceDefinition, paramReference);
if (owningParameterDef == null) {
throw new InvalidRequestException("Unknown parameter name: " + targetResourceType + ':' + paramReference);
}
RuntimeSearchParam paramDef = mySearchParamRegistry.getSearchParamByName(targetResourceDefinition, paramName);
IQueryParameterAnd<IQueryParameterOr<IQueryParameterType>> parsedParam = (IQueryParameterAnd<IQueryParameterOr<IQueryParameterType>>) ParameterUtil.parseQueryParams(myContext, paramDef, paramName, parameters);
ArrayList<IQueryParameterType> orValues = Lists.newArrayList();
for (IQueryParameterOr<IQueryParameterType> next : parsedParam.getValuesAsQueryTokens()) {
orValues.addAll(next.getValuesAsQueryTokens());
if (paramName.startsWith("_has:")) {
ourLog.trace("Handing double _has query: {}", paramName);
String qualifier = paramName.substring(4);
paramName = Constants.PARAM_HAS;
for (IQueryParameterType next : nextOrList) {
HasParam nextHasParam = new HasParam();
nextHasParam.setValueAsQueryToken(myContext, Constants.PARAM_HAS, qualifier, next.getValueAsQueryToken(myContext));
orValues.add(nextHasParam);
}
} else {
//Ensure that the name of the search param
// (e.g. the `code` in Patient?_has:Observation:subject:code=sys|val)
// exists on the target resource type.
RuntimeSearchParam owningParameterDef = mySearchParamRegistry.getSearchParamByName(targetResourceDefinition, paramName);
if (owningParameterDef == null) {
throw new InvalidRequestException("Unknown parameter name: " + targetResourceType + ':' + parameterName);
}
//Ensure that the name of the back-referenced search param on the target (e.g. the `subject` in Patient?_has:Observation:subject:code=sys|val)
//exists on the target resource.
owningParameterDef = mySearchParamRegistry.getSearchParamByName(targetResourceDefinition, paramReference);
if (owningParameterDef == null) {
throw new InvalidRequestException("Unknown parameter name: " + targetResourceType + ':' + paramReference);
}
RuntimeSearchParam paramDef = mySearchParamRegistry.getSearchParamByName(targetResourceDefinition, paramName);
IQueryParameterAnd<IQueryParameterOr<IQueryParameterType>> parsedParam = (IQueryParameterAnd<IQueryParameterOr<IQueryParameterType>>) ParameterUtil.parseQueryParams(myContext, paramDef, paramName, parameters);
for (IQueryParameterOr<IQueryParameterType> next : parsedParam.getValuesAsQueryTokens()) {
orValues.addAll(next.getValuesAsQueryTokens());
}
}
//Handle internal chain inside the has.
if (parameterName.contains(".")) {
String chainedPartOfParameter = getChainedPart(parameterName);

View File

@ -876,6 +876,11 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("NOLINK");
obs.setDevice(new Reference(devId));
IIdType obsId = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
DiagnosticReport dr = new DiagnosticReport();
dr.addResult().setReference(obsId.getValue());
dr.setStatus(DiagnosticReport.DiagnosticReportStatus.FINAL);
myObservationDao.create(obs, mySrd);
}
@ -896,6 +901,51 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), empty());
}
@Test
public void testHasParameterDouble() {
// Matching
IIdType pid0;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("00");
patient.addName().setFamily("Tester").addGiven("Joe");
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("NOLINK");
obs.setSubject(new Reference(pid0));
IIdType obsId = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
DiagnosticReport dr = new DiagnosticReport();
dr.addResult().setReference(obsId.getValue());
dr.setStatus(DiagnosticReport.DiagnosticReportStatus.FINAL);
myDiagnosticReportDao.create(dr, mySrd);
}
// Matching
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.addName().setFamily("Tester").addGiven("Joe");
IIdType pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("NOLINK");
obs.setSubject(new Reference(pid1));
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
}
SearchParameterMap params = SearchParameterMap.newSynchronous();
// Double _has
params = new SearchParameterMap();
params.add("_has", new HasParam("Observation", "subject", "_has:DiagnosticReport:result:status", "final"));
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), containsInAnyOrder(pid0.getValue()));
}
@Test
public void testHasParameterChained() {
IIdType pid0;