diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderObservationDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderObservationDstu3.java index 4a1e3189a67..bbbff175c47 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderObservationDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderObservationDstu3.java @@ -60,6 +60,10 @@ public class BaseJpaResourceProviderObservationDstu3 extends JpaResourceProvider @OperationParam(name="code") TokenAndListParam theCode, + @Description(shortDefinition="The effective date of the observation") + @OperationParam(name="date") + DateAndListParam theDate, + @Description(shortDefinition="The subject that the observation is about (if patient)") @OperationParam(name="patient") ReferenceAndListParam thePatient, @@ -78,11 +82,12 @@ public class BaseJpaResourceProviderObservationDstu3 extends JpaResourceProvider SearchParameterMap paramMap = new SearchParameterMap(); paramMap.add(Observation.SP_CATEGORY, theCategory); paramMap.add(Observation.SP_CODE, theCode); + paramMap.add(Observation.SP_DATE, theDate); if (thePatient != null) { - paramMap.add("patient", thePatient); + paramMap.add(Observation.SP_PATIENT, thePatient); } if (theSubject != null) { - paramMap.add("subject", theSubject); + paramMap.add(Observation.SP_SUBJECT, theSubject); } paramMap.setLastNMax(theMax.getValue()); if (theCount != null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderObservationR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderObservationR4.java index 2ad20aa1974..1d9874f8803 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderObservationR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderObservationR4.java @@ -59,6 +59,10 @@ public class BaseJpaResourceProviderObservationR4 extends JpaResourceProviderR4< @OperationParam(name="code") TokenAndListParam theCode, + @Description(shortDefinition="The effective date of the observation") + @OperationParam(name="date") + DateAndListParam theDate, + @Description(shortDefinition="The subject that the observation is about (if patient)") @OperationParam(name="patient") ReferenceAndListParam thePatient, @@ -77,6 +81,7 @@ public class BaseJpaResourceProviderObservationR4 extends JpaResourceProviderR4< SearchParameterMap paramMap = new SearchParameterMap(); paramMap.add(Observation.SP_CATEGORY, theCategory); paramMap.add(Observation.SP_CODE, theCode); + paramMap.add(Observation.SP_DATE, theDate); if (thePatient != null) { paramMap.add(Observation.SP_PATIENT, thePatient); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderObservationR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderObservationR5.java index b5cacc7c571..530392b15f2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderObservationR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderObservationR5.java @@ -60,6 +60,10 @@ public class BaseJpaResourceProviderObservationR5 extends JpaResourceProviderR5< @OperationParam(name="code") TokenAndListParam theCode, + @Description(shortDefinition="The effective date of the observation") + @OperationParam(name="date") + DateAndListParam theDate, + @Description(shortDefinition="The subject that the observation is about (if patient)") @OperationParam(name="patient") ReferenceAndListParam thePatient, @@ -78,6 +82,7 @@ public class BaseJpaResourceProviderObservationR5 extends JpaResourceProviderR5< SearchParameterMap paramMap = new SearchParameterMap(); paramMap.add(Observation.SP_CATEGORY, theCategory); paramMap.add(Observation.SP_CODE, theCode); + paramMap.add(Observation.SP_DATE, theDate); if (thePatient != null) { paramMap.add(Observation.SP_PATIENT, thePatient); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java index c9118efd88f..ccc1bf75454 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java @@ -7,6 +7,8 @@ import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.util.LastNParameterHelper; import ca.uhn.fhir.model.api.IQueryParameterType; +import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.ParamPrefixEnum; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; @@ -25,7 +27,9 @@ import org.shadehapi.elasticsearch.client.RequestOptions; import org.shadehapi.elasticsearch.client.RestHighLevelClient; import org.shadehapi.elasticsearch.common.xcontent.XContentType; import org.shadehapi.elasticsearch.index.query.BoolQueryBuilder; +import org.shadehapi.elasticsearch.index.query.MatchQueryBuilder; import org.shadehapi.elasticsearch.index.query.QueryBuilders; +import org.shadehapi.elasticsearch.index.query.RangeQueryBuilder; import org.shadehapi.elasticsearch.index.reindex.DeleteByQueryRequest; import org.shadehapi.elasticsearch.search.SearchHit; import org.shadehapi.elasticsearch.search.SearchHits; @@ -321,6 +325,7 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc { BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); addCategoriesCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); addObservationCodeCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); + addDateCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); searchSourceBuilder.query(boolQueryBuilder); } searchSourceBuilder.size(0); @@ -341,6 +346,7 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc { boolQueryBuilder.must(QueryBuilders.termQuery("subject", theSubjectParam)); addCategoriesCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); addObservationCodeCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); + addDateCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); searchSourceBuilder.query(boolQueryBuilder); searchSourceBuilder.size(0); @@ -492,6 +498,41 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc { } + private void addDateCriteria(BoolQueryBuilder theBoolQueryBuilder, SearchParameterMap theSearchParameterMap, FhirContext theFhirContext) { + String dateParamName = LastNParameterHelper.getEffectiveParamName(theFhirContext); + if (theSearchParameterMap.containsKey(dateParamName)) { + List> andOrParams = theSearchParameterMap.get(dateParamName); + for (List nextAnd : andOrParams) { + BoolQueryBuilder myDateBoolQueryBuilder = new BoolQueryBuilder(); + for (IQueryParameterType nextOr : nextAnd) { + if (nextOr instanceof DateParam) { + DateParam myDate = (DateParam) nextOr; + createDateCriteria(myDate, myDateBoolQueryBuilder); + } + } + theBoolQueryBuilder.must(myDateBoolQueryBuilder); + } + } + } + + private void createDateCriteria(DateParam theDate, BoolQueryBuilder theBoolQueryBuilder) { + Long dateInstant = theDate.getValue().getTime(); + RangeQueryBuilder myRangeQueryBuilder = new RangeQueryBuilder("effectivedtm"); + + ParamPrefixEnum prefix = theDate.getPrefix(); + if (prefix == ParamPrefixEnum.GREATERTHAN || prefix == ParamPrefixEnum.STARTS_AFTER) { + theBoolQueryBuilder.should(myRangeQueryBuilder.gt(dateInstant)); + } else if (prefix == ParamPrefixEnum.LESSTHAN || prefix == ParamPrefixEnum.ENDS_BEFORE) { + theBoolQueryBuilder.should(myRangeQueryBuilder.lt(dateInstant)); + } else if (prefix == ParamPrefixEnum.LESSTHAN_OR_EQUALS) { + theBoolQueryBuilder.should(myRangeQueryBuilder.lte(dateInstant)); + } else if (prefix == ParamPrefixEnum.GREATERTHAN_OR_EQUALS) { + theBoolQueryBuilder.should(myRangeQueryBuilder.gte(dateInstant)); + } else { + theBoolQueryBuilder.should(new MatchQueryBuilder("effectivedtm", dateInstant)); + } + } + @VisibleForTesting List executeLastNWithAllFields(SearchParameterMap theSearchParameterMap, FhirContext theFhirContext) { return buildAndExecuteSearch(theSearchParameterMap, theFhirContext, null, t -> t, 100); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseR4SearchLastN.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseR4SearchLastN.java index bcfde6bac4f..048617cab80 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseR4SearchLastN.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseR4SearchLastN.java @@ -8,6 +8,10 @@ import ca.uhn.fhir.jpa.config.TestR4ConfigWithElasticsearchClient; import ca.uhn.fhir.jpa.dao.BaseJpaTest; import ca.uhn.fhir.jpa.dao.SearchBuilder; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.param.DateAndListParam; +import ca.uhn.fhir.rest.param.DateOrListParam; +import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.ParamPrefixEnum; import ca.uhn.fhir.rest.param.ReferenceAndListParam; import ca.uhn.fhir.rest.param.ReferenceOrListParam; import ca.uhn.fhir.rest.param.ReferenceParam; @@ -105,6 +109,8 @@ public class BaseR4SearchLastN extends BaseJpaTest { private static final Map observationCodeMap = new HashMap<>(); private static final Map observationEffectiveMap = new HashMap<>(); + private static Calendar observationDate = new GregorianCalendar(); + @Before public void beforeCreateTestPatientsAndObservations() { // Using a static flag to ensure that test data and elasticsearch index is only created once. @@ -141,15 +147,13 @@ public class BaseR4SearchLastN extends BaseJpaTest { private void createFiveObservationsForPatientCodeCategory(IIdType thePatientId, String theObservationCode, String theCategoryCode, Integer theTimeOffset) { - Calendar observationDate = new GregorianCalendar(); for (int idx=0; idx<5; idx++ ) { Observation obs = new Observation(); obs.getSubject().setReferenceElement(thePatientId); obs.getCode().addCoding().setCode(theObservationCode).setSystem(codeSystem); obs.setValue(new StringType(theObservationCode + "_0")); - observationDate.add(Calendar.HOUR, -theTimeOffset+idx); - Date effectiveDtm = observationDate.getTime(); + Date effectiveDtm = new Date(observationDate.getTimeInMillis() - (3600*1000*(theTimeOffset+idx))); obs.setEffective(new DateTimeType(effectiveDtm)); obs.getCategoryFirstRep().addCoding().setCode(theCategoryCode).setSystem(categorySystem); String observationId = myObservationDao.create(obs, mockSrd()).getId().toUnqualifiedVersionless().getValue(); @@ -535,6 +539,53 @@ public class BaseR4SearchLastN extends BaseJpaTest { return new TokenAndListParam().addAnd(myTokenOrListParam); } + @Test + public void testLastNSingleDate() { + + SearchParameterMap params = new SearchParameterMap(); + ReferenceParam subjectParam = new ReferenceParam("Patient", "", patient0Id.getValue()); + params.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); + + DateParam myDateParam = new DateParam(ParamPrefixEnum.LESSTHAN, new Date(observationDate.getTimeInMillis() - (3600*1000*9))); + params.add(Observation.SP_DATE, myDateParam); + + List sortedPatients = new ArrayList<>(); + sortedPatients.add(patient0Id.getValue()); + + List sortedObservationCodes = new ArrayList<>(); + sortedObservationCodes.add(observationCd0); + sortedObservationCodes.add(observationCd1); + + executeTestCase(params, sortedPatients,sortedObservationCodes, null,15); + + } + + @Test + public void testLastNMultipleDates() { + + SearchParameterMap params = new SearchParameterMap(); + ReferenceParam subjectParam = new ReferenceParam("Patient", "", patient0Id.getValue()); + params.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); + + DateParam lowDateParam = new DateParam(ParamPrefixEnum.LESSTHAN, new Date(observationDate.getTimeInMillis() - (3600*1000*(9)))); + DateParam highDateParam = new DateParam(ParamPrefixEnum.GREATERTHAN, new Date(observationDate.getTimeInMillis() - (3600*1000*(15)))); + DateAndListParam myDateAndListParam = new DateAndListParam(); + myDateAndListParam.addAnd(new DateOrListParam().addOr(lowDateParam)); + myDateAndListParam.addAnd(new DateOrListParam().addOr(highDateParam)); + + params.add(Observation.SP_DATE, myDateAndListParam); + + List sortedPatients = new ArrayList<>(); + sortedPatients.add(patient0Id.getValue()); + + List sortedObservationCodes = new ArrayList<>(); + sortedObservationCodes.add(observationCd0); + sortedObservationCodes.add(observationCd1); + + executeTestCase(params, sortedPatients,sortedObservationCodes, null,10); + + } + @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java index e128026f737..547c0eb96f0 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java @@ -33,10 +33,13 @@ public class LastNElasticsearchSvcMultipleObservationsIT { private static ObjectMapper ourMapperNonPrettyPrint; + private static boolean indexLoaded = false; + private final Map>> createdPatientObservationMap = new HashMap<>(); private FhirContext myFhirContext = FhirContext.forR4(); + static private Calendar baseObservationDate = new GregorianCalendar(); @BeforeClass public static void beforeClass() { @@ -48,13 +51,10 @@ public class LastNElasticsearchSvcMultipleObservationsIT { @Before public void before() throws IOException { - createMultiplePatientsAndObservations(); - } - - @After - public void after() throws IOException { - elasticsearchSvc.deleteAllDocuments(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.deleteAllDocuments(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); + if (!indexLoaded) { + createMultiplePatientsAndObservations(); + indexLoaded = true; + } } @Test @@ -248,27 +248,41 @@ public class LastNElasticsearchSvcMultipleObservationsIT { @Test public void testLastNNoMatchQueries() { - // Invalid Patient + + ReferenceParam validPatientParam = new ReferenceParam("Patient", "", "9"); + TokenParam validCategoryCodeParam = new TokenParam("http://mycodes.org/fhir/observation-category","test-heart-rate"); + TokenParam validObservationCodeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-1"); + DateParam validDateParam = new DateParam(ParamPrefixEnum.EQUAL, new Date(baseObservationDate.getTimeInMillis() - (9*3600*1000))); + + // Ensure that valid parameters are indeed valid SearchParameterMap searchParameterMap = new SearchParameterMap(); - ReferenceParam patientParam = new ReferenceParam("Patient", "", "10"); - searchParameterMap.add(Observation.SP_PATIENT, buildReferenceAndListParam(patientParam)); - TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/observation-category", "test-heart-rate"); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - TokenParam codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-1"); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); + searchParameterMap.add(Observation.SP_PATIENT, buildReferenceAndListParam(validPatientParam)); + searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); + searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); + searchParameterMap.add(Observation.SP_DATE, validDateParam); searchParameterMap.setLastNMax(100); List observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); + assertEquals(1, observations.size()); + + // Invalid Patient + searchParameterMap = new SearchParameterMap(); + ReferenceParam patientParam = new ReferenceParam("Patient", "", "10"); + searchParameterMap.add(Observation.SP_PATIENT, buildReferenceAndListParam(patientParam)); + searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); + searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); + searchParameterMap.add(Observation.SP_DATE, validDateParam); + searchParameterMap.setLastNMax(100); + + observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); assertEquals(0, observations.size()); // Invalid subject searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", "10"); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - categoryParam = new TokenParam("http://mycodes.org/fhir/observation-category", "test-heart-rate"); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-1"); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); + searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(patientParam)); + searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); + searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); + searchParameterMap.add(Observation.SP_DATE, validDateParam); searchParameterMap.setLastNMax(100); observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); @@ -276,12 +290,11 @@ public class LastNElasticsearchSvcMultipleObservationsIT { // Invalid observation code searchParameterMap = new SearchParameterMap(); - subjectParam = new ReferenceParam("Patient", "", "9"); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - categoryParam = new TokenParam("http://mycodes.org/fhir/observation-category", "test-heart-rate"); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-999"); + searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(validPatientParam)); + searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); + TokenParam codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-999"); searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); + searchParameterMap.add(Observation.SP_DATE, validDateParam); searchParameterMap.setLastNMax(100); observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); @@ -289,17 +302,112 @@ public class LastNElasticsearchSvcMultipleObservationsIT { // Invalid category code searchParameterMap = new SearchParameterMap(); - subjectParam = new ReferenceParam("Patient", "", "9"); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - categoryParam = new TokenParam("http://mycodes.org/fhir/observation-category", "test-not-a-category"); + searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(validPatientParam)); + TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/observation-category", "test-not-a-category"); searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-1"); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); + searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); + searchParameterMap.add(Observation.SP_DATE, validDateParam); searchParameterMap.setLastNMax(100); observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); assertEquals(0, observations.size()); + // Invalid date + searchParameterMap = new SearchParameterMap(); + searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(validPatientParam)); + searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); + searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); + searchParameterMap.add(Observation.SP_DATE, new DateParam(ParamPrefixEnum.GREATERTHAN, baseObservationDate.getTime())); + searchParameterMap.setLastNMax(100); + observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); + assertEquals(0, observations.size()); + + } + + @Test + public void testLastNEffectiveDates() { + Date highDate = new Date(baseObservationDate.getTimeInMillis() - (3600*1000)); + Date lowDate = new Date(baseObservationDate.getTimeInMillis() - (10*3600*1000)); + + SearchParameterMap searchParameterMap = new SearchParameterMap(); + ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3"); + searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); + DateParam dateParam = new DateParam(ParamPrefixEnum.EQUAL, lowDate); + searchParameterMap.add(Observation.SP_DATE, dateParam); + searchParameterMap.setLastNMax(100); + List observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); + assertEquals(1, observations.size()); + + searchParameterMap = new SearchParameterMap(); + searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); + dateParam = new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, lowDate); + searchParameterMap.add(Observation.SP_DATE, dateParam); + searchParameterMap.setLastNMax(100); + observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); + assertEquals(10, observations.size()); + + searchParameterMap = new SearchParameterMap(); + searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); + dateParam = new DateParam(ParamPrefixEnum.GREATERTHAN, lowDate); + searchParameterMap.add(Observation.SP_DATE, dateParam); + searchParameterMap.setLastNMax(100); + observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); + assertEquals(9, observations.size()); + + searchParameterMap = new SearchParameterMap(); + searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); + dateParam = new DateParam(ParamPrefixEnum.STARTS_AFTER, lowDate); + searchParameterMap.add(Observation.SP_DATE, dateParam); + searchParameterMap.setLastNMax(100); + observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); + assertEquals(9, observations.size()); + + searchParameterMap = new SearchParameterMap(); + searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); + dateParam = new DateParam(ParamPrefixEnum.LESSTHAN_OR_EQUALS, highDate); + searchParameterMap.add(Observation.SP_DATE, dateParam); + searchParameterMap.setLastNMax(100); + observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); + assertEquals(10, observations.size()); + + searchParameterMap = new SearchParameterMap(); + searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); + dateParam = new DateParam(ParamPrefixEnum.LESSTHAN, highDate); + searchParameterMap.add(Observation.SP_DATE, dateParam); + searchParameterMap.setLastNMax(100); + observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); + assertEquals(9, observations.size()); + + searchParameterMap = new SearchParameterMap(); + searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); + dateParam = new DateParam(ParamPrefixEnum.ENDS_BEFORE, highDate); + searchParameterMap.add(Observation.SP_DATE, dateParam); + searchParameterMap.setLastNMax(100); + observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); + assertEquals(9, observations.size()); + + searchParameterMap = new SearchParameterMap(); + searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); + DateParam startDateParam = new DateParam(ParamPrefixEnum.GREATERTHAN, new Date(baseObservationDate.getTimeInMillis() - (4*3600*1000))); + DateAndListParam dateAndListParam = new DateAndListParam(); + dateAndListParam.addAnd(new DateOrListParam().addOr(startDateParam)); + dateParam = new DateParam(ParamPrefixEnum.LESSTHAN_OR_EQUALS, highDate); + dateAndListParam.addAnd(new DateOrListParam().addOr(dateParam)); + searchParameterMap.add(Observation.SP_DATE, dateAndListParam); + searchParameterMap.setLastNMax(100); + observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); + assertEquals(3, observations.size()); + + searchParameterMap = new SearchParameterMap(); + searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); + startDateParam = new DateParam(ParamPrefixEnum.GREATERTHAN, new Date(baseObservationDate.getTimeInMillis() - (4*3600*1000))); + searchParameterMap.add(Observation.SP_DATE, startDateParam); + dateParam = new DateParam(ParamPrefixEnum.LESSTHAN, lowDate); + searchParameterMap.add(Observation.SP_DATE, dateParam); + searchParameterMap.setLastNMax(100); + observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); + assertEquals(0, observations.size()); + } private void createMultiplePatientsAndObservations() throws IOException { @@ -359,9 +467,7 @@ public class LastNElasticsearchSvcMultipleObservationsIT { assertTrue(elasticsearchSvc.performIndex(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX, codeableConceptId2, codeJson2Document, ElasticsearchSvcImpl.CODE_DOCUMENT_TYPE)); } - Calendar observationDate = new GregorianCalendar(); - observationDate.add(Calendar.HOUR, -10 + entryCount); - Date effectiveDtm = observationDate.getTime(); + Date effectiveDtm = new Date(baseObservationDate.getTimeInMillis() - ((10-entryCount)*3600*1000)); observationJson.setEffectiveDtm(effectiveDtm); String observationDocument = ourMapperNonPrettyPrint.writeValueAsString(observationJson); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java index 1e2a6ecb8ed..072d171ad1d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java @@ -7,6 +7,8 @@ import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.model.dstu2.resource.Observation; +import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.ParamPrefixEnum; import ca.uhn.fhir.rest.param.ReferenceAndListParam; import ca.uhn.fhir.rest.param.ReferenceOrListParam; import ca.uhn.fhir.rest.param.ReferenceParam; @@ -108,6 +110,7 @@ public class LastNElasticsearchSvcSingleObservationIT { searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam))); TokenParam codeParam = new TokenParam(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE); searchParameterMap.add(Observation.SP_CODE, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(codeParam))); + searchParameterMap.add(Observation.SP_DATE, new DateParam(ParamPrefixEnum.EQUAL, EFFECTIVEDTM)); searchParameterMap.setLastNMax(3); diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java index abc1a3850b4..b035a528f8f 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java @@ -38,17 +38,20 @@ public class LastNParameterHelper { private static boolean isLastNParameterDstu3(String theParamName) { return (theParamName.equals(org.hl7.fhir.dstu3.model.Observation.SP_SUBJECT) || theParamName.equals(org.hl7.fhir.dstu3.model.Observation.SP_PATIENT) - || theParamName.equals(org.hl7.fhir.dstu3.model.Observation.SP_CATEGORY) || theParamName.equals(org.hl7.fhir.r5.model.Observation.SP_CODE)); + || theParamName.equals(org.hl7.fhir.dstu3.model.Observation.SP_CATEGORY) || theParamName.equals(org.hl7.fhir.r5.model.Observation.SP_CODE)) + || theParamName.equals(org.hl7.fhir.dstu3.model.Observation.SP_DATE); } private static boolean isLastNParameterR4(String theParamName) { return (theParamName.equals(org.hl7.fhir.r4.model.Observation.SP_SUBJECT) || theParamName.equals(org.hl7.fhir.r4.model.Observation.SP_PATIENT) - || theParamName.equals(org.hl7.fhir.r4.model.Observation.SP_CATEGORY) || theParamName.equals(org.hl7.fhir.r4.model.Observation.SP_CODE)); + || theParamName.equals(org.hl7.fhir.r4.model.Observation.SP_CATEGORY) || theParamName.equals(org.hl7.fhir.r4.model.Observation.SP_CODE)) + || theParamName.equals(org.hl7.fhir.r4.model.Observation.SP_DATE); } private static boolean isLastNParameterR5(String theParamName) { return (theParamName.equals(org.hl7.fhir.r5.model.Observation.SP_SUBJECT) || theParamName.equals(org.hl7.fhir.r5.model.Observation.SP_PATIENT) - || theParamName.equals(org.hl7.fhir.r5.model.Observation.SP_CATEGORY) || theParamName.equals(org.hl7.fhir.r5.model.Observation.SP_CODE)); + || theParamName.equals(org.hl7.fhir.r5.model.Observation.SP_CATEGORY) || theParamName.equals(org.hl7.fhir.r5.model.Observation.SP_CODE)) + || theParamName.equals(org.hl7.fhir.r5.model.Observation.SP_DATE); } public static String getSubjectParamName(FhirContext theContext) {