Additional changes to enable processing of lastn operations with large numbers of parameters.

This commit is contained in:
ianmarshall 2020-05-05 16:53:47 -04:00
parent 3e49e5615f
commit 4db5beeabf
13 changed files with 367 additions and 257 deletions

View File

@ -38,7 +38,7 @@ public abstract class BaseHapiFhirResourceDaoObservation<T extends IBaseResource
if (theSearchParameterMap.getSort() == null) {
SortSpec effectiveDtm = new SortSpec("date").setOrder(SortOrderEnum.DESC);
SortSpec observationCode = new SortSpec(IndexConstants.CODE_SEARCH_PARAM).setOrder(SortOrderEnum.ASC).setChain(effectiveDtm);
theSearchParameterMap.setSort(new SortSpec(IndexConstants.SUBJECT_SEARCH_PARAM).setChain(observationCode));
theSearchParameterMap.setSort(new SortSpec(IndexConstants.SUBJECT_SEARCH_PARAM).setOrder(SortOrderEnum.ASC).setChain(observationCode));
}
}

View File

@ -351,16 +351,19 @@ public class SearchBuilder implements ISearchBuilder {
}
}
Integer myMaxObservationsPerCode = null;
String[] maxCountParams = theRequest.getParameters().get("max");
if (maxCountParams != null && maxCountParams.length > 0) {
myMaxObservationsPerCode = Integer.valueOf(maxCountParams[0]);
// String[] maxCountParams = theRequest.getParameters().get("max");
// if (maxCountParams != null && maxCountParams.length > 0) {
// myMaxObservationsPerCode = Integer.valueOf(maxCountParams[0]);
if(myParams.getLastNMax() != null) {
myMaxObservationsPerCode = myParams.getLastNMax();
} else {
throw new InvalidRequestException("Max parameter is required for $lastn operation");
}
List<String> lastnResourceIds = myIElasticsearchSvc.executeLastN(myParams, myMaxObservationsPerCode);
for (String lastnResourceId : lastnResourceIds) {
pids.add(myIdHelperService.resolveResourcePersistentIds(myRequestPartitionId, myResourceName, lastnResourceId));
}
// for (String lastnResourceId : lastnResourceIds) {
// pids.add(myIdHelperService.resolveResourcePersistentIds(myRequestPartitionId, myResourceName, lastnResourceId));
// }
pids = normalizeIdListForLastNInClause(lastnResourceIds);
}
if (pids.isEmpty()) {
// Will never match
@ -411,6 +414,50 @@ public class SearchBuilder implements ISearchBuilder {
return query;
}
private List<ResourcePersistentId> normalizeIdListForLastNInClause(List<String> lastnResourceIds) {
List<ResourcePersistentId> retVal = new ArrayList<>();
for (String lastnResourceId : lastnResourceIds) {
retVal.add(new ResourcePersistentId(Long.parseLong(lastnResourceId)));
}
/*
The following is a workaround to a known issue involving Hibernate. If queries are used with "in" clauses with large and varying
numbers of parameters, this can overwhelm Hibernate's QueryPlanCache and deplete heap space. See the following link for more info:
https://stackoverflow.com/questions/31557076/spring-hibernate-query-plan-cache-memory-usage.
Normalizing the number of parameters in the "in" clause stabilizes the size of the QueryPlanCache, so long as the number of
arguments never exceeds the maximum specified below.
*/
int listSize = retVal.size();
if(listSize > 1 && listSize < 10) {
padIdListWithPlaceholders(retVal, 10);
} else if (listSize > 10 && listSize < 100) {
padIdListWithPlaceholders(retVal, 100);
} else if (listSize > 100 && listSize < 200) {
padIdListWithPlaceholders(retVal, 200);
} else if (listSize > 200 && listSize < 500) {
padIdListWithPlaceholders(retVal, 500);
} else if (listSize > 500 && listSize < 1000) {
padIdListWithPlaceholders(retVal, 1000);
} else if (listSize > 1000 && listSize < 500) {
padIdListWithPlaceholders(retVal, 5000);
} else if (listSize > 5000 && listSize < 10000) {
padIdListWithPlaceholders(retVal, 10000);
} else if (listSize > 10000 && listSize < 20000) {
padIdListWithPlaceholders(retVal, 20000);
} else if (listSize > 20000 && listSize < 30000) {
padIdListWithPlaceholders(retVal, 30000);
}
return retVal;
}
private void padIdListWithPlaceholders(List<ResourcePersistentId> theIdList, int preferredListSize) {
while(theIdList.size() < preferredListSize) {
theIdList.add(new ResourcePersistentId(-1L));
}
}
/**
* @return Returns {@literal true} if any search parameter sorts were found, or false if
* no sorts were found, or only non-search parameters ones (e.g. _id, _lastUpdated)
@ -1190,7 +1237,7 @@ public class SearchBuilder implements ISearchBuilder {
mySearchRuntimeDetails.setQueryStopwatch(new StopWatch());
Query<Long> hibernateQuery = (Query<Long>) query;
Query<Long> hibernateQuery = (Query<Long>) query;
hibernateQuery.setFetchSize(myFetchSize);
ScrollableResults scroll = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY);
myResultsIterator = new ScrollableResultsIterator<>(scroll);

View File

@ -29,6 +29,8 @@ import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.ReferenceAndListParam;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.dstu3.model.Observation;
import org.springframework.beans.factory.annotation.Autowired;

View File

@ -29,6 +29,8 @@ import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.ReferenceAndListParam;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.Observation;
import org.springframework.beans.factory.annotation.Autowired;

View File

@ -3,19 +3,17 @@ package ca.uhn.fhir.jpa.provider.dstu3;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoObservation;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.*;
import org.hl7.fhir.dstu3.model.Observation;
import java.util.Set;
import org.hl7.fhir.dstu3.model.UnsignedIntType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
/*
* #%L
@ -50,17 +48,9 @@ public class BaseJpaResourceProviderObservationDstu3 extends JpaResourceProvider
ca.uhn.fhir.rest.api.server.RequestDetails theRequestDetails,
@Description(shortDefinition="Search the contents of the resource's data using a filter")
@OperationParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_FILTER)
StringAndListParam theFtFilter,
@Description(shortDefinition="Search the contents of the resource's data using a fulltext search")
@OperationParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT)
StringAndListParam theFtContent,
@Description(shortDefinition="Search the contents of the resource's narrative using a fulltext search")
@OperationParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_TEXT)
StringAndListParam theFtText,
@Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(shortDefinition="The classification of the type of observation")
@OperationParam(name="category")
@ -70,10 +60,6 @@ public class BaseJpaResourceProviderObservationDstu3 extends JpaResourceProvider
@OperationParam(name="code")
TokenAndListParam theCode,
@Description(shortDefinition="Obtained date/time. If the obtained element is a period, a date that falls in the period")
@OperationParam(name="date")
DateRangeParam theDate,
@Description(shortDefinition="The subject that the observation is about (if patient)")
@OperationParam(name="patient")
ReferenceAndListParam thePatient,
@ -82,69 +68,31 @@ public class BaseJpaResourceProviderObservationDstu3 extends JpaResourceProvider
@OperationParam(name="subject" )
ReferenceAndListParam theSubject,
@IncludeParam(reverse=true)
Set<Include> theRevIncludes,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name="_lastUpdated")
DateRangeParam theLastUpdated,
@IncludeParam(allow= {
"Observation:based-on",
"Observation:derived-from",
"Observation:device",
"Observation:encounter",
"Observation:focus",
"Observation:has-member",
"Observation:part-of",
"Observation:patient",
"Observation:performer",
"Observation:specimen",
"Observation:subject",
"*"
})
Set<Include> theIncludes,
@Description(shortDefinition="The maximum number of observations to return for each observation code")
@OperationParam(name = "max", typeName = "integer", min = 0, max = 1)
IPrimitiveType<Integer> theMax,
@Sort
SortSpec theSort,
@ca.uhn.fhir.rest.annotation.Count
Integer theCount,
SummaryEnum theSummaryMode,
SearchTotalModeEnum theSearchTotalMode
SortSpec theSort
) {
startRequest(theServletRequest);
try {
SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_FILTER, theFtFilter);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT, theFtContent);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_TEXT, theFtText);
paramMap.add("category", theCategory);
paramMap.add("code", theCode);
paramMap.add("date", theDate);
paramMap.add("patient", thePatient);
paramMap.add("subject", theSubject);
paramMap.setRevIncludes(theRevIncludes);
paramMap.setLastUpdated(theLastUpdated);
paramMap.setIncludes(theIncludes);
paramMap.setLastN(true);
if (theSort == null) {
SortSpec effectiveDtm = new SortSpec("date").setOrder(SortOrderEnum.DESC);
SortSpec observationCode = new SortSpec("code").setOrder(SortOrderEnum.ASC).setChain(effectiveDtm);
if (thePatient != null && theSubject == null) {
theSort = new SortSpec("patient").setChain(observationCode);
} else {
theSort = new SortSpec("subject").setChain(observationCode);
}
paramMap.add(Observation.SP_CATEGORY, theCategory);
paramMap.add(Observation.SP_CODE, theCode);
paramMap.add(Observation.SP_PATIENT, thePatient);
paramMap.add(Observation.SP_SUBJECT, theSubject);
paramMap.setLastNMax(theMax.getValue());
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
paramMap.setSort(theSort);
paramMap.setCount(theCount);
paramMap.setSummaryMode(theSummaryMode);
paramMap.setSearchTotalMode(theSearchTotalMode);
return ((IFhirResourceDaoObservation<org.hl7.fhir.dstu3.model.Observation>) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse);
if (theSort != null) {
paramMap.setSort(theSort);
}
return ((IFhirResourceDaoObservation<Observation>) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse);
} finally {
endRequest(theServletRequest);
}

View File

@ -3,20 +3,17 @@ package ca.uhn.fhir.jpa.provider.r4;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoObservation;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.*;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.*;
import java.util.Set;
/*
* #%L
* HAPI FHIR JPA Server
@ -50,17 +47,9 @@ public class BaseJpaResourceProviderObservationR4 extends JpaResourceProviderR4<
ca.uhn.fhir.rest.api.server.RequestDetails theRequestDetails,
@Description(shortDefinition="Search the contents of the resource's data using a filter")
@OperationParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_FILTER)
StringAndListParam theFtFilter,
@Description(shortDefinition="Search the contents of the resource's data using a fulltext search")
@OperationParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT)
StringAndListParam theFtContent,
@Description(shortDefinition="Search the contents of the resource's narrative using a fulltext search")
@OperationParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_TEXT)
StringAndListParam theFtText,
@Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(shortDefinition="The classification of the type of observation")
@OperationParam(name="category")
@ -70,10 +59,6 @@ public class BaseJpaResourceProviderObservationR4 extends JpaResourceProviderR4<
@OperationParam(name="code")
TokenAndListParam theCode,
@Description(shortDefinition="Obtained date/time. If the obtained element is a period, a date that falls in the period")
@OperationParam(name="date")
DateRangeParam theDate,
@Description(shortDefinition="The subject that the observation is about (if patient)")
@OperationParam(name="patient")
ReferenceAndListParam thePatient,
@ -82,67 +67,29 @@ public class BaseJpaResourceProviderObservationR4 extends JpaResourceProviderR4<
@OperationParam(name="subject" )
ReferenceAndListParam theSubject,
@IncludeParam(reverse=true)
Set<Include> theRevIncludes,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name="_lastUpdated")
DateRangeParam theLastUpdated,
@IncludeParam(allow= {
"Observation:based-on",
"Observation:derived-from",
"Observation:device",
"Observation:encounter",
"Observation:focus",
"Observation:has-member",
"Observation:part-of",
"Observation:patient",
"Observation:performer",
"Observation:specimen",
"Observation:subject",
"*"
})
Set<Include> theIncludes,
@Description(shortDefinition="The maximum number of observations to return for each observation code")
@OperationParam(name = "max", typeName = "integer", min = 0, max = 1)
IPrimitiveType<Integer> theMax,
@Sort
SortSpec theSort,
@ca.uhn.fhir.rest.annotation.Count
Integer theCount,
SummaryEnum theSummaryMode,
SearchTotalModeEnum theSearchTotalMode
SortSpec theSort
) {
startRequest(theServletRequest);
try {
SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_FILTER, theFtFilter);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT, theFtContent);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_TEXT, theFtText);
paramMap.add("category", theCategory);
paramMap.add("code", theCode);
paramMap.add("date", theDate);
paramMap.add("patient", thePatient);
paramMap.add("subject", theSubject);
paramMap.setRevIncludes(theRevIncludes);
paramMap.setLastUpdated(theLastUpdated);
paramMap.setIncludes(theIncludes);
/* paramMap.setLastN(true);
if (theSort == null) {
SortSpec effectiveDtm = new SortSpec("date").setOrder(SortOrderEnum.DESC);
SortSpec observationCode = new SortSpec("code").setOrder(SortOrderEnum.ASC).setChain(effectiveDtm);
if (thePatient != null && theSubject == null) {
theSort = new SortSpec("patient").setChain(observationCode);
} else {
theSort = new SortSpec("subject").setChain(observationCode);
}
} */
paramMap.setSort(theSort);
paramMap.setCount(theCount);
paramMap.setSummaryMode(theSummaryMode);
paramMap.setSearchTotalMode(theSearchTotalMode);
paramMap.setLastNMax(theMax.getValue());
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
if (theSort != null) {
paramMap.setSort(theSort);
}
return ((IFhirResourceDaoObservation<Observation>) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse);
} finally {

View File

@ -3,20 +3,18 @@ package ca.uhn.fhir.jpa.provider.r5;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoObservation;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.SearchTotalModeEnum;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SortOrderEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.*;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r5.model.UnsignedIntType;
import org.hl7.fhir.r5.model.Observation;
import java.util.Set;
/*
* #%L
* HAPI FHIR JPA Server
@ -50,17 +48,9 @@ public class BaseJpaResourceProviderObservationR5 extends JpaResourceProviderR5<
ca.uhn.fhir.rest.api.server.RequestDetails theRequestDetails,
@Description(shortDefinition="Search the contents of the resource's data using a filter")
@OperationParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_FILTER)
StringAndListParam theFtFilter,
@Description(shortDefinition="Search the contents of the resource's data using a fulltext search")
@OperationParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT)
StringAndListParam theFtContent,
@Description(shortDefinition="Search the contents of the resource's narrative using a fulltext search")
@OperationParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_TEXT)
StringAndListParam theFtText,
@Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT)
UnsignedIntType theCount,
@Description(shortDefinition="The classification of the type of observation")
@OperationParam(name="category")
@ -70,10 +60,6 @@ public class BaseJpaResourceProviderObservationR5 extends JpaResourceProviderR5<
@OperationParam(name="code")
TokenAndListParam theCode,
@Description(shortDefinition="Obtained date/time. If the obtained element is a period, a date that falls in the period")
@OperationParam(name="date")
DateRangeParam theDate,
@Description(shortDefinition="The subject that the observation is about (if patient)")
@OperationParam(name="patient")
ReferenceAndListParam thePatient,
@ -82,69 +68,31 @@ public class BaseJpaResourceProviderObservationR5 extends JpaResourceProviderR5<
@OperationParam(name="subject" )
ReferenceAndListParam theSubject,
@IncludeParam(reverse=true)
Set<Include> theRevIncludes,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OperationParam(name="_lastUpdated")
DateRangeParam theLastUpdated,
@IncludeParam(allow= {
"Observation:based-on",
"Observation:derived-from",
"Observation:device",
"Observation:encounter",
"Observation:focus",
"Observation:has-member",
"Observation:part-of",
"Observation:patient",
"Observation:performer",
"Observation:specimen",
"Observation:subject",
"*"
})
Set<Include> theIncludes,
@Description(shortDefinition="The maximum number of observations to return for each observation code")
@OperationParam(name = "max", typeName = "integer", min = 0, max = 1)
IPrimitiveType<Integer> theMax,
@Sort
SortSpec theSort,
@ca.uhn.fhir.rest.annotation.Count
Integer theCount,
SummaryEnum theSummaryMode,
SearchTotalModeEnum theSearchTotalMode
SortSpec theSort
) {
startRequest(theServletRequest);
try {
SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_FILTER, theFtFilter);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT, theFtContent);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_TEXT, theFtText);
paramMap.add("category", theCategory);
paramMap.add("code", theCode);
paramMap.add("date", theDate);
paramMap.add("patient", thePatient);
paramMap.add("subject", theSubject);
paramMap.setRevIncludes(theRevIncludes);
paramMap.setLastUpdated(theLastUpdated);
paramMap.setIncludes(theIncludes);
paramMap.setLastN(true);
if (theSort == null) {
SortSpec effectiveDtm = new SortSpec("date").setOrder(SortOrderEnum.DESC);
SortSpec observationCode = new SortSpec("code").setOrder(SortOrderEnum.ASC).setChain(effectiveDtm);
if (thePatient != null && theSubject == null) {
theSort = new SortSpec("patient").setChain(observationCode);
} else {
theSort = new SortSpec("subject").setChain(observationCode);
}
paramMap.setLastNMax(theMax.getValue());
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}
paramMap.setSort(theSort);
paramMap.setCount(theCount);
paramMap.setSummaryMode(theSummaryMode);
paramMap.setSearchTotalMode(theSearchTotalMode);
return ((IFhirResourceDaoObservation<org.hl7.fhir.r5.model.Observation>) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse);
if (theSort != null) {
paramMap.setSort(theSort);
}
return ((IFhirResourceDaoObservation<Observation>) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse);
} finally {
endRequest(theServletRequest);
}

View File

@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.search.lastn;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.jpa.search.lastn.json.CodeJson;
@ -42,6 +43,7 @@ import org.shadehapi.elasticsearch.search.sort.SortOrder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import static org.apache.commons.lang3.StringUtils.isBlank;
@ -200,21 +202,64 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
@Override
public List<String> executeLastN(SearchParameterMap theSearchParameterMap, Integer theMaxObservationsPerCode) {
String[] topHitsInclude = {OBSERVATION_IDENTIFIER_FIELD_NAME};
SearchRequest myLastNRequest = buildObservationsSearchRequest(theSearchParameterMap, createCompositeAggregationBuilder(theMaxObservationsPerCode, topHitsInclude));
try {
SearchResponse lastnResponse = executeSearchRequest(myLastNRequest);
return buildObservationIdList(lastnResponse);
List<SearchResponse> responses = buildAndExecuteSearch(theSearchParameterMap, theMaxObservationsPerCode, topHitsInclude);
List<String> observationIds = new ArrayList<>();
for (SearchResponse response : responses) {
// observationIds.addAll(buildObservationIdList(response));
observationIds.addAll(buildObservationList(response, t -> t.getIdentifier(), theSearchParameterMap));
}
return observationIds;
} catch (IOException theE) {
throw new InvalidRequestException("Unable to execute LastN request", theE);
}
}
private List<SearchResponse> buildAndExecuteSearch(SearchParameterMap theSearchParameterMap, Integer theMaxObservationsPerCode, String[] topHitsInclude) {
List<SearchResponse> responses = new ArrayList<>();
if (theSearchParameterMap.containsKey(IndexConstants.PATIENT_SEARCH_PARAM) || theSearchParameterMap.containsKey(IndexConstants.SUBJECT_SEARCH_PARAM)) {
ArrayList<String> subjectReferenceCriteria = new ArrayList<>();
List<List<IQueryParameterType>> patientParams = new ArrayList<>();
if (theSearchParameterMap.get(IndexConstants.PATIENT_SEARCH_PARAM) != null) {
patientParams.addAll(theSearchParameterMap.get(IndexConstants.PATIENT_SEARCH_PARAM));
}
if (theSearchParameterMap.get(IndexConstants.SUBJECT_SEARCH_PARAM) != null) {
patientParams.addAll(theSearchParameterMap.get(IndexConstants.SUBJECT_SEARCH_PARAM));
}
for (List<? extends IQueryParameterType> nextSubjectList : patientParams) {
subjectReferenceCriteria.addAll(getReferenceValues(nextSubjectList));
}
for (String subject : subjectReferenceCriteria) {
SearchRequest myLastNRequest = buildObservationsSearchRequest(subject, theSearchParameterMap, createCompositeAggregationBuilder(theMaxObservationsPerCode, topHitsInclude));
try {
SearchResponse lastnResponse = executeSearchRequest(myLastNRequest);
responses.add(lastnResponse);
} catch (IOException theE) {
throw new InvalidRequestException("Unable to execute LastN request", theE);
}
}
} else {
SearchRequest myLastNRequest = buildObservationsSearchRequest(theSearchParameterMap, createObservationCodeAggregationBuilder(theMaxObservationsPerCode, topHitsInclude));
try {
SearchResponse lastnResponse = executeSearchRequest(myLastNRequest);
responses.add(lastnResponse);
} catch (IOException theE) {
throw new InvalidRequestException("Unable to execute LastN request", theE);
}
}
return responses;
}
@VisibleForTesting
List<ObservationJson> executeLastNWithAllFields(SearchParameterMap theSearchParameterMap, Integer theMaxObservationsPerCode) {
SearchRequest myLastNRequest = buildObservationsSearchRequest(theSearchParameterMap, createCompositeAggregationBuilder(theMaxObservationsPerCode, null));
try {
SearchResponse lastnResponse = executeSearchRequest(myLastNRequest);
return buildObservationDocumentList(lastnResponse);
List<SearchResponse> responses = buildAndExecuteSearch(theSearchParameterMap, theMaxObservationsPerCode, null);
List<ObservationJson> observationDocuments = new ArrayList<>();
for (SearchResponse response : responses) {
observationDocuments.addAll(buildObservationList(response, t -> t, theSearchParameterMap));
}
return observationDocuments;
} catch (IOException theE) {
throw new InvalidRequestException("Unable to execute LastN request", theE);
}
@ -227,8 +272,7 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
return buildCodeResult(codeSearchResponse);
}
@VisibleForTesting
SearchRequest buildObservationCodesSearchRequest(int theMaxResultSetSize) {
private SearchRequest buildObservationCodesSearchRequest(int theMaxResultSetSize) {
SearchRequest searchRequest = new SearchRequest(IndexConstants.CODE_INDEX);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// Query
@ -239,27 +283,30 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
}
private CompositeAggregationBuilder createCompositeAggregationBuilder(int theMaxNumberObservationsPerCode, String[] theTopHitsInclude) {
CompositeValuesSourceBuilder<?> subjectValuesBuilder = new TermsValuesSourceBuilder("subject").field("subject");
List<CompositeValuesSourceBuilder<?>> compositeAggSubjectSources = new ArrayList();
compositeAggSubjectSources.add(subjectValuesBuilder);
CompositeAggregationBuilder compositeAggregationSubjectBuilder = new CompositeAggregationBuilder(GROUP_BY_SUBJECT, compositeAggSubjectSources);
compositeAggregationSubjectBuilder.subAggregation(createObservationCodeAggregationBuilder(theMaxNumberObservationsPerCode, theTopHitsInclude));
compositeAggregationSubjectBuilder.size(10000);
return compositeAggregationSubjectBuilder;
}
private TermsAggregationBuilder createObservationCodeAggregationBuilder(int theMaxNumberObservationsPerCode, String[] theTopHitsInclude) {
TermsAggregationBuilder observationCodeAggregationBuilder = new TermsAggregationBuilder(GROUP_BY_CODE, ValueType.STRING).field("codeconceptid");
// Top Hits Aggregation
observationCodeAggregationBuilder.subAggregation(AggregationBuilders.topHits("most_recent_effective")
.sort("effectivedtm", SortOrder.DESC)
.fetchSource(theTopHitsInclude, null).size(theMaxNumberObservationsPerCode));
observationCodeAggregationBuilder.size(10000);
CompositeValuesSourceBuilder<?> subjectValuesBuilder = new TermsValuesSourceBuilder("subject").field("subject");
List<CompositeValuesSourceBuilder<?>> compositeAggSubjectSources = new ArrayList();
compositeAggSubjectSources.add(subjectValuesBuilder);
CompositeAggregationBuilder compositeAggregationSubjectBuilder = new CompositeAggregationBuilder(GROUP_BY_SUBJECT, compositeAggSubjectSources);
compositeAggregationSubjectBuilder.subAggregation(observationCodeAggregationBuilder);
compositeAggregationSubjectBuilder.size(10000);
return compositeAggregationSubjectBuilder;
return observationCodeAggregationBuilder;
}
private SearchResponse executeSearchRequest(SearchRequest searchRequest) throws IOException {
public SearchResponse executeSearchRequest(SearchRequest searchRequest) throws IOException {
return myRestHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
}
private List<String> buildObservationIdList(SearchResponse theSearchResponse) throws IOException {
List<String> theObservationList = new ArrayList<>();
for (ParsedComposite.ParsedBucket subjectBucket : getSubjectBuckets(theSearchResponse)) {
@ -288,12 +335,43 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
return theObservationList;
}
private <T> List<T> buildObservationList(SearchResponse theSearchResponse, Function<ObservationJson,T> setValue, SearchParameterMap theSearchParameterMap) throws IOException {
List<T> theObservationList = new ArrayList<>();
if (theSearchParameterMap.containsKey(IndexConstants.PATIENT_SEARCH_PARAM) || theSearchParameterMap.containsKey(IndexConstants.SUBJECT_SEARCH_PARAM)) {
for (ParsedComposite.ParsedBucket subjectBucket : getSubjectBuckets(theSearchResponse)) {
for (Terms.Bucket observationCodeBucket : getObservationCodeBuckets(subjectBucket)) {
for (SearchHit lastNMatch : getLastNMatches(observationCodeBucket)) {
String indexedObservation = lastNMatch.getSourceAsString();
ObservationJson observationJson = objectMapper.readValue(indexedObservation, ObservationJson.class);
theObservationList.add(setValue.apply(observationJson));
}
}
}
} else {
for (Terms.Bucket observationCodeBucket : getObservationCodeBuckets(theSearchResponse)) {
for (SearchHit lastNMatch : getLastNMatches(observationCodeBucket)) {
String indexedObservation = lastNMatch.getSourceAsString();
ObservationJson observationJson = objectMapper.readValue(indexedObservation, ObservationJson.class);
theObservationList.add(setValue.apply(observationJson));
}
}
}
return theObservationList;
}
private List<ParsedComposite.ParsedBucket> getSubjectBuckets(SearchResponse theSearchResponse) {
Aggregations responseAggregations = theSearchResponse.getAggregations();
ParsedComposite aggregatedSubjects = responseAggregations.get(GROUP_BY_SUBJECT);
return aggregatedSubjects.getBuckets();
}
private List<? extends Terms.Bucket> getObservationCodeBuckets(SearchResponse theSearchResponse) {
Aggregations responseAggregations = theSearchResponse.getAggregations();
ParsedTerms aggregatedObservationCodes = responseAggregations.get(GROUP_BY_CODE);
return aggregatedObservationCodes.getBuckets();
}
private List<? extends Terms.Bucket> getObservationCodeBuckets(ParsedComposite.ParsedBucket theSubjectBucket) {
Aggregations observationCodeAggregations = theSubjectBucket.getAggregations();
ParsedTerms aggregatedObservationCodes = observationCodeAggregations.get(GROUP_BY_CODE);
@ -338,6 +416,24 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
return searchRequest;
}
private SearchRequest buildObservationsSearchRequest(String theSubjectParam, SearchParameterMap theSearchParameterMap, AggregationBuilder theAggregationBuilder) {
SearchRequest searchRequest = new SearchRequest(IndexConstants.OBSERVATION_INDEX);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// Query
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.termQuery("subject", theSubjectParam));
addCategoriesCriteria(boolQueryBuilder, theSearchParameterMap);
addObservationCodeCriteria(boolQueryBuilder, theSearchParameterMap);
searchSourceBuilder.query(boolQueryBuilder);
searchSourceBuilder.size(0);
// Aggregation by order codes
searchSourceBuilder.aggregation(theAggregationBuilder);
searchRequest.source(searchSourceBuilder);
return searchRequest;
}
private Boolean searchParamsHaveLastNCriteria(SearchParameterMap theSearchParameterMap) {
return theSearchParameterMap != null &&
(theSearchParameterMap.containsKey(IndexConstants.PATIENT_SEARCH_PARAM) || theSearchParameterMap.containsKey(IndexConstants.SUBJECT_SEARCH_PARAM) ||
@ -519,11 +615,12 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
myRestHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
}
public void deleteObservationIndex(String theObservationIdentifier) throws IOException {
/* public void deleteObservationIndex(String theObservationIdentifier) throws IOException {
DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(IndexConstants.OBSERVATION_DOCUMENT_TYPE);
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.termsQuery(OBSERVATION_IDENTIFIER_FIELD_NAME, theObservationIdentifier));
deleteByQueryRequest.setQuery(boolQueryBuilder);
myRestHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
}
*/
}

View File

@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.api.dao.*;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.config.TestR4ConfigWithElasticsearchClient;
import ca.uhn.fhir.jpa.dao.BaseJpaTest;
import ca.uhn.fhir.jpa.rp.r4.ObservationResourceProvider;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
@ -57,6 +58,8 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseJpaTest {
return myPlatformTransactionManager;
}
ObservationResourceProvider observationRp = new ObservationResourceProvider();
private final String observationCd0 = "code0";
private final String observationCd1 = "code1";
private final String observationCd2 = "code2";
@ -101,6 +104,8 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseJpaTest {
dataLoaded = true;
}
observationRp.setDao(myObservationDao);
}
private void createObservationsForPatient(IIdType thePatientId) {
@ -138,9 +143,13 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseJpaTest {
}
@Test
public void testLastNNoParams() {
public void testLastNAllPatients() {
SearchParameterMap params = new SearchParameterMap();
ReferenceParam subjectParam1 = new ReferenceParam("Patient", "", patient0Id.getValue());
ReferenceParam subjectParam2 = new ReferenceParam("Patient", "", patient1Id.getValue());
ReferenceParam subjectParam3 = new ReferenceParam("Patient", "", patient2Id.getValue());
params.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam1, subjectParam2, subjectParam3));
List<String> sortedPatients = new ArrayList<>();
sortedPatients.add(patient0Id.getValue());
@ -155,14 +164,39 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseJpaTest {
executeTestCase(params, sortedPatients, sortedObservationCodes, null,90);
}
@Test
public void testLastNNoPatients() {
SearchParameterMap params = new SearchParameterMap();
params.setLastNMax(1);
List<String> sortedPatients = new ArrayList<>();
List<String> sortedObservationCodes = new ArrayList<>();
sortedObservationCodes.add(observationCd0);
sortedObservationCodes.add(observationCd1);
sortedObservationCodes.add(observationCd2);
// executeTestCase(params, sortedPatients, sortedObservationCodes, null,3);
params.setLastN(true);
Map<String, String[]> requestParameters = new HashMap<>();
when(mySrd.getParameters()).thenReturn(requestParameters);
List<String> actual = toUnqualifiedVersionlessIdValues(myObservationDao.observationsLastN(params, mockSrd(),null));
assertEquals(3, actual.size());
}
private void executeTestCase(SearchParameterMap params, List<String> sortedPatients, List<String> sortedObservationCodes, List<String> theCategories, int expectedObservationCount) {
List<String> actual;
params.setLastN(true);
Map<String, String[]> requestParameters = new HashMap<>();
String[] maxParam = new String[1];
maxParam[0] = "100";
requestParameters.put("max", maxParam);
// String[] maxParam = new String[1];
// maxParam[0] = "100";
// requestParameters.put("max", maxParam);
params.setLastNMax(100);
when(mySrd.getParameters()).thenReturn(requestParameters);
actual = toUnqualifiedVersionlessIdValues(myObservationDao.observationsLastN(params, mockSrd(),null));
@ -297,6 +331,11 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseJpaTest {
// One category parameter.
SearchParameterMap params = new SearchParameterMap();
ReferenceParam subjectParam1 = new ReferenceParam("Patient", "", patient0Id.getValue());
ReferenceParam subjectParam2 = new ReferenceParam("Patient", "", patient1Id.getValue());
ReferenceParam subjectParam3 = new ReferenceParam("Patient", "", patient2Id.getValue());
params.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam1, subjectParam2, subjectParam3));
TokenParam categoryParam = new TokenParam(categorySystem, categoryCd0);
params.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam));
List<String> myCategories = new ArrayList<>();
@ -315,6 +354,7 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseJpaTest {
// Another category parameter.
params = new SearchParameterMap();
params.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam1, subjectParam2, subjectParam3));
categoryParam = new TokenParam(categorySystem, categoryCd2);
params.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam));
myCategories = new ArrayList<>();
@ -333,6 +373,11 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseJpaTest {
// Two category parameters.
SearchParameterMap params = new SearchParameterMap();
ReferenceParam subjectParam1 = new ReferenceParam("Patient", "", patient0Id.getValue());
ReferenceParam subjectParam2 = new ReferenceParam("Patient", "", patient1Id.getValue());
ReferenceParam subjectParam3 = new ReferenceParam("Patient", "", patient2Id.getValue());
params.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam1, subjectParam2, subjectParam3));
TokenParam categoryParam1 = new TokenParam(categorySystem, categoryCd0);
TokenParam categoryParam2 = new TokenParam(categorySystem, categoryCd1);
params.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam1, categoryParam2));
@ -357,6 +402,11 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseJpaTest {
// One code parameter.
SearchParameterMap params = new SearchParameterMap();
ReferenceParam subjectParam1 = new ReferenceParam("Patient", "", patient0Id.getValue());
ReferenceParam subjectParam2 = new ReferenceParam("Patient", "", patient1Id.getValue());
ReferenceParam subjectParam3 = new ReferenceParam("Patient", "", patient2Id.getValue());
params.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam1, subjectParam2, subjectParam3));
TokenParam code = new TokenParam(codeSystem, observationCd0);
params.add(Observation.SP_CODE, buildTokenAndListParam(code));
List<String> sortedObservationCodes = new ArrayList<>();
@ -371,6 +421,7 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseJpaTest {
// Another code parameter.
params = new SearchParameterMap();
params.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam1, subjectParam2, subjectParam3));
code = new TokenParam(codeSystem, observationCd2);
params.add(Observation.SP_CODE, buildTokenAndListParam(code));
sortedObservationCodes = new ArrayList<>();
@ -385,6 +436,11 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseJpaTest {
// Two code parameters.
SearchParameterMap params = new SearchParameterMap();
ReferenceParam subjectParam1 = new ReferenceParam("Patient", "", patient0Id.getValue());
ReferenceParam subjectParam2 = new ReferenceParam("Patient", "", patient1Id.getValue());
ReferenceParam subjectParam3 = new ReferenceParam("Patient", "", patient2Id.getValue());
params.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam1, subjectParam2, subjectParam3));
TokenParam codeParam1 = new TokenParam(codeSystem, observationCd0);
TokenParam codeParam2 = new TokenParam(codeSystem, observationCd1);
params.add(Observation.SP_CODE, buildTokenAndListParam(codeParam1, codeParam2));

View File

@ -79,6 +79,8 @@ public class PersistObservationIndexedSearchParamLastNR4IT {
private final String CODEFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-code";
private final String CODEFIRSTCODINGCODE = "test-code";
private ReferenceAndListParam multiSubjectParams = null;
@Test
public void testIndexObservationSingle() {
indexSingleObservation();
@ -177,6 +179,8 @@ public class PersistObservationIndexedSearchParamLastNR4IT {
// Check that all observations were indexed.
SearchParameterMap searchParameterMap = new SearchParameterMap();
searchParameterMap.add(Observation.SP_SUBJECT, multiSubjectParams);
//searchParameterMap.
List<String> observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 10);
assertEquals(100, observationIdsOnly.size());
@ -223,10 +227,14 @@ public class PersistObservationIndexedSearchParamLastNR4IT {
categoryCodeableConcept2.setCoding(category2);
categoryConcepts2.add(categoryCodeableConcept2);
ReferenceOrListParam subjectParams = new ReferenceOrListParam();
for (int patientCount = 0; patientCount < 10; patientCount++) {
String subjectId = String.valueOf(patientCount);
ReferenceParam subjectParam = new ReferenceParam("Patient", "", subjectId);
subjectParams.addOr(subjectParam);
for (int entryCount = 0; entryCount < 10; entryCount++) {
Observation observation = new Observation();
@ -254,6 +262,9 @@ public class PersistObservationIndexedSearchParamLastNR4IT {
}
}
multiSubjectParams = new ReferenceAndListParam().addAnd(subjectParams);
}
@Test
@ -265,6 +276,7 @@ public class PersistObservationIndexedSearchParamLastNR4IT {
assertNotNull(observation);
SearchParameterMap searchParameterMap = new SearchParameterMap();
searchParameterMap.add(Observation.SP_SUBJECT, multiSubjectParams);
List<String> observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 10);
assertEquals(100, observationIdsOnly.size());
assertTrue(observationIdsOnly.contains("55"));

View File

@ -56,23 +56,40 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
}
@Test
public void testLastNNoCriteriaQuery() {
public void testLastNAllPatientsQuery() {
// execute Observation ID search (Composite Aggregation) last 3 observations for each patient
List<ObservationJson> observations = elasticsearchSvc.executeLastNWithAllFields(null, 3);
SearchParameterMap searchParameterMap = new SearchParameterMap();
ReferenceParam subjectParam = new ReferenceParam("Patient", "", "0");
searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam));
subjectParam = new ReferenceParam("Patient", "", "1");
searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam));
subjectParam = new ReferenceParam("Patient", "", "2");
searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam));
subjectParam = new ReferenceParam("Patient", "", "3");
searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam));
subjectParam = new ReferenceParam("Patient", "", "4");
searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam));
subjectParam = new ReferenceParam("Patient", "", "5");
searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam));
subjectParam = new ReferenceParam("Patient", "", "6");
searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam));
subjectParam = new ReferenceParam("Patient", "", "7");
searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam));
subjectParam = new ReferenceParam("Patient", "", "8");
searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam));
subjectParam = new ReferenceParam("Patient", "", "9");
searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam));
validateQueryResponse(observations);
List<ObservationJson> observations = elasticsearchSvc.executeLastNWithAllFields(searchParameterMap, 3);
}
private void validateQueryResponse(List<ObservationJson> observationIdsOnly) {
assertEquals(60, observationIdsOnly.size());
assertEquals(60, observations.size());
// Observation documents should be grouped by subject, then by observation code, and then sorted by effective date/time
// within each observation code. Verify the grouping by creating a nested Map.
Map<String, Map<String, List<Date>>> queriedPatientObservationMap = new HashMap<>();
ObservationJson previousObservationJson = null;
for (ObservationJson observationJson : observationIdsOnly) {
for (ObservationJson observationJson : observations) {
assertNotNull(observationJson.getIdentifier());
assertNotNull(observationJson.getSubject());
assertNotNull(observationJson.getCode_concept_id());
@ -373,4 +390,19 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
}
@Test
public void testLastNNoParamsQuery() {
SearchParameterMap searchParameterMap = new SearchParameterMap();
List<ObservationJson> observations = elasticsearchSvc.executeLastNWithAllFields(searchParameterMap, 1);
assertEquals(2, observations.size());
String observationCode1 = observations.get(0).getCode_coding_code_system_hash();
String observationCode2 = observations.get(1).getCode_coding_code_system_hash();
assertNotEquals(observationCode1, observationCode2);
}
}

View File

@ -47,9 +47,9 @@ public class TestElasticsearchConfig {
return embeddedElastic;
}
@PreDestroy
public void stop() {
embeddedElasticSearch().stop();
}
// @PreDestroy
// public void stop() {
// embeddedElasticSearch().stop();
// }
}

View File

@ -62,6 +62,7 @@ public class SearchParameterMap implements Serializable {
private SearchTotalModeEnum mySearchTotalMode;
private QuantityParam myNearDistanceParam;
private boolean myLastN;
private Integer myLastNMax;
/**
* Constructor
@ -322,6 +323,24 @@ public class SearchParameterMap implements Serializable {
}
/**
* If set, tells the server the maximum number of observations to return for each
* observation code in the result set of a lastn operation
*/
public Integer getLastNMax() {
return myLastNMax;
}
/**
* If set, tells the server the maximum number of observations to return for each
* observation code in the result set of a lastn operation
*/
public SearchParameterMap setLastNMax(Integer theLastNMax) {
myLastNMax = theLastNMax;
return this;
}
/**
* This method creates a URL query string representation of the parameters in this
* object, excluding the part before the parameters, e.g.