Merge pull request #2240 from hapifhir/ft_support_composite_sort

Add support for composite sorting
This commit is contained in:
Frank Tao 2020-12-16 16:54:56 -05:00 committed by GitHub
commit effbd19924
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 715 additions and 102 deletions

View File

@ -444,7 +444,7 @@ public class SearchBuilder implements ISearchBuilder {
if (generatedSql.isMatchNothing()) {
return Optional.empty();
}
SearchQueryExecutor executor = mySqlBuilderFactory.newSearchQueryExecutor(generatedSql, myMaxResultsToFetch);
return Optional.of(executor);
}
@ -528,9 +528,25 @@ public class SearchBuilder implements ISearchBuilder {
break;
case QUANTITY:
theQueryStack.addSortOnQuantity(myResourceName, theSort.getParamName(), ascending);
break;
case COMPOSITE:
List<RuntimeSearchParam> compositList = param.getCompositeOf();
if (compositList == null) {
throw new InvalidRequestException("The composite _sort parameter " + theSort.getParamName() + " is not defined by the resource " + myResourceName);
}
if (compositList.size() != 2) {
throw new InvalidRequestException("The composite _sort parameter " + theSort.getParamName()
+ " must have 2 composite types declared in parameter annotation, found "
+ compositList.size());
}
RuntimeSearchParam left = compositList.get(0);
RuntimeSearchParam right = compositList.get(1);
createCompositeSort(theQueryStack, myResourceName, left.getParamType(), left.getName(), ascending);
createCompositeSort(theQueryStack, myResourceName, right.getParamType(), right.getName(), ascending);
break;
case SPECIAL:
case COMPOSITE:
case HAS:
default:
throw new InvalidRequestException("This server does not support _sort specifications of type " + param.getParamType() + " - Can't serve _sort=" + theSort.getParamName());
@ -542,6 +558,27 @@ public class SearchBuilder implements ISearchBuilder {
createSort(theQueryStack, theSort.getChain());
}
private void createCompositeSort(QueryStack theQueryStack, String theResourceName, RestSearchParameterTypeEnum theParamType, String theParamName, boolean theAscending) {
switch (theParamType) {
case STRING:
theQueryStack.addSortOnString(myResourceName, theParamName, theAscending);
break;
case DATE:
theQueryStack.addSortOnDate(myResourceName, theParamName, theAscending);
break;
case TOKEN:
theQueryStack.addSortOnToken(myResourceName, theParamName, theAscending);
break;
case QUANTITY:
theQueryStack.addSortOnQuantity(myResourceName, theParamName, theAscending);
break;
default:
throw new InvalidRequestException("Don't know how to handle composite parameter with type of " + theParamType + " on _sort="+ theParamName);
}
}
private void doLoadPids(Collection<ResourcePersistentId> thePids, Collection<ResourcePersistentId> theIncludedPids, List<IBaseResource> theResourceListToPopulate, boolean theForHistoryOperation,
Map<ResourcePersistentId, Integer> thePosition) {

View File

@ -2063,20 +2063,80 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
@Test()
public void testSortByComposite() {
Observation o = new Observation();
o.getCode().setText("testSortByComposite");
myObservationDao.create(o, mySrd);
IIdType pid0;
IIdType oid1;
IIdType oid2;
IIdType oid3;
IIdType oid4;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.addName().addFamily("Tester").addGiven("Joe");
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReference(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new StringDt("200"));
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReference(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new StringDt("300"));
oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReference(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new StringDt("150"));
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReference(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new StringDt("250"));
oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
SearchParameterMap pm = new SearchParameterMap();
pm.setSort(new SortSpec(Observation.SP_CODE_VALUE_CONCEPT));
try {
myObservationDao.search(pm).size();
fail();
} catch (InvalidRequestException e) {
assertEquals("This server does not support _sort specifications of type COMPOSITE - Can't serve _sort=code-value-concept", e.getMessage());
}
pm.setSort(new SortSpec(Observation.SP_CODE_VALUE_STRING));
IBundleProvider found = myObservationDao.search(pm);
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertEquals(4, list.size());
assertEquals(oid3, list.get(0));
assertEquals(oid1, list.get(1));
assertEquals(oid4, list.get(2));
assertEquals(oid2, list.get(3));
}
@Test
public void testSortByDate() {
Patient p = new Patient();

View File

@ -30,6 +30,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import com.google.common.collect.Lists;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
@ -2551,18 +2552,78 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
@Test()
public void testSortByComposite() {
Observation o = new Observation();
o.getCode().setText("testSortByComposite");
myObservationDao.create(o, mySrd);
IIdType pid0;
IIdType oid1;
IIdType oid2;
IIdType oid3;
IIdType oid4;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.addName().setFamily("Tester").addGiven("Joe");
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new StringType("200"));
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new StringType("300"));
oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new StringType("150"));
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new StringType("250"));
oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
SearchParameterMap pm = new SearchParameterMap();
pm.setSort(new SortSpec(Observation.SP_CODE_VALUE_CONCEPT));
try {
myObservationDao.search(pm).size();
fail();
} catch (InvalidRequestException e) {
assertEquals("This server does not support _sort specifications of type COMPOSITE - Can't serve _sort=code-value-concept", e.getMessage());
}
pm.setSort(new SortSpec(Observation.SP_CODE_VALUE_STRING));
IBundleProvider found = myObservationDao.search(pm);
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertEquals(4, list.size());
assertEquals(oid3, list.get(0));
assertEquals(oid1, list.get(1));
assertEquals(oid4, list.get(2));
assertEquals(oid2, list.get(3));
}
@Test

View File

@ -2979,21 +2979,82 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
assertTrue(next.getResource().getIdElement().hasIdPart());
}
}
@Test()
public void testSortByComposite() {
Observation o = new Observation();
o.getCode().setText("testSortByComposite");
myObservationDao.create(o, mySrd);
IIdType pid0;
IIdType oid1;
IIdType oid2;
IIdType oid3;
IIdType oid4;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.addName().setFamily("Tester").addGiven("Joe");
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new StringType("200"));
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new StringType("300"));
oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new StringType("150"));
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new StringType("250"));
oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
SearchParameterMap pm = new SearchParameterMap();
pm.setSort(new SortSpec(Observation.SP_CODE_VALUE_CONCEPT));
try {
myObservationDao.search(pm).size();
fail();
} catch (InvalidRequestException e) {
assertEquals("This server does not support _sort specifications of type COMPOSITE - Can't serve _sort=code-value-concept", e.getMessage());
}
pm.setSort(new SortSpec(Observation.SP_CODE_VALUE_STRING));
IBundleProvider found = myObservationDao.search(pm);
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertEquals(4, list.size());
assertEquals(oid3, list.get(0));
assertEquals(oid1, list.get(1));
assertEquals(oid4, list.get(2));
assertEquals(oid2, list.get(3));
}
@Test

View File

@ -35,12 +35,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.dstu2.resource.SearchParameter;
import ca.uhn.fhir.model.dstu2.valueset.XPathUsageTypeEnum;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.gclient.NumberClientParam;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@ -53,11 +50,9 @@ import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@ -74,6 +69,7 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.BaseResource;
import ca.uhn.fhir.model.dstu2.resource.Basic;
@ -131,7 +127,6 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.UrlUtil;
public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
@ -2185,23 +2180,86 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
}
@Test
public void testSearchWithInvalidSort() {
try {
Observation o = new Observation();
o.getCode().setText("testSearchWithInvalidSort");
myObservationDao.create(o, mySrd);
IBaseBundle bundle = ourClient
.search()
.forResource(Observation.class)
.sort().ascending(Observation.CODE_VALUE_QUANTITY) // composite sort not supported yet
.prettyPrint()
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: This server does not support _sort specifications of type COMPOSITE - Can't serve _sort=code-value-quantity", e.getMessage());
public void testSearchWithCompositeSortWith_CodeValueQuantity() throws IOException {
IIdType pid0;
IIdType oid1;
IIdType oid2;
IIdType oid3;
IIdType oid4;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.addName().addFamily("Tester").addGiven("Joe");
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReference(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new QuantityDt().setValue(200));
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReference(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new QuantityDt().setValue(300));
oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReference(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new QuantityDt().setValue(150));
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReference(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new QuantityDt().setValue(250));
oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
String uri = ourServerBase + "/Observation?_sort=code-value-quantity";
Bundle found;
HttpGet get = new HttpGet(uri);
try (CloseableHttpResponse resp = ourHttpClient.execute(get)) {
String output = IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8);
found = myFhirCtx.newXmlParser().parseResource(Bundle.class, output);
}
ourLog.info("Bundle: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(found));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertEquals(4, found.getEntry().size());
assertEquals(oid3, list.get(0));
assertEquals(oid1, list.get(1));
assertEquals(oid4, list.get(2));
assertEquals(oid2, list.get(3));
}
@Test
public void testSearchWithMissing() {
ourLog.info("Starting testSearchWithMissing");

View File

@ -3412,24 +3412,87 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
@Test
public void testSearchWithInvalidSort() {
try {
Observation o = new Observation();
o.getCode().setText("testSearchWithInvalidSort");
myObservationDao.create(o, mySrd);
ourClient
.search()
.forResource(Observation.class)
.sort().ascending(Observation.CODE_VALUE_QUANTITY) // composite sort not supported yet
.prettyPrint()
.returnBundle(Bundle.class)
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: This server does not support _sort specifications of type COMPOSITE - Can't serve _sort=code-value-quantity", e.getMessage());
public void testSearchWithCompositeSort_CodeValueDate() throws IOException {
IIdType pid0;
IIdType oid1;
IIdType oid2;
IIdType oid3;
IIdType oid4;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.addName().setFamily("Tester").addGiven("Joe");
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new DateTimeType("2020-02-01"));
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new DateTimeType("2020-04-01"));
oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new DateTimeType("2020-01-01"));
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new DateTimeType("2020-03-01"));
oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
String uri = ourServerBase + "/Observation?_sort=code-value-date";
Bundle found;
HttpGet get = new HttpGet(uri);
try (CloseableHttpResponse resp = ourHttpClient.execute(get)) {
String output = IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8);
found = myFhirCtx.newXmlParser().parseResource(Bundle.class, output);
}
ourLog.info("Bundle: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(found));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertEquals(4, found.getEntry().size());
assertEquals(oid3, list.get(0));
assertEquals(oid1, list.get(1));
assertEquals(oid4, list.get(2));
assertEquals(oid2, list.get(3));
}
@Test
public void testSearchWithMissing() {
ourLog.info("Starting testSearchWithMissing");

View File

@ -75,6 +75,7 @@ import org.hl7.fhir.r4.model.Bundle.SearchEntryMode;
import org.hl7.fhir.r4.model.CarePlan;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Condition;
import org.hl7.fhir.r4.model.DateTimeType;
@ -104,6 +105,7 @@ import org.hl7.fhir.r4.model.MolecularSequence;
import org.hl7.fhir.r4.model.Narrative;
import org.hl7.fhir.r4.model.Narrative.NarrativeStatus;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Observation.ObservationComponentComponent;
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.Organization;
@ -4546,24 +4548,183 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
}
@Test
public void testSearchWithInvalidSort() {
try {
Observation o = new Observation();
o.getCode().setText("testSearchWithInvalidSort");
myObservationDao.create(o, mySrd);
myClient
.search()
.forResource(Observation.class)
.sort().ascending(Observation.CODE_VALUE_QUANTITY) // composite sort not supported yet
.prettyPrint()
.returnBundle(Bundle.class)
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: This server does not support _sort specifications of type COMPOSITE - Can't serve _sort=code-value-quantity", e.getMessage());
public void testSearchWithCompositeSortWith_CodeValueQuantity() throws IOException {
IIdType pid0;
IIdType oid1;
IIdType oid2;
IIdType oid3;
IIdType oid4;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.addName().setFamily("Tester").addGiven("Joe");
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new Quantity().setValue(200));
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new Quantity().setValue(300));
oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new Quantity().setValue(150));
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
obs.getCode().addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new Quantity().setValue(250));
oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
String uri = ourServerBase + "/Observation?_sort=code-value-quantity";
Bundle found;
HttpGet get = new HttpGet(uri);
try (CloseableHttpResponse resp = ourHttpClient.execute(get)) {
String output = IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8);
found = myFhirCtx.newXmlParser().parseResource(Bundle.class, output);
}
ourLog.info("Bundle: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(found));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertEquals(4, found.getEntry().size());
assertEquals(oid3, list.get(0));
assertEquals(oid1, list.get(1));
assertEquals(oid4, list.get(2));
assertEquals(oid2, list.get(3));
}
@Test
public void testSearchWithCompositeSortWith_CompCodeValueQuantity() throws IOException {
IIdType pid0;
IIdType oid1;
IIdType oid2;
IIdType oid3;
IIdType oid4;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.addName().setFamily("Tester").addGiven("Joe");
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
ObservationComponentComponent comp = obs.addComponent();
CodeableConcept cc = new CodeableConcept();
cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
comp.setCode(cc);
comp.setValue(new Quantity().setValue(200));
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
ObservationComponentComponent comp = obs.addComponent();
CodeableConcept cc = new CodeableConcept();
cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
comp.setCode(cc);
comp.setValue(new Quantity().setValue(300));
oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
ObservationComponentComponent comp = obs.addComponent();
CodeableConcept cc = new CodeableConcept();
cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
comp.setCode(cc);
comp.setValue(new Quantity().setValue(150));
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
ObservationComponentComponent comp = obs.addComponent();
CodeableConcept cc = new CodeableConcept();
cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
comp.setCode(cc);
comp.setValue(new Quantity().setValue(250));
oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
String uri = ourServerBase + "/Observation?_sort=combo-code-value-quantity";
Bundle found;
HttpGet get = new HttpGet(uri);
try (CloseableHttpResponse resp = ourHttpClient.execute(get)) {
String output = IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8);
found = myFhirCtx.newXmlParser().parseResource(Bundle.class, output);
}
ourLog.info("Bundle: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(found));
List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertEquals(4, found.getEntry().size());
assertEquals(oid3, list.get(0));
assertEquals(oid1, list.get(1));
assertEquals(oid4, list.get(2));
assertEquals(oid2, list.get(3));
}
@Test
public void testSearchWithMissing() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);

View File

@ -1,13 +1,16 @@
package ca.uhn.fhir.jpa.provider.r5;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
import ca.uhn.fhir.util.UrlUtil;
import com.google.common.base.Charsets;
import static org.hamcrest.MatcherAssert.assertThat;
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.assertNotEquals;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
@ -16,24 +19,29 @@ import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.DateTimeType;
import org.hl7.fhir.r5.model.IdType;
import org.hl7.fhir.r5.model.Observation;
import org.hl7.fhir.r5.model.Observation.ObservationComponentComponent;
import org.hl7.fhir.r5.model.Patient;
import org.hl7.fhir.r5.model.Quantity;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import com.google.common.base.Charsets;
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.assertNotEquals;
import static org.hamcrest.MatcherAssert.assertThat;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
import ca.uhn.fhir.util.UrlUtil;
@SuppressWarnings("Duplicates")
public class ResourceProviderR5Test extends BaseResourceProviderR5Test {
@ -246,5 +254,109 @@ public class ResourceProviderR5Test extends BaseResourceProviderR5Test {
assertEquals(0, output.getEntry().size());
}
@Test
public void testSearchWithCompositeSort() throws IOException {
IIdType pid0;
IIdType oid1;
IIdType oid2;
IIdType oid3;
IIdType oid4;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.addName().setFamily("Tester").addGiven("Joe");
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
ObservationComponentComponent comp = obs.addComponent();
CodeableConcept cc = new CodeableConcept();
cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
comp.setCode(cc);
comp.setValue(new Quantity().setValue(200));
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
ObservationComponentComponent comp = obs.addComponent();
CodeableConcept cc = new CodeableConcept();
cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
comp.setCode(cc);
comp.setValue(new Quantity().setValue(300));
oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
ObservationComponentComponent comp = obs.addComponent();
CodeableConcept cc = new CodeableConcept();
cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
comp.setCode(cc);
comp.setValue(new Quantity().setValue(150));
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
{
Observation obs = new Observation();
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
obs.getSubject().setReferenceElement(pid0);
ObservationComponentComponent comp = obs.addComponent();
CodeableConcept cc = new CodeableConcept();
cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
comp.setCode(cc);
comp.setValue(new Quantity().setValue(250));
oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
String uri = ourServerBase + "/Observation?_sort=combo-code-value-quantity";
Bundle found;
HttpGet get = new HttpGet(uri);
try (CloseableHttpResponse resp = ourHttpClient.execute(get)) {
String output = IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8);
found = myFhirCtx.newXmlParser().parseResource(Bundle.class, output);
}
ourLog.info("Bundle: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(found));
List<IdType> list = toUnqualifiedVersionlessIds(found);
assertEquals(4, found.getEntry().size());
assertEquals(oid3, list.get(0));
assertEquals(oid1, list.get(1));
assertEquals(oid4, list.get(2));
assertEquals(oid2, list.get(3));
}
protected List<IdType> toUnqualifiedVersionlessIds(Bundle theFound) {
List<IdType> retVal = new ArrayList<>();
for (BundleEntryComponent next : theFound.getEntry()) {
if (next.getResource()!= null) {
retVal.add(next.getResource().getIdElement().toUnqualifiedVersionless());
}
}
return retVal;
}
}