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> <version>1.0-SNAPSHOT</version>
<classifier>shaded6</classifier> <classifier>shaded6</classifier>
</dependency> </dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.9</version>
</dependency>
</dependencies> </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.api.dao.IFhirResourceDaoObservation;
import ca.uhn.fhir.jpa.search.lastn.IndexConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.api.server.*; import ca.uhn.fhir.rest.api.server.*;
@ -35,12 +34,18 @@ public abstract class BaseHapiFhirResourceDaoObservation<T extends IBaseResource
} }
theSearchParameterMap.setLastN(true); theSearchParameterMap.setLastN(true);
if (theSearchParameterMap.getSort() == null) { SortSpec effectiveDtm = new SortSpec(getEffectiveParamName()).setOrder(SortOrderEnum.DESC);
SortSpec effectiveDtm = new SortSpec("date").setOrder(SortOrderEnum.DESC); SortSpec observationCode = new SortSpec(getCodeParamName()).setOrder(SortOrderEnum.ASC).setChain(effectiveDtm);
// TODO: Should probably remove these constants, maybe move this logic to the version-specific classes. if(theSearchParameterMap.containsKey(getSubjectParamName()) || theSearchParameterMap.containsKey(getPatientParamName())) {
SortSpec observationCode = new SortSpec(IndexConstants.CODE_SEARCH_PARAM).setOrder(SortOrderEnum.ASC).setChain(effectiveDtm); theSearchParameterMap.setSort(new SortSpec(getSubjectParamName()).setOrder(SortOrderEnum.ASC).setChain(observationCode));
theSearchParameterMap.setSort(new SortSpec(IndexConstants.SUBJECT_SEARCH_PARAM).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 { } else {
throw new InvalidRequestException("Max parameter is required for $lastn operation"); 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) { for (String lastnResourceId : lastnResourceIds) {
pids.add(myIdHelperService.resolveResourcePersistentIds(myRequestPartitionId, myResourceName, lastnResourceId)); pids.add(myIdHelperService.resolveResourcePersistentIds(myRequestPartitionId, myResourceName, lastnResourceId));
} }
@ -422,52 +422,6 @@ public class SearchBuilder implements ISearchBuilder {
searchForIdsWithAndOr(myParams, theRequest); 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 // Add PID list predicate for full text search and/or lastn operation
if (thePidList != null && thePidList.size() > 0) { if (thePidList != null && thePidList.size() > 0) {
myQueryRoot.addPredicate(myQueryRoot.get("myId").as(Long.class).in(thePidList)); 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.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; 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.instance.model.api.IBaseResource;
import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.Observation;
import org.springframework.beans.factory.annotation.Autowired; 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); 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 @Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { 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); 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 @Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { 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.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; 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.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.Observation; import org.hl7.fhir.r5.model.Observation;
import org.springframework.beans.factory.annotation.Autowired; 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); 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 @Override
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { 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") @Description(shortDefinition="The maximum number of observations to return for each observation code")
@OperationParam(name = "max", typeName = "integer", min = 0, max = 1) @OperationParam(name = "max", typeName = "integer", min = 0, max = 1)
IPrimitiveType<Integer> theMax, IPrimitiveType<Integer> theMax
@Sort
SortSpec theSort
) { ) {
startRequest(theServletRequest); startRequest(theServletRequest);
@ -81,17 +78,17 @@ public class BaseJpaResourceProviderObservationDstu3 extends JpaResourceProvider
SearchParameterMap paramMap = new SearchParameterMap(); SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add(Observation.SP_CATEGORY, theCategory); paramMap.add(Observation.SP_CATEGORY, theCategory);
paramMap.add(Observation.SP_CODE, theCode); paramMap.add(Observation.SP_CODE, theCode);
paramMap.add(Observation.SP_PATIENT, thePatient); if (thePatient != null) {
paramMap.add(Observation.SP_SUBJECT, theSubject); paramMap.add("patient", thePatient);
}
if (theSubject != null) {
paramMap.add("subject", theSubject);
}
paramMap.setLastNMax(theMax.getValue()); paramMap.setLastNMax(theMax.getValue());
if (theCount != null) { if (theCount != null) {
paramMap.setCount(theCount.getValue()); paramMap.setCount(theCount.getValue());
} }
if (theSort != null) {
paramMap.setSort(theSort);
}
return ((IFhirResourceDaoObservation<Observation>) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse); return ((IFhirResourceDaoObservation<Observation>) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse);
} finally { } finally {
endRequest(theServletRequest); 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") @Description(shortDefinition="The maximum number of observations to return for each observation code")
@OperationParam(name = "max", typeName = "integer", min = 0, max = 1) @OperationParam(name = "max", typeName = "integer", min = 0, max = 1)
IPrimitiveType<Integer> theMax, IPrimitiveType<Integer> theMax
@Sort
SortSpec theSort
) { ) {
startRequest(theServletRequest); startRequest(theServletRequest);
try { try {
SearchParameterMap paramMap = new SearchParameterMap(); SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add("category", theCategory); paramMap.add(Observation.SP_CATEGORY, theCategory);
paramMap.add("code", theCode); paramMap.add(Observation.SP_CODE, theCode);
paramMap.add("patient", thePatient); if (thePatient != null) {
paramMap.add("subject", theSubject); paramMap.add(Observation.SP_PATIENT, thePatient);
}
if (theSubject != null) {
paramMap.add(Observation.SP_SUBJECT, theSubject);
}
paramMap.setLastNMax(theMax.getValue()); paramMap.setLastNMax(theMax.getValue());
if (theCount != null) { if (theCount != null) {
paramMap.setCount(theCount.getValue()); paramMap.setCount(theCount.getValue());
} }
if (theSort != null) {
paramMap.setSort(theSort);
}
return ((IFhirResourceDaoObservation<Observation>) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse); return ((IFhirResourceDaoObservation<Observation>) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse);
} finally { } finally {
endRequest(theServletRequest); 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") @Description(shortDefinition="The maximum number of observations to return for each observation code")
@OperationParam(name = "max", typeName = "integer", min = 0, max = 1) @OperationParam(name = "max", typeName = "integer", min = 0, max = 1)
IPrimitiveType<Integer> theMax, IPrimitiveType<Integer> theMax
@Sort
SortSpec theSort
) { ) {
startRequest(theServletRequest); startRequest(theServletRequest);
try { try {
SearchParameterMap paramMap = new SearchParameterMap(); SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add("category", theCategory); paramMap.add(Observation.SP_CATEGORY, theCategory);
paramMap.add("code", theCode); paramMap.add(Observation.SP_CODE, theCode);
paramMap.add("patient", thePatient); if (thePatient != null) {
paramMap.add("subject", theSubject); paramMap.add(Observation.SP_PATIENT, thePatient);
}
if (theSubject != null) {
paramMap.add(Observation.SP_SUBJECT, theSubject);
}
paramMap.setLastNMax(theMax.getValue()); paramMap.setLastNMax(theMax.getValue());
if (theCount != null) { if (theCount != null) {
paramMap.setCount(theCount.getValue()); paramMap.setCount(theCount.getValue());
} }
if (theSort != null) {
paramMap.setSort(theSort);
}
return ((IFhirResourceDaoObservation<Observation>) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse); return ((IFhirResourceDaoObservation<Observation>) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse);
} finally { } finally {
endRequest(theServletRequest); endRequest(theServletRequest);

View File

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

View File

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

View File

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

View File

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