Support composite parametrs with quantity type in JPA

This commit is contained in:
James Agnew 2016-05-06 18:43:07 -04:00
parent 8c8434943c
commit 4ae4b1a8d8
4 changed files with 246 additions and 128 deletions

View File

@ -452,64 +452,12 @@ public class SearchBuilder {
List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr;
if (addPredicateMissingFalseIfPresent(builder, theParamName, from, codePredicates, nextOr)) {
continue;
}
String systemValue;
String unitsValue;
ParamPrefixEnum cmpValue;
BigDecimal valueValue;
String valueString;
if (params instanceof BaseQuantityDt) {
BaseQuantityDt param = (BaseQuantityDt) params;
systemValue = param.getSystemElement().getValueAsString();
unitsValue = param.getUnitsElement().getValueAsString();
cmpValue = ParamPrefixEnum.forDstu1Value(param.getComparatorElement().getValueAsString());
valueValue = param.getValueElement().getValue();
valueString = param.getValueElement().getValueAsString();
} else if (params instanceof QuantityParam) {
QuantityParam param = (QuantityParam) params;
systemValue = param.getSystem();
unitsValue = param.getUnits();
cmpValue = param.getPrefix();
valueValue = param.getValue();
valueString = param.getValueAsString();
} else {
throw new IllegalArgumentException("Invalid quantity type: " + params.getClass());
}
Predicate system = null;
if (!isBlank(systemValue)) {
system = builder.equal(from.get("mySystem"), systemValue);
}
Predicate code = null;
if (!isBlank(unitsValue)) {
code = builder.equal(from.get("myUnits"), unitsValue);
}
cmpValue = ObjectUtils.defaultIfNull(cmpValue, ParamPrefixEnum.EQUAL);
final Expression<BigDecimal> path = from.get("myValue");
String invalidMessageName = "invalidQuantityPrefix";
Predicate num = createPredicateNumeric(builder, params, cmpValue, valueValue, path, invalidMessageName, valueString);
if (system == null && code == null) {
codePredicates.add(num);
} else if (system == null) {
Predicate singleCode = builder.and(code, num);
Predicate singleCode = createPredicateQuantity(builder, from, nextOr);
codePredicates.add(singleCode);
} else if (code == null) {
Predicate singleCode = builder.and(system, num);
codePredicates.add(singleCode);
} else {
Predicate singleCode = builder.and(system, code, num);
codePredicates.add(singleCode);
}
}
List<Predicate> predicates = new ArrayList<Predicate>();
@ -1039,6 +987,11 @@ public class SearchBuilder {
retVal = createPredicateDate(builder, dateJoin, leftValue);
break;
}
case QUANTITY: {
From<ResourceIndexedSearchParamQuantity, ResourceIndexedSearchParamQuantity> dateJoin = from.join("myParamsQuantity", JoinType.INNER);
retVal = createPredicateQuantity(builder, dateJoin, leftValue);
break;
}
}
if (retVal == null) {
@ -1168,6 +1121,61 @@ public class SearchBuilder {
return num;
}
private Predicate createPredicateQuantity(CriteriaBuilder theBuilder, From<ResourceIndexedSearchParamQuantity, ResourceIndexedSearchParamQuantity> theFrom, IQueryParameterType theParam) {
String systemValue;
String unitsValue;
ParamPrefixEnum cmpValue;
BigDecimal valueValue;
String valueString;
if (theParam instanceof BaseQuantityDt) {
BaseQuantityDt param = (BaseQuantityDt) theParam;
systemValue = param.getSystemElement().getValueAsString();
unitsValue = param.getUnitsElement().getValueAsString();
cmpValue = ParamPrefixEnum.forDstu1Value(param.getComparatorElement().getValueAsString());
valueValue = param.getValueElement().getValue();
valueString = param.getValueElement().getValueAsString();
} else if (theParam instanceof QuantityParam) {
QuantityParam param = (QuantityParam) theParam;
systemValue = param.getSystem();
unitsValue = param.getUnits();
cmpValue = param.getPrefix();
valueValue = param.getValue();
valueString = param.getValueAsString();
} else {
throw new IllegalArgumentException("Invalid quantity type: " + theParam.getClass());
}
Predicate system = null;
if (!isBlank(systemValue)) {
system = theBuilder.equal(theFrom.get("mySystem"), systemValue);
}
Predicate code = null;
if (!isBlank(unitsValue)) {
code = theBuilder.equal(theFrom.get("myUnits"), unitsValue);
}
cmpValue = ObjectUtils.defaultIfNull(cmpValue, ParamPrefixEnum.EQUAL);
final Expression<BigDecimal> path = theFrom.get("myValue");
String invalidMessageName = "invalidQuantityPrefix";
Predicate num = createPredicateNumeric(theBuilder, theParam, cmpValue, valueValue, path, invalidMessageName, valueString);
Predicate singleCode;
if (system == null && code == null) {
singleCode = num;
} else if (system == null) {
singleCode = theBuilder.and(code, num);
} else if (code == null) {
singleCode = theBuilder.and(system, num);
} else {
singleCode = theBuilder.and(system, code, num);
}
return singleCode;
}
private void createPredicateResourceId(CriteriaBuilder builder, CriteriaQuery<?> cq, List<Predicate> thePredicates, Expression<Long> theExpression) {
if (myParams.isPersistResults()) {
if (mySearchEntity.getTotalCount() > -1) {

View File

@ -47,6 +47,7 @@ import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.dstu.resource.BaseResource;
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
@ -98,7 +99,6 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.TestUtil;
@SuppressWarnings("unchecked")
@ -518,6 +518,58 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
}
@Test
public void testSearchCompositeParamQuantity() {
//@formatter:off
Observation o1 = new Observation();
o1.addComponent()
.setCode(new CodeableConceptDt().addCoding(new CodingDt().setSystem("http://foo").setCode("code1")))
.setValue(new QuantityDt().setSystem("http://bar").setCode("code1").setValue(100));
o1.addComponent()
.setCode(new CodeableConceptDt().addCoding(new CodingDt().setSystem("http://foo").setCode("code2")))
.setValue(new QuantityDt().setSystem("http://bar").setCode("code2").setValue(100));
IIdType id1 = myObservationDao.create(o1, mySrd).getId().toUnqualifiedVersionless();
Observation o2 = new Observation();
o2.addComponent()
.setCode(new CodeableConceptDt().addCoding(new CodingDt().setSystem("http://foo").setCode("code1")))
.setValue(new QuantityDt().setSystem("http://bar").setCode("code1").setValue(200));
o2.addComponent()
.setCode(new CodeableConceptDt().addCoding(new CodingDt().setSystem("http://foo").setCode("code3")))
.setValue(new QuantityDt().setSystem("http://bar").setCode("code2").setValue(200));
IIdType id2 = myObservationDao.create(o2, mySrd).getId().toUnqualifiedVersionless();
//@formatter:on
{
TokenParam v0 = new TokenParam("http://foo", "code1");
QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 150, "http://bar", "code1");
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<TokenParam, QuantityParam>(v0, v1);
IBundleProvider result = myObservationDao.search(Observation.SP_COMPONENT_CODE_COMPONENT_VALUE_QUANTITY, val);
assertThat(toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(id2.getValue()));
}
{
TokenParam v0 = new TokenParam("http://foo", "code1");
QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 50, "http://bar", "code1");
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<TokenParam, QuantityParam>(v0, v1);
IBundleProvider result = myObservationDao.search(Observation.SP_COMPONENT_CODE_COMPONENT_VALUE_QUANTITY, val);
assertThat(toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(id1.getValue(), id2.getValue()));
}
{
TokenParam v0 = new TokenParam("http://foo", "code4");
QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 50, "http://bar", "code1");
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<TokenParam, QuantityParam>(v0, v1);
IBundleProvider result = myObservationDao.search(Observation.SP_COMPONENT_CODE_COMPONENT_VALUE_QUANTITY, val);
assertThat(toUnqualifiedVersionlessIdValues(result), empty());
}
{
TokenParam v0 = new TokenParam("http://foo", "code1");
QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 50, "http://bar", "code4");
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<TokenParam, QuantityParam>(v0, v1);
IBundleProvider result = myObservationDao.search(Observation.SP_COMPONENT_CODE_COMPONENT_VALUE_QUANTITY, val);
assertThat(toUnqualifiedVersionlessIdValues(result), empty());
}
}
/**
* #222
*/

View File

@ -25,6 +25,8 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu3.model.Appointment;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.ConceptMap;
import org.hl7.fhir.dstu3.model.ContactPoint.ContactPointSystem;
import org.hl7.fhir.dstu3.model.DateTimeType;
@ -99,12 +101,6 @@ import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3SearchNoFtTest.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Test
public void testCodeSearch() {
Subscription subs = new Subscription();
@ -119,6 +115,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
assertThat(toUnqualifiedVersionlessIds(mySubscriptionDao.search(map)), contains(id));
}
@Test
public void testEverythingTimings() throws Exception {
String methodName = "testEverythingIncludesBackReferences";
@ -438,35 +435,6 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
}
/**
* https://chat.fhir.org/#narrow/stream/implementers/topic/Understanding.20_include
*/
@Test
public void testSearchWithTypedInclude() {
IIdType patId;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
patId = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
IIdType practId;
{
Practitioner pract = new Practitioner();
pract.addIdentifier().setSystem("urn:system").setValue("001");
practId = myPractitionerDao.create(pract, mySrd).getId().toUnqualifiedVersionless();
}
Appointment appt = new Appointment();
appt.addParticipant().getActor().setReference(patId.getValue());
appt.addParticipant().getActor().setReference(practId.getValue());
IIdType apptId = myAppointmentDao.create(appt, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params = new SearchParameterMap();
params.addInclude(Appointment.INCLUDE_PATIENT);
assertThat(toUnqualifiedVersionlessIds(myAppointmentDao.search(params)), containsInAnyOrder(patId, apptId));
}
@Test
public void testSearchByIdParamWrongType() {
IIdType id1;
@ -550,6 +518,57 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
}
@Test
public void testSearchCompositeParamQuantity() {
//@formatter:off
Observation o1 = new Observation();
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code1")))
.setValue(new Quantity().setSystem("http://bar").setCode("code1").setValue(100));
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code2")))
.setValue(new Quantity().setSystem("http://bar").setCode("code2").setValue(100));
IIdType id1 = myObservationDao.create(o1, mySrd).getId().toUnqualifiedVersionless();
Observation o2 = new Observation();
o2.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code1")))
.setValue(new Quantity().setSystem("http://bar").setCode("code1").setValue(200));
o2.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code3")))
.setValue(new Quantity().setSystem("http://bar").setCode("code2").setValue(200));
IIdType id2 = myObservationDao.create(o2, mySrd).getId().toUnqualifiedVersionless();
//@formatter:on
{
TokenParam v0 = new TokenParam("http://foo", "code1");
QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 150, "http://bar", "code1");
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<TokenParam, QuantityParam>(v0, v1);
IBundleProvider result = myObservationDao.search(Observation.SP_COMPONENT_CODE_VALUE_QUANTITY, val);
assertThat(toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(id2.getValue()));
}
{
TokenParam v0 = new TokenParam("http://foo", "code1");
QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 50, "http://bar", "code1");
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<TokenParam, QuantityParam>(v0, v1);
IBundleProvider result = myObservationDao.search(Observation.SP_COMPONENT_CODE_VALUE_QUANTITY, val);
assertThat(toUnqualifiedVersionlessIdValues(result), containsInAnyOrder(id1.getValue(), id2.getValue()));
}
{
TokenParam v0 = new TokenParam("http://foo", "code4");
QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 50, "http://bar", "code1");
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<TokenParam, QuantityParam>(v0, v1);
IBundleProvider result = myObservationDao.search(Observation.SP_COMPONENT_CODE_VALUE_QUANTITY, val);
assertThat(toUnqualifiedVersionlessIdValues(result), empty());
}
{
TokenParam v0 = new TokenParam("http://foo", "code1");
QuantityParam v1 = new QuantityParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, 50, "http://bar", "code4");
CompositeParam<TokenParam, QuantityParam> val = new CompositeParam<TokenParam, QuantityParam>(v0, v1);
IBundleProvider result = myObservationDao.search(Observation.SP_COMPONENT_CODE_VALUE_QUANTITY, val);
assertThat(toUnqualifiedVersionlessIdValues(result), empty());
}
}
/**
* #222
@ -867,6 +886,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
ourLog.info("Searching: {}", map.getLastUpdated());
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), containsInAnyOrder(id1a));
}
@Test
public void testSearchNameParam() {
IIdType id1;
@ -926,8 +946,6 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
myLocationDao.create(loc, mySrd);
}
}
@Test
public void testSearchNumberParam() {
Encounter e1 = new Encounter();
@ -955,7 +973,6 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
}
}
@Test
public void testSearchParamChangesType() {
String name = "testSearchParamChangesType";
@ -983,6 +1000,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
}
@Test
public void testSearchPractitionerPhoneAndEmailParam() {
String methodName = "testSearchPractitionerPhoneAndEmailParam";
@ -1032,6 +1050,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
}
@Test
public void testSearchResourceLinkWithChain() {
Patient patient = new Patient();
@ -2209,6 +2228,57 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
}
}
/**
* https://chat.fhir.org/#narrow/stream/implementers/topic/Understanding.20_include
*/
@Test
public void testSearchWithTypedInclude() {
IIdType patId;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
patId = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
IIdType practId;
{
Practitioner pract = new Practitioner();
pract.addIdentifier().setSystem("urn:system").setValue("001");
practId = myPractitionerDao.create(pract, mySrd).getId().toUnqualifiedVersionless();
}
Appointment appt = new Appointment();
appt.addParticipant().getActor().setReference(patId.getValue());
appt.addParticipant().getActor().setReference(practId.getValue());
IIdType apptId = myAppointmentDao.create(appt, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params = new SearchParameterMap();
params.addInclude(Appointment.INCLUDE_PATIENT);
assertThat(toUnqualifiedVersionlessIds(myAppointmentDao.search(params)), containsInAnyOrder(patId, apptId));
}
@Test
public void testSearchWithUriParam() throws Exception {
Class<ValueSet> type = ValueSet.class;
String resourceName = "/valueset-dstu2.json";
ValueSet vs = loadResourceFromClasspath(type, resourceName);
IIdType id1 = myValueSetDao.update(vs, mySrd).getId().toUnqualifiedVersionless();
ValueSet vs2 = new ValueSet();
vs2.setUrl("http://hl7.org/foo/bar");
IIdType id2 = myValueSetDao.create(vs2, mySrd).getId().toUnqualifiedVersionless();
IBundleProvider result;
result = myValueSetDao.search(ValueSet.SP_URL, new UriParam("http://hl7.org/fhir/ValueSet/basic-resource-type"));
assertThat(toUnqualifiedVersionlessIds(result), contains(id1));
result = myValueSetDao.search(ValueSet.SP_URL, new UriParam("http://hl7.org/fhir/ValueSet/basic-resource-type").setQualifier(UriParamQualifierEnum.BELOW));
assertThat(toUnqualifiedVersionlessIds(result), contains(id1));
result = myValueSetDao.search(ValueSet.SP_URL, new UriParam("http://hl7.org/fhir/ValueSet/").setQualifier(UriParamQualifierEnum.BELOW));
assertThat(toUnqualifiedVersionlessIds(result), contains(id1));
}
@Test
public void testSearchWithUriParamAbove() throws Exception {
ValueSet vs1 = new ValueSet();
@ -2240,28 +2310,6 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
assertThat(toUnqualifiedVersionlessIds(result), empty());
}
@Test
public void testSearchWithUriParam() throws Exception {
Class<ValueSet> type = ValueSet.class;
String resourceName = "/valueset-dstu2.json";
ValueSet vs = loadResourceFromClasspath(type, resourceName);
IIdType id1 = myValueSetDao.update(vs, mySrd).getId().toUnqualifiedVersionless();
ValueSet vs2 = new ValueSet();
vs2.setUrl("http://hl7.org/foo/bar");
IIdType id2 = myValueSetDao.create(vs2, mySrd).getId().toUnqualifiedVersionless();
IBundleProvider result;
result = myValueSetDao.search(ValueSet.SP_URL, new UriParam("http://hl7.org/fhir/ValueSet/basic-resource-type"));
assertThat(toUnqualifiedVersionlessIds(result), contains(id1));
result = myValueSetDao.search(ValueSet.SP_URL, new UriParam("http://hl7.org/fhir/ValueSet/basic-resource-type").setQualifier(UriParamQualifierEnum.BELOW));
assertThat(toUnqualifiedVersionlessIds(result), contains(id1));
result = myValueSetDao.search(ValueSet.SP_URL, new UriParam("http://hl7.org/fhir/ValueSet/").setQualifier(UriParamQualifierEnum.BELOW));
assertThat(toUnqualifiedVersionlessIds(result), contains(id1));
}
@Test
public void testSearchWithUriParamBelow() throws Exception {
Class<ValueSet> type = ValueSet.class;
@ -2297,5 +2345,10 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
return b.toString();
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -118,6 +118,11 @@
class, as it was accidentally commented out. Thanks
to GitHub user @Virdulys for the pull request!
</action>
<action type="add">
JPA server now supports composite search parameters
where the type of the composite parameter is
a quantity (e.g. Observation:component-code-component-value-quantity)
</action>
</release>
<release version="1.5" date="2016-04-20">
<action type="fix" issue="339">