diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index a9f0297e403..3f6223d8827 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -585,6 +585,11 @@ 1.0-SNAPSHOT shaded6 + + org.postgresql + postgresql + 42.2.9 + diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoObservation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoObservation.java index 391e34db536..ed06b3c4eaf 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoObservation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoObservation.java @@ -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 lastnResourceIds = myIElasticsearchSvc.executeLastN(myParams, myMaxObservationsPerCode, theMaximumResults); + List 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 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 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)); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoObservationDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoObservationDstu3.java index b147bb62598..c31c77cd7a0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoObservationDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoObservationDstu3.java @@ -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) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoObservationR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoObservationR4.java index 3290c91fc38..c25ce16ea54 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoObservationR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoObservationR4.java @@ -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) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoObservationR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoObservationR5.java index 7a693c91999..8d9b7f2acfe 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoObservationR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoObservationR5.java @@ -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) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderObservationDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderObservationDstu3.java index a70b7a61d89..4a1e3189a67 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderObservationDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderObservationDstu3.java @@ -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 theMax, - - @Sort - SortSpec theSort + IPrimitiveType 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) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse); } finally { endRequest(theServletRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderObservationR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderObservationR4.java index a4b81125af9..2ad20aa1974 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderObservationR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderObservationR4.java @@ -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 theMax, - - @Sort - SortSpec theSort + IPrimitiveType 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) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse); } finally { endRequest(theServletRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderObservationR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderObservationR5.java index 95e80a775ed..b5cacc7c571 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderObservationR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r5/BaseJpaResourceProviderObservationR5.java @@ -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 theMax, - - @Sort - SortSpec theSort + IPrimitiveType 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) getDao()).observationsLastN(paramMap, theRequestDetails, theServletResponse); } finally { endRequest(theServletRequest); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java index 49af7d3510d..40655996679 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java @@ -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 executeLastN(SearchParameterMap theSearchParameterMap, Integer theMaxObservationsPerCode, Integer theMaxResultsToFetch) { + public List executeLastN(SearchParameterMap theSearchParameterMap, FhirContext theFhirContext, Integer theMaxResultsToFetch) { + String OBSERVATION_IDENTIFIER_FIELD_NAME = "identifier"; String[] topHitsInclude = {OBSERVATION_IDENTIFIER_FIELD_NAME}; try { - List responses = buildAndExecuteSearch(theSearchParameterMap, theMaxObservationsPerCode, topHitsInclude); + List responses = buildAndExecuteSearch(theSearchParameterMap, theFhirContext, topHitsInclude); List 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 buildAndExecuteSearch(SearchParameterMap theSearchParameterMap, Integer theMaxObservationsPerCode, + private List buildAndExecuteSearch(SearchParameterMap theSearchParameterMap, FhirContext theFhirContext, String[] topHitsInclude) { List 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 subjectReferenceCriteria = new ArrayList<>(); List> 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 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 executeLastNWithAllFields(SearchParameterMap theSearchParameterMap, Integer theMaxObservationsPerCode, Integer theMaxResultsToFetch) { + List executeLastNWithAllFields(SearchParameterMap theSearchParameterMap, FhirContext theFhirContext) { try { - List responses = buildAndExecuteSearch(theSearchParameterMap, theMaxObservationsPerCode, null); + List responses = buildAndExecuteSearch(theSearchParameterMap, theFhirContext, null); List 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 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 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 buildObservationIdList(SearchResponse theSearchResponse) throws IOException { - List 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 buildObservationDocumentList(SearchResponse theSearchResponse) throws IOException { - List 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 List buildObservationList(SearchResponse theSearchResponse, Function setValue, - SearchParameterMap theSearchParameterMap, Integer theMaxResultsToFetch) throws IOException { + SearchParameterMap theSearchParameterMap, FhirContext theFhirContext, + Integer theMaxResultsToFetch) throws IOException { List 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 getObservationCodeBuckets(SearchResponse theSearchResponse) { Aggregations responseAggregations = theSearchResponse.getAggregations(); - ParsedTerms aggregatedObservationCodes = responseAggregations.get(GROUP_BY_CODE); - return aggregatedObservationCodes.getBuckets(); + return getObservationCodeBuckets(responseAggregations); } private List 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 getObservationCodeBuckets(Aggregations theObservationCodeSystemAggregations) { + List 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 subjectReferenceCriteria = new ArrayList<>(); - List> 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 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 getReferenceValues(List 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 codeSystemHashList = new ArrayList<>(); ArrayList codeOnlyList = new ArrayList<>(); ArrayList systemOnlyList = new ArrayList<>(); ArrayList textOnlyList = new ArrayList<>(); - List> andOrParams = theSearchParameterMap.get(IndexConstants.CATEGORY_SEARCH_PARAM); + List> andOrParams = theSearchParameterMap.get(categoryParamName); for (List 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 codeSystemHashList = new ArrayList<>(); ArrayList codeOnlyList = new ArrayList<>(); ArrayList systemOnlyList = new ArrayList<>(); ArrayList textOnlyList = new ArrayList<>(); - List> andOrParams = theSearchParameterMap.get(IndexConstants.CODE_SEARCH_PARAM); + List> andOrParams = theSearchParameterMap.get(codeParamName); for (List 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); - } - */ } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IElasticsearchSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IElasticsearchSvc.java index 592b6939d6d..f994dc7cf18 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IElasticsearchSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IElasticsearchSvc.java @@ -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 executeLastN(SearchParameterMap theSearchParameterMap, Integer theMaxObservationsPerCode, Integer theMaxResultsToFetch); + List executeLastN(SearchParameterMap theSearchParameterMap, FhirContext theFhirContext, Integer theMaxResultsToFetch); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IndexConstants.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IndexConstants.java deleted file mode 100644 index 535d299fd9f..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IndexConstants.java +++ /dev/null @@ -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"; - -} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java index dfa4c95bd21..48d2a12d247 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java @@ -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; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/PersistObservationIndexedSearchParamLastNR4IT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/PersistObservationIndexedSearchParamLastNR4IT.java index fd1942840da..bafdfe53fee 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/PersistObservationIndexedSearchParamLastNR4IT.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/PersistObservationIndexedSearchParamLastNR4IT.java @@ -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 observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 3, 100); + List 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 observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 10, 200); + + searchParameterMap.setLastNMax(10); + + List 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 observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 10, 200); + searchParameterMap.setLastNMax(10); + List 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 observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 10, 200); + searchParameterMap.setLastNMax(10); + + List 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 observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap,1, 200); + searchParameterMap.setLastNMax(1); + List 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()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java index 25215936831..4b16c31c095 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java @@ -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>> 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 observations = elasticsearchSvc.executeLastNWithAllFields(searchParameterMap, 3, 100); + List 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 observations = elasticsearchSvc.executeLastN(searchParameterMap, 100, 100); + List 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 observations = elasticsearchSvc.executeLastN(searchParameterMap, 100, 100); + List 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 observations = elasticsearchSvc.executeLastN(searchParameterMap, 100, 100); + List 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 observations = elasticsearchSvc.executeLastN(searchParameterMap, 100, 100); + List 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 observations = elasticsearchSvc.executeLastN(searchParameterMap, 100, 100); + searchParameterMap.setLastNMax(100); + + List 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> observationCodeMap = createdPatientObservationMap.get(subject); @@ -393,7 +409,8 @@ public class LastNElasticsearchSvcMultipleObservationsIT { @Test public void testLastNNoParamsQuery() { SearchParameterMap searchParameterMap = new SearchParameterMap(); - List observations = elasticsearchSvc.executeLastNWithAllFields(searchParameterMap, 1, 100); + searchParameterMap.setLastNMax(1); + List observations = elasticsearchSvc.executeLastNWithAllFields(searchParameterMap, myFhirContext); assertEquals(2, observations.size()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java index 416c949f6ce..e220bf96569 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java @@ -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 observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, 3, 100); + List observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); assertEquals(1, observationIdsOnly.size()); assertEquals(RESOURCEPID, observationIdsOnly.get(0)); // execute Observation search for all search fields - List observations = elasticsearchSvc.executeLastNWithAllFields(searchParameterMap, 3, 100); + List 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 codes = elasticsearchSvc.queryAllIndexedObservationCodes(1000); + List 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); diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java index c999194e926..9792226d71b 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java @@ -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()); + } + } + + }