Work on sort support in JPA

This commit is contained in:
jamesagnew 2015-03-09 08:01:13 -04:00
parent da1d49a108
commit 5fd987c497
5 changed files with 112 additions and 31 deletions

View File

@ -107,18 +107,20 @@ public class SortSpec {
* Sets the chained sort specification, or <code>null</code> if none. If multiple sort parameters are chained * Sets the chained sort specification, or <code>null</code> if none. If multiple sort parameters are chained
* (indicating a sub-sort), the second level sort is chained via this property. * (indicating a sub-sort), the second level sort is chained via this property.
*/ */
public void setChain(SortSpec theChain) { public SortSpec setChain(SortSpec theChain) {
if (theChain == this) { if (theChain == this) {
throw new IllegalArgumentException("Can not chain this to itself"); throw new IllegalArgumentException("Can not chain this to itself");
} }
myChain = theChain; myChain = theChain;
return this;
} }
/** /**
* Sets the actual name of the search param to sort by * Sets the actual name of the search param to sort by
*/ */
public void setParamName(String theFieldName) { public SortSpec setParamName(String theFieldName) {
myParamName = theFieldName; myParamName = theFieldName;
return this;
} }
/** /**
@ -126,8 +128,9 @@ public class SortSpec {
* means {@link SortOrderEnum#ASC} according to the <a * means {@link SortOrderEnum#ASC} according to the <a
* href="http://hl7.org/implement/standards/fhir/search.html#sort">FHIR specification</a>) * href="http://hl7.org/implement/standards/fhir/search.html#sort">FHIR specification</a>)
*/ */
public void setOrder(SortOrderEnum theOrder) { public SortSpec setOrder(SortOrderEnum theOrder) {
myOrder = theOrder; myOrder = theOrder;
return this;
} }
} }

View File

@ -51,6 +51,7 @@ import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.IBaseResource; import org.hl7.fhir.instance.model.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -96,6 +97,7 @@ import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.param.CompositeParam; import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateParam;
@ -871,21 +873,34 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
throw new InvalidRequestException("Unknown sort parameter '" + theSort.getParamName() + "'"); throw new InvalidRequestException("Unknown sort parameter '" + theSort.getParamName() + "'");
} }
String joinAttrName = "myParamsString"; String joinAttrName;
String sortAttrName = "myValueExact"; String sortAttrName;
switch (param.getParamType()) { switch (param.getParamType()) {
case STRING: { case STRING:
From<?, ?> stringJoin = theFrom.join(joinAttrName, JoinType.LEFT); joinAttrName = "myParamsString";
Predicate p = theBuilder.equal(stringJoin.get("myParamName"), theSort.getParamName()); sortAttrName = "myValueExact";
Predicate pn = theBuilder.isNull(stringJoin.get("myParamName"));
thePredicates.add(theBuilder.or(p, pn));
theOrders.add(theBuilder.asc(stringJoin.get(sortAttrName)));
break; break;
case DATE:
joinAttrName = "myParamsDate";
sortAttrName = "myValueLow";
break;
default:
throw new NotImplementedException("This server does not support _sort specifications of type " + param.getParamType() + " - Can't serve _sort=" + theSort.getParamName());
} }
From<?, ?> stringJoin = theFrom.join(joinAttrName, JoinType.LEFT);
// Predicate p = theBuilder.equal(stringJoin.get("myParamName"), theSort.getParamName());
// Predicate pn = theBuilder.isNull(stringJoin.get("myParamName"));
// thePredicates.add(theBuilder.or(p, pn));
if (theSort.getOrder() == null || theSort.getOrder() == SortOrderEnum.ASC) {
theOrders.add(theBuilder.asc(stringJoin.get(sortAttrName)));
}else {
theOrders.add(theBuilder.desc(stringJoin.get(sortAttrName)));
} }
createSort(theBuilder, theFrom, theSort.getChain(), theOrders, thePredicates); createSort(theBuilder, theFrom, theSort.getChain(), theOrders, null);
} }
@Override @Override

View File

@ -28,9 +28,13 @@ import javax.persistence.Table;
import javax.persistence.Temporal; import javax.persistence.Temporal;
import javax.persistence.TemporalType; import javax.persistence.TemporalType;
//@formatter:off
@Entity @Entity
@Table(name = "HFJ_SPIDX_DATE" /*, indexes= {@Index(name="IDX_SP_DATE", columnList= "SP_VALUE_LOW,SP_VALUE_HIGH")}*/) @Table(name = "HFJ_SPIDX_DATE" /*, indexes= {@Index(name="IDX_SP_DATE", columnList= "SP_VALUE_LOW,SP_VALUE_HIGH")}*/)
@org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_DATE", indexes= {@org.hibernate.annotations.Index(name="IDX_SP_DATE", columnNames= {"RES_TYPE", "SP_NAME", "SP_VALUE_LOW","SP_VALUE_HIGH"})}) @org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_DATE", indexes= {
@org.hibernate.annotations.Index(name="IDX_SP_DATE", columnNames= {"RES_TYPE", "SP_NAME", "SP_VALUE_LOW","SP_VALUE_HIGH"})
})
//@formatter:on
public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchParam { public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchParam {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@ -1,11 +1,6 @@
package ca.uhn.fhir.jpa.dao; package ca.uhn.fhir.jpa.dao;
import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotEquals;
@ -60,6 +55,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.param.CompositeParam; import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateParam;
@ -303,11 +299,11 @@ public class FhirResourceDaoTest {
{ {
Set<Long> found = ourObservationDao.searchForIds(Observation.SP_DATE, new DateParam(">2001-01-02")); Set<Long> found = ourObservationDao.searchForIds(Observation.SP_DATE, new DateParam(">2001-01-02"));
assertThat(found, contains(id2.getIdPartAsLong())); assertThat(found, hasItem(id2.getIdPartAsLong()));
} }
{ {
Set<Long> found = ourObservationDao.searchForIds(Observation.SP_DATE, new DateParam(">2016-01-02")); Set<Long> found = ourObservationDao.searchForIds(Observation.SP_DATE, new DateParam(">2016-01-02"));
assertThat(found, not(contains(id2.getIdPartAsLong()))); assertThat(found, not(hasItem(id2.getIdPartAsLong())));
} }
} }
@ -722,14 +718,23 @@ public class FhirResourceDaoTest {
@Test @Test
public void testPersistSearchParamDate() { public void testPersistSearchParamDate() {
List<Patient> found = toList(ourPatientDao.search(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01")));
int initialSize2000 = found.size();
found = toList(ourPatientDao.search(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2002-01-01")));
int initialSize2002 = found.size();
Patient patient = new Patient(); Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001"); patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.setBirthDate(new DateDt("2001-01-01")); patient.setBirthDate(new DateDt("2001-01-01"));
ourPatientDao.create(patient); ourPatientDao.create(patient);
List<Patient> found = toList(ourPatientDao.search(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01"))); found = toList(ourPatientDao.search(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01")));
assertEquals(1, found.size()); assertEquals(1 + initialSize2000, found.size());
found = toList(ourPatientDao.search(Patient.SP_BIRTHDATE, new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2002-01-01")));
assertEquals(initialSize2002, found.size());
// If this throws an exception, that would be an acceptable outcome as well.. // If this throws an exception, that would be an acceptable outcome as well..
found = toList(ourPatientDao.search(Patient.SP_BIRTHDATE + "AAAA", new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01"))); found = toList(ourPatientDao.search(Patient.SP_BIRTHDATE + "AAAA", new DateParam(QuantityCompararatorEnum.GREATERTHAN, "2000-01-01")));
@ -1487,35 +1492,86 @@ public class FhirResourceDaoTest {
} }
@Test @Test
public void testSort() { public void testSortByString() {
Patient p = new Patient(); Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testSort001"); p.addIdentifier().setSystem("urn:system").setValue("testSortByString");
p.addName().addFamily("testSortF1").addGiven("testSortG1"); p.addName().addFamily("testSortF1").addGiven("testSortG1");
IdDt id1 = ourPatientDao.create(p).getId().toUnqualifiedVersionless(); IdDt id1 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
// Create out of order // Create out of order
p = new Patient(); p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testSort001"); p.addIdentifier().setSystem("urn:system").setValue("testSortByString");
p.addName().addFamily("testSortF3").addGiven("testSortG3"); p.addName().addFamily("testSortF3").addGiven("testSortG3");
IdDt id3 = ourPatientDao.create(p).getId().toUnqualifiedVersionless(); IdDt id3 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
p = new Patient(); p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testSort001"); p.addIdentifier().setSystem("urn:system").setValue("testSortByString");
p.addName().addFamily("testSortF2").addGiven("testSortG2"); p.addName().addFamily("testSortF2").addGiven("testSortG2");
IdDt id2 = ourPatientDao.create(p).getId().toUnqualifiedVersionless(); IdDt id2 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
p = new Patient(); p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testSort001"); p.addIdentifier().setSystem("urn:system").setValue("testSortByString");
IdDt id4 = ourPatientDao.create(p).getId().toUnqualifiedVersionless(); IdDt id4 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
SearchParameterMap pm = new SearchParameterMap(); SearchParameterMap pm = new SearchParameterMap();
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testSort001")); pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testSortByString"));
pm.setSort(new SortSpec(Patient.SP_FAMILY)); pm.setSort(new SortSpec(Patient.SP_FAMILY));
List<IdDt> actual = toUnqualifiedVersionlessIds(ourPatientDao.search(pm)); List<IdDt> actual = toUnqualifiedVersionlessIds(ourPatientDao.search(pm));
assertEquals(4, actual.size()); assertEquals(4, actual.size());
assertThat(actual, contains(id1, id2, id3, id4)); assertThat(actual, contains(id1, id2, id3, id4));
} }
@Test
public void testSortByDate() {
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testtestSortByDate");
p.addName().addFamily("testSortF1").addGiven("testSortG1");
p.setBirthDate(new DateDt("2001-01-01"));
IdDt id1 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
// Create out of order
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testtestSortByDate");
p.addName().addFamily("testSortF2").addGiven("testSortG2");
p.setBirthDate(new DateDt("2001-01-03"));
IdDt id3 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testtestSortByDate");
p.addName().addFamily("testSortF3").addGiven("testSortG3");
p.setBirthDate(new DateDt("2001-01-02"));
IdDt id2 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testtestSortByDate");
IdDt id4 = ourPatientDao.create(p).getId().toUnqualifiedVersionless();
List<IdDt> actual;
SearchParameterMap pm;
pm = new SearchParameterMap();
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testtestSortByDate"));
pm.setSort(new SortSpec(Patient.SP_BIRTHDATE));
actual = toUnqualifiedVersionlessIds(ourPatientDao.search(pm));
assertEquals(4, actual.size());
assertThat(actual, contains(id1, id2, id3, id4));
pm = new SearchParameterMap();
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testtestSortByDate"));
pm.setSort(new SortSpec(Patient.SP_BIRTHDATE).setOrder(SortOrderEnum.ASC));
actual = toUnqualifiedVersionlessIds(ourPatientDao.search(pm));
assertEquals(4, actual.size());
assertThat(actual, contains(id1, id2, id3, id4));
pm = new SearchParameterMap();
pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testtestSortByDate"));
pm.setSort(new SortSpec(Patient.SP_BIRTHDATE).setOrder(SortOrderEnum.DESC));
actual = toUnqualifiedVersionlessIds(ourPatientDao.search(pm));
assertEquals(4, actual.size());
assertThat(actual, contains(id4, id3, id2, id1));
}
@Test @Test
public void testStoreUnversionedResources() { public void testStoreUnversionedResources() {
Organization o1 = new Organization(); Organization o1 = new Organization();

View File

@ -110,10 +110,13 @@
this.name = this.id; this.name = this.id;
} }
} }
}); });
$("#outerForm").attr("action", "search").submit(); $("#outerForm").attr("action", "search").submit();
}); });
$('#search-btn').prop('disabled', false); $(document).ready(function() {
// If the user clicks "back", re-activate the button
$('#search-btn').prop('disabled', false);
});
</script> </script>
<br clear="all"/> <br clear="all"/>