Additional changes to enable processing of lastn operations with large numbers of parameters.
This commit is contained in:
parent
3e49e5615f
commit
4db5beeabf
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -47,9 +47,9 @@ public class TestElasticsearchConfig {
|
|||
return embeddedElastic;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void stop() {
|
||||
embeddedElasticSearch().stop();
|
||||
}
|
||||
// @PreDestroy
|
||||
// public void stop() {
|
||||
// embeddedElasticSearch().stop();
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue