Fixed sorting with chunked and paged queries.

This commit is contained in:
ianmarshall 2020-05-19 14:48:26 -04:00
parent 529e1e1f5e
commit 6a27192a96
17 changed files with 334 additions and 273 deletions

View File

@ -585,6 +585,11 @@
<version>1.0-SNAPSHOT</version>
<classifier>shaded6</classifier>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.9</version>
</dependency>
</dependencies>

View File

@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.dao;
*/
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoObservation;
import ca.uhn.fhir.jpa.search.lastn.IndexConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.api.server.*;
@ -35,12 +34,18 @@ public abstract class BaseHapiFhirResourceDaoObservation<T extends IBaseResource
}
theSearchParameterMap.setLastN(true);
if (theSearchParameterMap.getSort() == null) {
SortSpec effectiveDtm = new SortSpec("date").setOrder(SortOrderEnum.DESC);
// TODO: Should probably remove these constants, maybe move this logic to the version-specific classes.
SortSpec observationCode = new SortSpec(IndexConstants.CODE_SEARCH_PARAM).setOrder(SortOrderEnum.ASC).setChain(effectiveDtm);
theSearchParameterMap.setSort(new SortSpec(IndexConstants.SUBJECT_SEARCH_PARAM).setOrder(SortOrderEnum.ASC).setChain(observationCode));
SortSpec effectiveDtm = new SortSpec(getEffectiveParamName()).setOrder(SortOrderEnum.DESC);
SortSpec observationCode = new SortSpec(getCodeParamName()).setOrder(SortOrderEnum.ASC).setChain(effectiveDtm);
if(theSearchParameterMap.containsKey(getSubjectParamName()) || theSearchParameterMap.containsKey(getPatientParamName())) {
theSearchParameterMap.setSort(new SortSpec(getSubjectParamName()).setOrder(SortOrderEnum.ASC).setChain(observationCode));
} else {
theSearchParameterMap.setSort(observationCode);
}
}
abstract protected String getEffectiveParamName();
abstract protected String getCodeParamName();
abstract protected String getSubjectParamName();
abstract protected String getPatientParamName();
}

View File

@ -317,7 +317,7 @@ public class SearchBuilder implements ISearchBuilder {
} else {
throw new InvalidRequestException("Max parameter is required for $lastn operation");
}
List<String> lastnResourceIds = myIElasticsearchSvc.executeLastN(myParams, myMaxObservationsPerCode, theMaximumResults);
List<String> lastnResourceIds = myIElasticsearchSvc.executeLastN(myParams, myContext, theMaximumResults);
for (String lastnResourceId : lastnResourceIds) {
pids.add(myIdHelperService.resolveResourcePersistentIds(myRequestPartitionId, myResourceName, lastnResourceId));
}
@ -422,52 +422,6 @@ public class SearchBuilder implements ISearchBuilder {
searchForIdsWithAndOr(myParams, theRequest);
}
/*
* Fulltext or lastn search
*/
/* if (myParams.containsKey(Constants.PARAM_CONTENT) || myParams.containsKey(Constants.PARAM_TEXT) || myParams.isLastN()) {
List<ResourcePersistentId> pids = new ArrayList<>();
if (myParams.containsKey(Constants.PARAM_CONTENT) || myParams.containsKey(Constants.PARAM_TEXT)) {
if (myFulltextSearchSvc == null) {
if (myParams.containsKey(Constants.PARAM_TEXT)) {
throw new InvalidRequestException("Fulltext search is not enabled on this service, can not process parameter: " + Constants.PARAM_TEXT);
} else if (myParams.containsKey(Constants.PARAM_CONTENT)) {
throw new InvalidRequestException("Fulltext search is not enabled on this service, can not process parameter: " + Constants.PARAM_CONTENT);
}
}
if (myParams.getEverythingMode() != null) {
pids = myFulltextSearchSvc.everything(myResourceName, myParams, theRequest);
} else {
pids = myFulltextSearchSvc.search(myResourceName, myParams);
}
} else if (myParams.isLastN()) {
if (myIElasticsearchSvc == null) {
if (myParams.isLastN()) {
throw new InvalidRequestException("LastN operation is not enabled on this service, can not process this request");
}
}
Integer myMaxObservationsPerCode = null;
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));
}
// pids = normalizeIdListForLastNInClause(lastnResourceIds);
}
if (pids.isEmpty()) {
// Will never match
pids = Collections.singletonList(new ResourcePersistentId(-1L));
}
myQueryRoot.addPredicate(myQueryRoot.get("myId").as(Long.class).in(ResourcePersistentId.toLongList(pids)));
}
*/
// Add PID list predicate for full text search and/or lastn operation
if (thePidList != null && thePidList.size() > 0) {
myQueryRoot.addPredicate(myQueryRoot.get("myId").as(Long.class).in(thePidList));

View File

@ -30,8 +30,6 @@ 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.api.server.storage.TransactionDetails;
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;
@ -52,6 +50,26 @@ public class FhirResourceDaoObservationDstu3 extends BaseHapiFhirResourceDaoObse
return mySearchCoordinatorSvc.registerSearch(this, theSearchParameterMap, getResourceName(), new CacheControlDirective().parse(theRequestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequestDetails);
}
@Override
protected String getEffectiveParamName() {
return Observation.SP_DATE;
}
@Override
protected String getCodeParamName() {
return Observation.SP_CODE;
}
@Override
protected String getSubjectParamName() {
return Observation.SP_SUBJECT;
}
@Override
protected String getPatientParamName() {
return Observation.SP_PATIENT;
}
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {

View File

@ -57,6 +57,26 @@ public class FhirResourceDaoObservationR4 extends BaseHapiFhirResourceDaoObserva
return mySearchCoordinatorSvc.registerSearch(this, theSearchParameterMap, getResourceName(), new CacheControlDirective().parse(theRequestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequestDetails);
}
@Override
protected String getEffectiveParamName() {
return Observation.SP_DATE;
}
@Override
protected String getCodeParamName() {
return Observation.SP_CODE;
}
@Override
protected String getSubjectParamName() {
return Observation.SP_SUBJECT;
}
@Override
protected String getPatientParamName() {
return Observation.SP_PATIENT;
}
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {

View File

@ -30,8 +30,6 @@ 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.api.server.storage.TransactionDetails;
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;
@ -52,6 +50,26 @@ public class FhirResourceDaoObservationR5 extends BaseHapiFhirResourceDaoObserva
return mySearchCoordinatorSvc.registerSearch(this, theSearchParameterMap, getResourceName(), new CacheControlDirective().parse(theRequestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequestDetails);
}
@Override
protected String getEffectiveParamName() {
return Observation.SP_DATE;
}
@Override
protected String getCodeParamName() {
return Observation.SP_CODE;
}
@Override
protected String getSubjectParamName() {
return Observation.SP_SUBJECT;
}
@Override
protected String getPatientParamName() {
return Observation.SP_PATIENT;
}
@Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {

View File

@ -70,10 +70,7 @@ public class BaseJpaResourceProviderObservationDstu3 extends JpaResourceProvider
@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
IPrimitiveType<Integer> theMax
) {
startRequest(theServletRequest);
@ -81,17 +78,17 @@ public class BaseJpaResourceProviderObservationDstu3 extends JpaResourceProvider
SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add(Observation.SP_CATEGORY, theCategory);
paramMap.add(Observation.SP_CODE, theCode);
paramMap.add(Observation.SP_PATIENT, thePatient);
paramMap.add(Observation.SP_SUBJECT, theSubject);
if (thePatient != null) {
paramMap.add("patient", thePatient);
}
if (theSubject != null) {
paramMap.add("subject", theSubject);
}
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 {
endRequest(theServletRequest);

View File

@ -69,28 +69,25 @@ public class BaseJpaResourceProviderObservationR4 extends JpaResourceProviderR4<
@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
IPrimitiveType<Integer> theMax
) {
startRequest(theServletRequest);
try {
SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add("category", theCategory);
paramMap.add("code", theCode);
paramMap.add("patient", thePatient);
paramMap.add("subject", theSubject);
paramMap.add(Observation.SP_CATEGORY, theCategory);
paramMap.add(Observation.SP_CODE, theCode);
if (thePatient != null) {
paramMap.add(Observation.SP_PATIENT, thePatient);
}
if (theSubject != null) {
paramMap.add(Observation.SP_SUBJECT, theSubject);
}
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 {
endRequest(theServletRequest);

View File

@ -70,28 +70,25 @@ public class BaseJpaResourceProviderObservationR5 extends JpaResourceProviderR5<
@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
IPrimitiveType<Integer> theMax
) {
startRequest(theServletRequest);
try {
SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add("category", theCategory);
paramMap.add("code", theCode);
paramMap.add("patient", thePatient);
paramMap.add("subject", theSubject);
paramMap.add(Observation.SP_CATEGORY, theCategory);
paramMap.add(Observation.SP_CODE, theCode);
if (thePatient != null) {
paramMap.add(Observation.SP_PATIENT, thePatient);
}
if (theSubject != null) {
paramMap.add(Observation.SP_SUBJECT, theSubject);
}
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 {
endRequest(theServletRequest);

View File

@ -1,6 +1,8 @@
package ca.uhn.fhir.jpa.search.lastn;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.util.LastNParameterHelper;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.TokenParam;
@ -48,13 +50,18 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
public class ElasticsearchSvcImpl implements IElasticsearchSvc {
public static final String OBSERVATION_INDEX = "observation_index";
public static final String CODE_INDEX = "code_index";
public static final String OBSERVATION_DOCUMENT_TYPE = "ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedSearchParamLastNEntity";
public static final String CODE_DOCUMENT_TYPE = "ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedCodeCodeableConceptEntity";
private final RestHighLevelClient myRestHighLevelClient;
private final ObjectMapper objectMapper = new ObjectMapper();
private final String GROUP_BY_SUBJECT = "group_by_subject";
private final String GROUP_BY_SYSTEM = "group_by_system";
private final String GROUP_BY_CODE = "group_by_code";
private final String OBSERVATION_IDENTIFIER_FIELD_NAME = "identifier";
public ElasticsearchSvcImpl(String theHostname, int thePort, String theUsername, String thePassword) {
@ -69,7 +76,7 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
}
private void createObservationIndexIfMissing() throws IOException {
if (indexExists(IndexConstants.OBSERVATION_INDEX)) {
if (indexExists(OBSERVATION_INDEX)) {
return;
}
String observationMapping = "{\n" +
@ -124,14 +131,14 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
" }\n" +
" }\n" +
"}\n";
if (!createIndex(IndexConstants.OBSERVATION_INDEX, observationMapping)) {
if (!createIndex(OBSERVATION_INDEX, observationMapping)) {
throw new RuntimeException("Failed to create observation index");
}
}
private void createCodeIndexIfMissing() throws IOException {
if (indexExists(IndexConstants.CODE_INDEX)) {
if (indexExists(CODE_INDEX)) {
return;
}
String codeMapping = "{\n" +
@ -161,7 +168,7 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
" }\n" +
" }\n" +
"}\n";
if (!createIndex(IndexConstants.CODE_INDEX, codeMapping)) {
if (!createIndex(CODE_INDEX, codeMapping)) {
throw new RuntimeException("Failed to create code index");
}
@ -199,16 +206,18 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
}
@Override
// TODO: Should eliminate dependency on SearchParameterMap in API.
public List<String> executeLastN(SearchParameterMap theSearchParameterMap, Integer theMaxObservationsPerCode, Integer theMaxResultsToFetch) {
public List<String> executeLastN(SearchParameterMap theSearchParameterMap, FhirContext theFhirContext, Integer theMaxResultsToFetch) {
String OBSERVATION_IDENTIFIER_FIELD_NAME = "identifier";
String[] topHitsInclude = {OBSERVATION_IDENTIFIER_FIELD_NAME};
try {
List<SearchResponse> responses = buildAndExecuteSearch(theSearchParameterMap, theMaxObservationsPerCode, topHitsInclude);
List<SearchResponse> responses = buildAndExecuteSearch(theSearchParameterMap, theFhirContext, topHitsInclude);
List<String> observationIds = new ArrayList<>();
for (SearchResponse response : responses) {
// observationIds.addAll(buildObservationIdList(response));
Integer maxResultsToAdd = theMaxResultsToFetch - observationIds.size();
observationIds.addAll(buildObservationList(response, t -> t.getIdentifier(), theSearchParameterMap, maxResultsToAdd));
Integer maxResultsToAdd = null;
if (theMaxResultsToFetch != null) {
maxResultsToAdd = theMaxResultsToFetch - observationIds.size();
}
observationIds.addAll(buildObservationList(response, ObservationJson::getIdentifier, theSearchParameterMap, theFhirContext, maxResultsToAdd));
}
return observationIds;
} catch (IOException theE) {
@ -216,23 +225,27 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
}
}
private List<SearchResponse> buildAndExecuteSearch(SearchParameterMap theSearchParameterMap, Integer theMaxObservationsPerCode,
private List<SearchResponse> buildAndExecuteSearch(SearchParameterMap theSearchParameterMap, FhirContext theFhirContext,
String[] topHitsInclude) {
List<SearchResponse> responses = new ArrayList<>();
if (theSearchParameterMap.containsKey(IndexConstants.PATIENT_SEARCH_PARAM) || theSearchParameterMap.containsKey(IndexConstants.SUBJECT_SEARCH_PARAM)) {
String patientParamName = LastNParameterHelper.getPatientParamName(theFhirContext);
String subjectParamName = LastNParameterHelper.getSubjectParamName(theFhirContext);
if (theSearchParameterMap.containsKey(patientParamName)
|| theSearchParameterMap.containsKey(subjectParamName)) {
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(patientParamName) != null) {
patientParams.addAll(theSearchParameterMap.get(patientParamName));
}
if (theSearchParameterMap.get(IndexConstants.SUBJECT_SEARCH_PARAM) != null) {
patientParams.addAll(theSearchParameterMap.get(IndexConstants.SUBJECT_SEARCH_PARAM));
if (theSearchParameterMap.get(subjectParamName) != null) {
patientParams.addAll(theSearchParameterMap.get(subjectParamName));
}
for (List<? extends IQueryParameterType> nextSubjectList : patientParams) {
subjectReferenceCriteria.addAll(getReferenceValues(nextSubjectList));
}
for (String subject : subjectReferenceCriteria) {
SearchRequest myLastNRequest = buildObservationsSearchRequest(subject, theSearchParameterMap, createCompositeAggregationBuilder(theMaxObservationsPerCode, topHitsInclude));
SearchRequest myLastNRequest = buildObservationsSearchRequest(subject, theSearchParameterMap, theFhirContext,
createCompositeAggregationBuilder(theSearchParameterMap.getLastNMax(), topHitsInclude));
try {
SearchResponse lastnResponse = executeSearchRequest(myLastNRequest);
responses.add(lastnResponse);
@ -241,7 +254,7 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
}
}
} else {
SearchRequest myLastNRequest = buildObservationsSearchRequest(theSearchParameterMap, createObservationCodeAggregationBuilder(theMaxObservationsPerCode, topHitsInclude));
SearchRequest myLastNRequest = buildObservationsSearchRequest(theSearchParameterMap, theFhirContext, createObservationCodeAggregationBuilder(theSearchParameterMap.getLastNMax(), topHitsInclude));
try {
SearchResponse lastnResponse = executeSearchRequest(myLastNRequest);
responses.add(lastnResponse);
@ -254,13 +267,12 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
}
@VisibleForTesting
// TODO: Should eliminate dependency on SearchParameterMap in API.
List<ObservationJson> executeLastNWithAllFields(SearchParameterMap theSearchParameterMap, Integer theMaxObservationsPerCode, Integer theMaxResultsToFetch) {
List<ObservationJson> executeLastNWithAllFields(SearchParameterMap theSearchParameterMap, FhirContext theFhirContext) {
try {
List<SearchResponse> responses = buildAndExecuteSearch(theSearchParameterMap, theMaxObservationsPerCode, null);
List<SearchResponse> responses = buildAndExecuteSearch(theSearchParameterMap, theFhirContext, null);
List<ObservationJson> observationDocuments = new ArrayList<>();
for (SearchResponse response : responses) {
observationDocuments.addAll(buildObservationList(response, t -> t, theSearchParameterMap, theMaxResultsToFetch));
observationDocuments.addAll(buildObservationList(response, t -> t, theSearchParameterMap, theFhirContext, 100));
}
return observationDocuments;
} catch (IOException theE) {
@ -269,20 +281,15 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
}
@VisibleForTesting
List<CodeJson> queryAllIndexedObservationCodes(int theMaxResultSetSize) throws IOException {
SearchRequest codeSearchRequest = buildObservationCodesSearchRequest(theMaxResultSetSize);
SearchResponse codeSearchResponse = executeSearchRequest(codeSearchRequest);
return buildCodeResult(codeSearchResponse);
}
private SearchRequest buildObservationCodesSearchRequest(int theMaxResultSetSize) {
SearchRequest searchRequest = new SearchRequest(IndexConstants.CODE_INDEX);
List<CodeJson> queryAllIndexedObservationCodes() throws IOException {
SearchRequest codeSearchRequest = new SearchRequest(CODE_INDEX);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// Query
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
searchSourceBuilder.size(theMaxResultSetSize);
searchRequest.source(searchSourceBuilder);
return searchRequest;
searchSourceBuilder.size(1000);
codeSearchRequest.source(searchSourceBuilder);
SearchResponse codeSearchResponse = executeSearchRequest(codeSearchRequest);
return buildCodeResult(codeSearchResponse);
}
private CompositeAggregationBuilder createCompositeAggregationBuilder(int theMaxNumberObservationsPerCode, String[] theTopHitsInclude) {
@ -297,51 +304,27 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
}
private TermsAggregationBuilder createObservationCodeAggregationBuilder(int theMaxNumberObservationsPerCode, String[] theTopHitsInclude) {
TermsAggregationBuilder observationCodeAggregationBuilder = new TermsAggregationBuilder(GROUP_BY_CODE, ValueType.STRING).field("codeconceptid");
TermsAggregationBuilder observationCodeCodeAggregationBuilder = new TermsAggregationBuilder(GROUP_BY_CODE, ValueType.STRING).field("codeconceptcodingcode");
// Top Hits Aggregation
observationCodeAggregationBuilder.subAggregation(AggregationBuilders.topHits("most_recent_effective")
observationCodeCodeAggregationBuilder.subAggregation(AggregationBuilders.topHits("most_recent_effective")
.sort("effectivedtm", SortOrder.DESC)
.fetchSource(theTopHitsInclude, null).size(theMaxNumberObservationsPerCode));
observationCodeAggregationBuilder.size(10000);
return observationCodeAggregationBuilder;
observationCodeCodeAggregationBuilder.size(10000);
TermsAggregationBuilder observationCodeSystemAggregationBuilder = new TermsAggregationBuilder(GROUP_BY_SYSTEM, ValueType.STRING).field("codeconceptcodingsystem");
observationCodeSystemAggregationBuilder.subAggregation(observationCodeCodeAggregationBuilder);
return observationCodeSystemAggregationBuilder;
}
public SearchResponse executeSearchRequest(SearchRequest searchRequest) throws IOException {
private 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)) {
for (Terms.Bucket observationCodeBucket : getObservationCodeBuckets(subjectBucket)) {
for (SearchHit lastNMatch : getLastNMatches(observationCodeBucket)) {
String indexedObservation = lastNMatch.getSourceAsString();
ObservationJson observationJson = objectMapper.readValue(indexedObservation, ObservationJson.class);
theObservationList.add(observationJson.getIdentifier());
}
}
}
return theObservationList;
}
private List<ObservationJson> buildObservationDocumentList(SearchResponse theSearchResponse) throws IOException {
List<ObservationJson> theObservationList = new ArrayList<>();
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(observationJson);
}
}
}
return theObservationList;
}
private <T> List<T> buildObservationList(SearchResponse theSearchResponse, Function<ObservationJson,T> setValue,
SearchParameterMap theSearchParameterMap, Integer theMaxResultsToFetch) throws IOException {
SearchParameterMap theSearchParameterMap, FhirContext theFhirContext,
Integer theMaxResultsToFetch) throws IOException {
List<T> theObservationList = new ArrayList<>();
if (theSearchParameterMap.containsKey(IndexConstants.PATIENT_SEARCH_PARAM) || theSearchParameterMap.containsKey(IndexConstants.SUBJECT_SEARCH_PARAM)) {
if (theSearchParameterMap.containsKey(LastNParameterHelper.getPatientParamName(theFhirContext))
|| theSearchParameterMap.containsKey(LastNParameterHelper.getSubjectParamName(theFhirContext))) {
for (ParsedComposite.ParsedBucket subjectBucket : getSubjectBuckets(theSearchResponse)) {
if (theMaxResultsToFetch != null && theObservationList.size() >= theMaxResultsToFetch) {
break;
@ -387,14 +370,23 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
private List<? extends Terms.Bucket> getObservationCodeBuckets(SearchResponse theSearchResponse) {
Aggregations responseAggregations = theSearchResponse.getAggregations();
ParsedTerms aggregatedObservationCodes = responseAggregations.get(GROUP_BY_CODE);
return aggregatedObservationCodes.getBuckets();
return getObservationCodeBuckets(responseAggregations);
}
private List<? extends Terms.Bucket> getObservationCodeBuckets(ParsedComposite.ParsedBucket theSubjectBucket) {
Aggregations observationCodeAggregations = theSubjectBucket.getAggregations();
ParsedTerms aggregatedObservationCodes = observationCodeAggregations.get(GROUP_BY_CODE);
return aggregatedObservationCodes.getBuckets();
Aggregations observationCodeSystemAggregations = theSubjectBucket.getAggregations();
return getObservationCodeBuckets(observationCodeSystemAggregations);
}
private List<? extends Terms.Bucket> getObservationCodeBuckets(Aggregations theObservationCodeSystemAggregations) {
List<Terms.Bucket> retVal = new ArrayList<>();
ParsedTerms aggregatedObservationCodeSystems = theObservationCodeSystemAggregations.get(GROUP_BY_SYSTEM);
for(Terms.Bucket observationCodeSystem : aggregatedObservationCodeSystems.getBuckets()) {
Aggregations observationCodeCodeAggregations = observationCodeSystem.getAggregations();
ParsedTerms aggregatedObservationCodeCodes = observationCodeCodeAggregations.get(GROUP_BY_CODE);
retVal.addAll(aggregatedObservationCodeCodes.getBuckets());
}
return retVal;
}
private SearchHit[] getLastNMatches(Terms.Bucket theObservationCodeBucket) {
@ -413,17 +405,16 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
return codes;
}
private SearchRequest buildObservationsSearchRequest(SearchParameterMap theSearchParameterMap, AggregationBuilder theAggregationBuilder) {
SearchRequest searchRequest = new SearchRequest(IndexConstants.OBSERVATION_INDEX);
private SearchRequest buildObservationsSearchRequest(SearchParameterMap theSearchParameterMap, FhirContext theFhirContext, AggregationBuilder theAggregationBuilder) {
SearchRequest searchRequest = new SearchRequest(OBSERVATION_INDEX);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// Query
if (!searchParamsHaveLastNCriteria(theSearchParameterMap)) {
if (!searchParamsHaveLastNCriteria(theSearchParameterMap, theFhirContext)) {
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
} else {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
addSubjectsCriteria(boolQueryBuilder, theSearchParameterMap);
addCategoriesCriteria(boolQueryBuilder, theSearchParameterMap);
addObservationCodeCriteria(boolQueryBuilder, theSearchParameterMap);
addCategoriesCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext);
addObservationCodeCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext);
searchSourceBuilder.query(boolQueryBuilder);
}
searchSourceBuilder.size(0);
@ -435,14 +426,15 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
return searchRequest;
}
private SearchRequest buildObservationsSearchRequest(String theSubjectParam, SearchParameterMap theSearchParameterMap, AggregationBuilder theAggregationBuilder) {
SearchRequest searchRequest = new SearchRequest(IndexConstants.OBSERVATION_INDEX);
private SearchRequest buildObservationsSearchRequest(String theSubjectParam, SearchParameterMap theSearchParameterMap, FhirContext theFhirContext,
AggregationBuilder theAggregationBuilder) {
SearchRequest searchRequest = new SearchRequest(OBSERVATION_INDEX);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// Query
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.termQuery("subject", theSubjectParam));
addCategoriesCriteria(boolQueryBuilder, theSearchParameterMap);
addObservationCodeCriteria(boolQueryBuilder, theSearchParameterMap);
addCategoriesCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext);
addObservationCodeCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext);
searchSourceBuilder.query(boolQueryBuilder);
searchSourceBuilder.size(0);
@ -453,30 +445,12 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
return searchRequest;
}
private Boolean searchParamsHaveLastNCriteria(SearchParameterMap theSearchParameterMap) {
private Boolean searchParamsHaveLastNCriteria(SearchParameterMap theSearchParameterMap, FhirContext theFhirContext) {
return theSearchParameterMap != null &&
(theSearchParameterMap.containsKey(IndexConstants.PATIENT_SEARCH_PARAM) || theSearchParameterMap.containsKey(IndexConstants.SUBJECT_SEARCH_PARAM) ||
theSearchParameterMap.containsKey(IndexConstants.CATEGORY_SEARCH_PARAM) || theSearchParameterMap.containsKey(IndexConstants.CODE_SEARCH_PARAM));
}
private void addSubjectsCriteria(BoolQueryBuilder theBoolQueryBuilder, SearchParameterMap theSearchParameterMap) {
if (theSearchParameterMap.containsKey(IndexConstants.PATIENT_SEARCH_PARAM) || theSearchParameterMap.containsKey(IndexConstants.SUBJECT_SEARCH_PARAM)) {
ArrayList<String> subjectReferenceCriteria = new ArrayList<>();
List<List<IQueryParameterType>> andOrParams = new ArrayList<>();
if (theSearchParameterMap.get(IndexConstants.PATIENT_SEARCH_PARAM) != null) {
andOrParams.addAll(theSearchParameterMap.get(IndexConstants.PATIENT_SEARCH_PARAM));
}
if (theSearchParameterMap.get(IndexConstants.SUBJECT_SEARCH_PARAM) != null) {
andOrParams.addAll(theSearchParameterMap.get(IndexConstants.SUBJECT_SEARCH_PARAM));
}
for (List<? extends IQueryParameterType> nextAnd : andOrParams) {
subjectReferenceCriteria.addAll(getReferenceValues(nextAnd));
}
if (subjectReferenceCriteria.size() > 0) {
theBoolQueryBuilder.must(QueryBuilders.termsQuery("subject", subjectReferenceCriteria));
}
}
(theSearchParameterMap.containsKey(LastNParameterHelper.getPatientParamName(theFhirContext))
|| theSearchParameterMap.containsKey(LastNParameterHelper.getSubjectParamName(theFhirContext))
|| theSearchParameterMap.containsKey(LastNParameterHelper.getCategoryParamName(theFhirContext))
|| theSearchParameterMap.containsKey(LastNParameterHelper.getCodeParamName(theFhirContext)));
}
private List<String> getReferenceValues(List<? extends IQueryParameterType> referenceParams) {
@ -496,13 +470,14 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
return referenceList;
}
private void addCategoriesCriteria(BoolQueryBuilder theBoolQueryBuilder, SearchParameterMap theSearchParameterMap) {
if (theSearchParameterMap.containsKey(IndexConstants.CATEGORY_SEARCH_PARAM)) {
private void addCategoriesCriteria(BoolQueryBuilder theBoolQueryBuilder, SearchParameterMap theSearchParameterMap, FhirContext theFhirContext) {
String categoryParamName = LastNParameterHelper.getCategoryParamName(theFhirContext);
if (theSearchParameterMap.containsKey(categoryParamName)) {
ArrayList<String> codeSystemHashList = new ArrayList<>();
ArrayList<String> codeOnlyList = new ArrayList<>();
ArrayList<String> systemOnlyList = new ArrayList<>();
ArrayList<String> textOnlyList = new ArrayList<>();
List<List<IQueryParameterType>> andOrParams = theSearchParameterMap.get(IndexConstants.CATEGORY_SEARCH_PARAM);
List<List<IQueryParameterType>> andOrParams = theSearchParameterMap.get(categoryParamName);
for (List<? extends IQueryParameterType> nextAnd : andOrParams) {
codeSystemHashList.addAll(getCodingCodeSystemValues(nextAnd));
codeOnlyList.addAll(getCodingCodeOnlyValues(nextAnd));
@ -593,13 +568,14 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
return textOnlyList;
}
private void addObservationCodeCriteria(BoolQueryBuilder theBoolQueryBuilder, SearchParameterMap theSearchParameterMap) {
if (theSearchParameterMap.containsKey(IndexConstants.CODE_SEARCH_PARAM)) {
private void addObservationCodeCriteria(BoolQueryBuilder theBoolQueryBuilder, SearchParameterMap theSearchParameterMap, FhirContext theFhirContext) {
String codeParamName = LastNParameterHelper.getCodeParamName(theFhirContext);
if (theSearchParameterMap.containsKey(codeParamName)) {
ArrayList<String> codeSystemHashList = new ArrayList<>();
ArrayList<String> codeOnlyList = new ArrayList<>();
ArrayList<String> systemOnlyList = new ArrayList<>();
ArrayList<String> textOnlyList = new ArrayList<>();
List<List<IQueryParameterType>> andOrParams = theSearchParameterMap.get(IndexConstants.CODE_SEARCH_PARAM);
List<List<IQueryParameterType>> andOrParams = theSearchParameterMap.get(codeParamName);
for (List<? extends IQueryParameterType> nextAnd : andOrParams) {
codeSystemHashList.addAll(getCodingCodeSystemValues(nextAnd));
codeOnlyList.addAll(getCodingCodeOnlyValues(nextAnd));
@ -634,12 +610,4 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
myRestHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
}
/* 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

@ -1,9 +1,10 @@
package ca.uhn.fhir.jpa.search.lastn;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import java.util.List;
public interface IElasticsearchSvc {
List<String> executeLastN(SearchParameterMap theSearchParameterMap, Integer theMaxObservationsPerCode, Integer theMaxResultsToFetch);
List<String> executeLastN(SearchParameterMap theSearchParameterMap, FhirContext theFhirContext, Integer theMaxResultsToFetch);
}

View File

@ -1,16 +0,0 @@
package ca.uhn.fhir.jpa.search.lastn;
public class IndexConstants {
// TODO: These should all be moved into ElasticSearchSvcImpl.
public static final String OBSERVATION_INDEX = "observation_index";
public static final String CODE_INDEX = "code_index";
public static final String OBSERVATION_DOCUMENT_TYPE = "ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedSearchParamLastNEntity";
public static final String CODE_DOCUMENT_TYPE = "ca.uhn.fhir.jpa.dao.lastn.entity.ObservationIndexedCodeCodeableConceptEntity";
public static final String SUBJECT_SEARCH_PARAM = "subject";
public static final String PATIENT_SEARCH_PARAM = "patient";
public static final String CODE_SEARCH_PARAM = "code";
public static final String CATEGORY_SEARCH_PARAM = "category";
}

View File

@ -104,12 +104,12 @@ public class TestR4Config extends BaseJavaConfigR4 {
retVal.setDriver(new org.h2.Driver());
// retVal.setDriver(new org.postgresql.Driver());
retVal.setUrl("jdbc:h2:mem:testdb_r4");
// retVal.setUrl("jdbc:postgresql://localhost:5432/hapi");
// retVal.setUrl("jdbc:postgresql://localhost:5432/cdr");
retVal.setMaxWaitMillis(10000);
retVal.setUsername("");
// retVal.setUsername("hapi");
// retVal.setUsername("cdr");
retVal.setPassword("");
// retVal.setPassword("HapiFHIR");
// retVal.setPassword("SmileCDR");
retVal.setMaxTotal(ourMaxThreads);
SLF4JLogLevel level = SLF4JLogLevel.INFO;

View File

@ -60,6 +60,9 @@ public class PersistObservationIndexedSearchParamLastNR4IT {
@Autowired
ObservationLastNIndexPersistSvc testObservationPersist;
@Autowired
protected FhirContext myFhirCtx;
@Before
public void before() {
@ -106,8 +109,9 @@ public class PersistObservationIndexedSearchParamLastNR4IT {
searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam)));
TokenParam codeParam = new TokenParam(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE);
searchParameterMap.add(Observation.SP_CODE, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(codeParam)));
searchParameterMap.setLastNMax(3);
List<String> observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 3, 100);
List<String> observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 100);
assertEquals(1, observationIdsOnly.size());
assertEquals(SINGLE_OBSERVATION_PID, observationIdsOnly.get(0));
@ -180,15 +184,18 @@ 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, 200);
searchParameterMap.setLastNMax(10);
List<String> observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200);
assertEquals(100, observationIdsOnly.size());
// Filter the results by category code.
TokenParam categoryParam = new TokenParam(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE);
searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam)));
observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 10, 100);
observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 100);
assertEquals(50, observationIdsOnly.size());
@ -277,7 +284,8 @@ public class PersistObservationIndexedSearchParamLastNR4IT {
SearchParameterMap searchParameterMap = new SearchParameterMap();
searchParameterMap.add(Observation.SP_SUBJECT, multiSubjectParams);
List<String> observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 10, 200);
searchParameterMap.setLastNMax(10);
List<String> observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200);
assertEquals(100, observationIdsOnly.size());
assertTrue(observationIdsOnly.contains("55"));
@ -295,7 +303,7 @@ public class PersistObservationIndexedSearchParamLastNR4IT {
observation = myResourceIndexedObservationLastNDao.findForIdentifier("55");
assertNull(observation);
observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 10, 200);
observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200);
assertEquals(99, observationIdsOnly.size());
assertTrue(!observationIdsOnly.contains("55"));
@ -316,7 +324,9 @@ public class PersistObservationIndexedSearchParamLastNR4IT {
searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam)));
TokenParam codeParam = new TokenParam(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE);
searchParameterMap.add(Observation.SP_CODE, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(codeParam)));
List<String> observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 10, 200);
searchParameterMap.setLastNMax(10);
List<String> observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200);
assertEquals(1, observationIdsOnly.size());
assertTrue(observationIdsOnly.contains(SINGLE_OBSERVATION_PID));
@ -339,7 +349,7 @@ public class PersistObservationIndexedSearchParamLastNR4IT {
assertEquals(newEffectiveDtm.getValue(), updatedObservationEntity.getEffectiveDtm());
// Repeat earlier Elasticsearch query. This time, should return no matches.
observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 10, 200);
observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200);
assertEquals(0, observationIdsOnly.size());
// Try again with the new patient ID.
@ -348,7 +358,8 @@ public class PersistObservationIndexedSearchParamLastNR4IT {
searchParameterMap.add(Observation.SP_SUBJECT, new ReferenceAndListParam().addAnd(new ReferenceOrListParam().addOr(subjectParam)));
searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam)));
searchParameterMap.add(Observation.SP_CODE, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(codeParam)));
observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 10, 200);
searchParameterMap.setLastNMax(10);
observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200);
// Should see the observation returned now.
assertEquals(1, observationIdsOnly.size());
@ -398,11 +409,13 @@ public class PersistObservationIndexedSearchParamLastNR4IT {
SearchParameterMap searchParameterMap = new SearchParameterMap();
// execute Observation ID search - Composite Aggregation
List<String> observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap,1, 200);
searchParameterMap.setLastNMax(1);
List<String> observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap,myFhirCtx, 200);
assertEquals(20, observationIdsOnly.size());
observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 3, 200);
searchParameterMap.setLastNMax(3);
observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200);
assertEquals(38, observationIdsOnly.size());

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.search.lastn;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.jpa.search.lastn.config.TestElasticsearchConfig;
@ -21,7 +22,6 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.io.IOException;
import java.util.*;
import static ca.uhn.fhir.jpa.search.lastn.IndexConstants.*;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ -35,6 +35,8 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
private final Map<String, Map<String, List<Date>>> createdPatientObservationMap = new HashMap<>();
private FhirContext myFhirContext = FhirContext.forR4();
@BeforeClass
public static void beforeClass() {
@ -51,8 +53,8 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
@After
public void after() throws IOException {
elasticsearchSvc.deleteAllDocuments(OBSERVATION_INDEX);
elasticsearchSvc.deleteAllDocuments(CODE_INDEX);
elasticsearchSvc.deleteAllDocuments(ElasticsearchSvcImpl.OBSERVATION_INDEX);
elasticsearchSvc.deleteAllDocuments(ElasticsearchSvcImpl.CODE_INDEX);
}
@Test
@ -80,8 +82,9 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam));
subjectParam = new ReferenceParam("Patient", "", "9");
searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam));
searchParameterMap.setLastNMax(3);
List<ObservationJson> observations = elasticsearchSvc.executeLastNWithAllFields(searchParameterMap, 3, 100);
List<ObservationJson> observations = elasticsearchSvc.executeLastNWithAllFields(searchParameterMap, myFhirContext);
assertEquals(60, observations.size());
@ -152,8 +155,9 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
TokenParam codeParam1 = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-1");
TokenParam codeParam2 = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-2");
searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam1, codeParam2));
searchParameterMap.setLastNMax(100);
List<String> observations = elasticsearchSvc.executeLastN(searchParameterMap, 100, 100);
List<String> observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100);
assertEquals(20, observations.size());
@ -164,8 +168,9 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
searchParameterMap.add(Observation.SP_PATIENT, buildReferenceAndListParam(patientParam1, patientParam2));
searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam1, categoryParam2));
searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam1, codeParam2));
searchParameterMap.setLastNMax(100);
observations = elasticsearchSvc.executeLastN(searchParameterMap, 100, 100);
observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100);
assertEquals(20, observations.size());
@ -197,8 +202,9 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam));
TokenParam codeParam = new TokenParam("test-code-1");
searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam));
searchParameterMap.setLastNMax(100);
List<String> observations = elasticsearchSvc.executeLastN(searchParameterMap, 100, 100);
List<String> observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100);
assertEquals(5, observations.size());
@ -214,8 +220,9 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam));
TokenParam codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", null);
searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam));
searchParameterMap.setLastNMax(100);
List<String> observations = elasticsearchSvc.executeLastN(searchParameterMap, 100, 100);
List<String> observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100);
assertEquals(10, observations.size());
}
@ -231,8 +238,9 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
TokenParam codeParam = new TokenParam("test-code-1 display");
codeParam.setModifier(TokenParamModifier.TEXT);
searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam));
searchParameterMap.setLastNMax(100);
List<String> observations = elasticsearchSvc.executeLastN(searchParameterMap, 100, 100);
List<String> observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100);
assertEquals(5, observations.size());
@ -248,7 +256,9 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam));
TokenParam codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-1");
searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam));
List<String> observations = elasticsearchSvc.executeLastN(searchParameterMap, 100, 100);
searchParameterMap.setLastNMax(100);
List<String> observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100);
assertEquals(0, observations.size());
// Invalid subject
@ -259,7 +269,9 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam));
codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-1");
searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam));
observations = elasticsearchSvc.executeLastN(searchParameterMap, 100, 100);
searchParameterMap.setLastNMax(100);
observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100);
assertEquals(0, observations.size());
// Invalid observation code
@ -270,7 +282,9 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam));
codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-999");
searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam));
observations = elasticsearchSvc.executeLastN(searchParameterMap, 100, 100);
searchParameterMap.setLastNMax(100);
observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100);
assertEquals(0, observations.size());
// Invalid category code
@ -281,7 +295,9 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam));
codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-1");
searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam));
observations = elasticsearchSvc.executeLastN(searchParameterMap, 100, 100);
searchParameterMap.setLastNMax(100);
observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100);
assertEquals(0, observations.size());
}
@ -341,12 +357,12 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
observationJson.setCategories(categoryConcepts1);
observationJson.setCode(codeableConceptField1);
observationJson.setCode_concept_id(codeableConceptId1);
assertTrue(elasticsearchSvc.performIndex(CODE_INDEX, codeableConceptId1, codeJson1Document, CODE_DOCUMENT_TYPE));
assertTrue(elasticsearchSvc.performIndex(ElasticsearchSvcImpl.CODE_INDEX, codeableConceptId1, codeJson1Document, ElasticsearchSvcImpl.CODE_DOCUMENT_TYPE));
} else {
observationJson.setCategories(categoryConcepts2);
observationJson.setCode(codeableConceptField2);
observationJson.setCode_concept_id(codeableConceptId2);
assertTrue(elasticsearchSvc.performIndex(CODE_INDEX, codeableConceptId2, codeJson2Document, CODE_DOCUMENT_TYPE));
assertTrue(elasticsearchSvc.performIndex(ElasticsearchSvcImpl.CODE_INDEX, codeableConceptId2, codeJson2Document, ElasticsearchSvcImpl.CODE_DOCUMENT_TYPE));
}
Calendar observationDate = new GregorianCalendar();
@ -355,7 +371,7 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
observationJson.setEffectiveDtm(effectiveDtm);
String observationDocument = ourMapperNonPrettyPrint.writeValueAsString(observationJson);
assertTrue(elasticsearchSvc.performIndex(OBSERVATION_INDEX, identifier, observationDocument, OBSERVATION_DOCUMENT_TYPE));
assertTrue(elasticsearchSvc.performIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX, identifier, observationDocument, ElasticsearchSvcImpl.OBSERVATION_DOCUMENT_TYPE));
if (createdPatientObservationMap.containsKey(subject)) {
Map<String, List<Date>> observationCodeMap = createdPatientObservationMap.get(subject);
@ -393,7 +409,8 @@ public class LastNElasticsearchSvcMultipleObservationsIT {
@Test
public void testLastNNoParamsQuery() {
SearchParameterMap searchParameterMap = new SearchParameterMap();
List<ObservationJson> observations = elasticsearchSvc.executeLastNWithAllFields(searchParameterMap, 1, 100);
searchParameterMap.setLastNMax(1);
List<ObservationJson> observations = elasticsearchSvc.executeLastNWithAllFields(searchParameterMap, myFhirContext);
assertEquals(2, observations.size());

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.search.lastn;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.search.lastn.config.TestElasticsearchConfig;
import ca.uhn.fhir.jpa.search.lastn.json.CodeJson;
import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson;
@ -67,12 +68,14 @@ public class LastNElasticsearchSvcSingleObservationIT {
final String CODEFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-code";
final String CODEFIRSTCODINGCODE = "test-code";
final String CODEFIRSTCODINGDISPLAY = "test-code display";
final String CODESECONDCODINGSYSTEM = "http://myalternatecodes.org/fhir/observation-code";
final String CODESECONDCODINGCODE = "test-alt-code";
final String CODESECONDCODINGDISPLAY = "test-alt-code display";
final String CODETHIRDCODINGSYSTEM = "http://mysecondaltcodes.org/fhir/observation-code";
final String CODETHIRDCODINGCODE = "test-second-alt-code";
final String CODETHIRDCODINGDISPLAY = "test-second-alt-code display";
// final String CODESECONDCODINGSYSTEM = "http://myalternatecodes.org/fhir/observation-code";
// final String CODESECONDCODINGCODE = "test-alt-code";
// final String CODESECONDCODINGDISPLAY = "test-alt-code display";
// final String CODETHIRDCODINGSYSTEM = "http://mysecondaltcodes.org/fhir/observation-code";
// final String CODETHIRDCODINGCODE = "test-second-alt-code";
// final String CODETHIRDCODINGDISPLAY = "test-second-alt-code display";
final FhirContext myFhirContext = FhirContext.forR4();
@BeforeClass
public static void beforeClass() {
@ -85,8 +88,8 @@ public class LastNElasticsearchSvcSingleObservationIT {
@After
public void after() throws IOException {
elasticsearchSvc.deleteAllDocuments(IndexConstants.OBSERVATION_INDEX);
elasticsearchSvc.deleteAllDocuments(IndexConstants.CODE_INDEX);
elasticsearchSvc.deleteAllDocuments(ElasticsearchSvcImpl.OBSERVATION_INDEX);
elasticsearchSvc.deleteAllDocuments(ElasticsearchSvcImpl.CODE_INDEX);
}
@Test
@ -102,14 +105,16 @@ public class LastNElasticsearchSvcSingleObservationIT {
TokenParam codeParam = new TokenParam(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE);
searchParameterMap.add(Observation.SP_CODE, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(codeParam)));
searchParameterMap.setLastNMax(3);
// execute Observation ID search
List<String> observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 3, 100);
List<String> observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100);
assertEquals(1, observationIdsOnly.size());
assertEquals(RESOURCEPID, observationIdsOnly.get(0));
// execute Observation search for all search fields
List<ObservationJson> observations = elasticsearchSvc.executeLastNWithAllFields(searchParameterMap, 3, 100);
List<ObservationJson> observations = elasticsearchSvc.executeLastNWithAllFields(searchParameterMap, myFhirContext);
validateFullObservationSearch(observations);
}
@ -243,7 +248,7 @@ public class LastNElasticsearchSvcSingleObservationIT {
assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE)), code_coding_code_system_hash);
// Retrieve all Observation codes
List<CodeJson> codes = elasticsearchSvc.queryAllIndexedObservationCodes(1000);
List<CodeJson> codes = elasticsearchSvc.queryAllIndexedObservationCodes();
assertEquals(1, codes.size());
CodeJson persistedObservationCode = codes.get(0);
@ -331,11 +336,11 @@ public class LastNElasticsearchSvcSingleObservationIT {
indexedObservation.setCode(codeableConceptField);
String observationDocument = ourMapperNonPrettyPrint.writeValueAsString(indexedObservation);
assertTrue(elasticsearchSvc.performIndex(IndexConstants.OBSERVATION_INDEX, RESOURCEPID, observationDocument, IndexConstants.OBSERVATION_DOCUMENT_TYPE));
assertTrue(elasticsearchSvc.performIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX, RESOURCEPID, observationDocument, ElasticsearchSvcImpl.OBSERVATION_DOCUMENT_TYPE));
CodeJson observationCode = new CodeJson(codeableConceptField, OBSERVATIONSINGLECODEID);
String codeDocument = ourMapperNonPrettyPrint.writeValueAsString(observationCode);
assertTrue(elasticsearchSvc.performIndex(IndexConstants.CODE_INDEX, OBSERVATIONSINGLECODEID, codeDocument, IndexConstants.CODE_DOCUMENT_TYPE));
assertTrue(elasticsearchSvc.performIndex(ElasticsearchSvcImpl.CODE_INDEX, OBSERVATIONSINGLECODEID, codeDocument, ElasticsearchSvcImpl.CODE_DOCUMENT_TYPE));
try {
Thread.sleep(1000L);

View File

@ -35,4 +35,66 @@ public class LastNParameterHelper {
throw new InvalidRequestException("$lastn operation is not implemented for FHIR Version " + theContext.getVersion().getVersion().getFhirVersionString());
}
}
public static String getSubjectParamName(FhirContext theContext) {
if (theContext.getVersion().getVersion() == FhirVersionEnum.R5) {
return org.hl7.fhir.r5.model.Observation.SP_SUBJECT;
} else if (theContext.getVersion().getVersion() == FhirVersionEnum.R4) {
return org.hl7.fhir.r4.model.Observation.SP_SUBJECT;
} else if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU3) {
return org.hl7.fhir.dstu3.model.Observation.SP_SUBJECT;
} else {
throw new InvalidRequestException("$lastn operation is not implemented for FHIR Version " + theContext.getVersion().getVersion().getFhirVersionString());
}
}
public static String getPatientParamName(FhirContext theContext) {
if (theContext.getVersion().getVersion() == FhirVersionEnum.R5) {
return org.hl7.fhir.r5.model.Observation.SP_PATIENT;
} else if (theContext.getVersion().getVersion() == FhirVersionEnum.R4) {
return org.hl7.fhir.r4.model.Observation.SP_PATIENT;
} else if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU3) {
return org.hl7.fhir.dstu3.model.Observation.SP_PATIENT;
} else {
throw new InvalidRequestException("$lastn operation is not implemented for FHIR Version " + theContext.getVersion().getVersion().getFhirVersionString());
}
}
public static String getEffectiveParamName(FhirContext theContext) {
if (theContext.getVersion().getVersion() == FhirVersionEnum.R5) {
return org.hl7.fhir.r5.model.Observation.SP_DATE;
} else if (theContext.getVersion().getVersion() == FhirVersionEnum.R4) {
return org.hl7.fhir.r4.model.Observation.SP_DATE;
} else if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU3) {
return org.hl7.fhir.dstu3.model.Observation.SP_DATE;
} else {
throw new InvalidRequestException("$lastn operation is not implemented for FHIR Version " + theContext.getVersion().getVersion().getFhirVersionString());
}
}
public static String getCategoryParamName(FhirContext theContext) {
if (theContext.getVersion().getVersion() == FhirVersionEnum.R5) {
return org.hl7.fhir.r5.model.Observation.SP_CATEGORY;
} else if (theContext.getVersion().getVersion() == FhirVersionEnum.R4) {
return org.hl7.fhir.r4.model.Observation.SP_CATEGORY;
} else if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU3) {
return org.hl7.fhir.dstu3.model.Observation.SP_CATEGORY;
} else {
throw new InvalidRequestException("$lastn operation is not implemented for FHIR Version " + theContext.getVersion().getVersion().getFhirVersionString());
}
}
public static String getCodeParamName(FhirContext theContext) {
if (theContext.getVersion().getVersion() == FhirVersionEnum.R5) {
return org.hl7.fhir.r5.model.Observation.SP_CODE;
} else if (theContext.getVersion().getVersion() == FhirVersionEnum.R4) {
return org.hl7.fhir.r4.model.Observation.SP_CODE;
} else if (theContext.getVersion().getVersion() == FhirVersionEnum.DSTU3) {
return org.hl7.fhir.dstu3.model.Observation.SP_CODE;
} else {
throw new InvalidRequestException("$lastn operation is not implemented for FHIR Version " + theContext.getVersion().getVersion().getFhirVersionString());
}
}
}