Add documentation and remove requirement for max parameter (defaulted to 1 now).

This commit is contained in:
ianmarshall 2020-06-08 18:08:02 -04:00
parent 30dd858f17
commit c86e46b196
9 changed files with 64 additions and 13 deletions

View File

@ -47,6 +47,7 @@ page.server_jpa.search=Search
page.server_jpa.performance=Performance page.server_jpa.performance=Performance
page.server_jpa.upgrading=Upgrade Guide page.server_jpa.upgrading=Upgrade Guide
page.server_jpa.diff=Diff Operation page.server_jpa.diff=Diff Operation
page.server_jpa.lastn=LastN Operation
section.server_jpa_empi.title=JPA Server: EMPI section.server_jpa_empi.title=JPA Server: EMPI
page.server_jpa_empi.empi=Enterprise Master Patient Index page.server_jpa_empi.empi=Enterprise Master Patient Index

View File

@ -0,0 +1,41 @@
# LastN Operation
HAPI FHIR 5.1.0 introduced preliminary support for the `$lastn` operation described [here](http://hl7.org/fhir/observation-operation-lastn.html).
This implementation of the `$lastn` operation requires an external Elasticsearch server implementation which is used to implement the indexes required by this operation. The following sections describe the current functionality supported by this operation and the configuration needed to enable this operation.
# Functional Overview and Parameters
As described in the [FHIR specification](http://hl7.org/fhir/observation-operation-lastn.html), the `$lastn` can be used to retrieve the most recent or last n=number of observations for one or more subjects. This implementation supports the following search parameters:
* `subject=` or `patient=`: Identifier(s) of patient(s) to return Observation resources for. If not specified, returns most recent observations for all patients.
* `category=`: One or more category code search parameters used to filter Observations.
* `Observation.code=`: One or more `Observation.code` search parameters use to filter and group observations. If not specified, returns most recent observations for all `Observation.code` values.
* `date=`: Date search parameters used to filter Observations by `Observation.effectiveDtm`.
* `max=`: The maximum number of observations to return for each `Observation.code`. If not specified, returns only the most recent observation in each group.
# Limitations
Search parameters other than those listed above are currently not supported.
The grouping of Observation resources by `Observation.code` means that the `$lastn` operation will not work in cases where `Observation.code` has more than one coding.
# Deployment and Configuration
The `$lastn` operation is disabled by default. The operation can be enabled by setting the DaoConfig#setLastNEnabled property (see [JavaDoc](/hapi-fhir/apidocs/hapi-fhir-jpaserver-api/ca/uhn/fhir/jpa/api/config/DaoConfig.html#setLastNEnabled(boolean))).
In addition, the Elasticsearch client service, `ElasticsearchSvcImpl` will need to be instantiated with parameters specifying how to connect to the Elasticsearch server, for e.g.:
```java
@Bean()
public ElasticsearchSvcImpl elasticsearchSvc() {
String elasticsearchHost = "localhost";
String elasticsearchUserId = "elastic";
String elasticsearchPassword = "changeme";
int elasticsearchPort = 9301;
return new ElasticsearchSvcImpl(elasticsearchHost, elasticsearchPort, elasticsearchUserId, elasticsearchPassword);
}
```
See the [JavaDoc](/hapi-fhir/apidocs/hapi-fhir-jpaserver-base/ca/uhn/fhir/jpa/search/lastn/IElasticsearchSvc.html) for more information regarding the Elasticsearch client service.

View File

@ -311,9 +311,6 @@ public class SearchBuilder implements ISearchBuilder {
throw new InvalidRequestException("LastN operation is not enabled on this service, can not process this request"); throw new InvalidRequestException("LastN operation is not enabled on this service, can not process this request");
} }
} }
if(myParams.getLastNMax() == null) {
throw new InvalidRequestException("Max parameter is required for $lastn operation");
}
List<String> lastnResourceIds = myIElasticsearchSvc.executeLastN(myParams, myContext, 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));

View File

@ -89,7 +89,9 @@ public class BaseJpaResourceProviderObservationDstu3 extends JpaResourceProvider
if (theSubject != null) { if (theSubject != null) {
paramMap.add(Observation.SP_SUBJECT, theSubject); paramMap.add(Observation.SP_SUBJECT, theSubject);
} }
if (theMax != null) {
paramMap.setLastNMax(theMax.getValue()); paramMap.setLastNMax(theMax.getValue());
}
if (theCount != null) { if (theCount != null) {
paramMap.setCount(theCount.getValue()); paramMap.setCount(theCount.getValue());
} }

View File

@ -88,7 +88,9 @@ public class BaseJpaResourceProviderObservationR4 extends JpaResourceProviderR4<
if (theSubject != null) { if (theSubject != null) {
paramMap.add(Observation.SP_SUBJECT, theSubject); paramMap.add(Observation.SP_SUBJECT, theSubject);
} }
if(theMax != null) {
paramMap.setLastNMax(theMax.getValue()); paramMap.setLastNMax(theMax.getValue());
}
if (theCount != null) { if (theCount != null) {
paramMap.setCount(theCount.getValue()); paramMap.setCount(theCount.getValue());
} }

View File

@ -89,7 +89,9 @@ public class BaseJpaResourceProviderObservationR5 extends JpaResourceProviderR5<
if (theSubject != null) { if (theSubject != null) {
paramMap.add(Observation.SP_SUBJECT, theSubject); paramMap.add(Observation.SP_SUBJECT, theSubject);
} }
if (theMax != null) {
paramMap.setLastNMax(theMax.getValue()); paramMap.setLastNMax(theMax.getValue());
}
if (theCount != null) { if (theCount != null) {
paramMap.setCount(theCount.getValue()); paramMap.setCount(theCount.getValue());
} }

View File

@ -97,7 +97,6 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
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"; 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) {
myRestHighLevelClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient(theHostname, thePort, theUsername, thePassword); myRestHighLevelClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient(theHostname, thePort, theUsername, thePassword);
@ -175,7 +174,7 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
break; break;
} }
SearchRequest myLastNRequest = buildObservationsSearchRequest(subject, theSearchParameterMap, theFhirContext, SearchRequest myLastNRequest = buildObservationsSearchRequest(subject, theSearchParameterMap, theFhirContext,
createObservationSubjectAggregationBuilder(theSearchParameterMap.getLastNMax(), topHitsInclude)); createObservationSubjectAggregationBuilder(getMaxParameter(theSearchParameterMap), topHitsInclude));
try { try {
SearchResponse lastnResponse = executeSearchRequest(myLastNRequest); SearchResponse lastnResponse = executeSearchRequest(myLastNRequest);
searchResults.addAll(buildObservationList(lastnResponse, setValue, theSearchParameterMap, theFhirContext, searchResults.addAll(buildObservationList(lastnResponse, setValue, theSearchParameterMap, theFhirContext,
@ -186,7 +185,7 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
} }
} else { } else {
SearchRequest myLastNRequest = buildObservationsSearchRequest(theSearchParameterMap, theFhirContext, SearchRequest myLastNRequest = buildObservationsSearchRequest(theSearchParameterMap, theFhirContext,
createObservationCodeAggregationBuilder(theSearchParameterMap.getLastNMax(), topHitsInclude)); createObservationCodeAggregationBuilder(getMaxParameter(theSearchParameterMap), topHitsInclude));
try { try {
SearchResponse lastnResponse = executeSearchRequest(myLastNRequest); SearchResponse lastnResponse = executeSearchRequest(myLastNRequest);
searchResults.addAll(buildObservationList(lastnResponse, setValue, theSearchParameterMap, theFhirContext, searchResults.addAll(buildObservationList(lastnResponse, setValue, theSearchParameterMap, theFhirContext,
@ -198,6 +197,14 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
return searchResults; return searchResults;
} }
private int getMaxParameter(SearchParameterMap theSearchParameterMap) {
if (theSearchParameterMap.getLastNMax() == null) {
return 1;
} else {
return theSearchParameterMap.getLastNMax();
}
}
private List<String> getSubjectReferenceCriteria(String thePatientParamName, String theSubjectParamName, SearchParameterMap theSearchParameterMap) { private List<String> getSubjectReferenceCriteria(String thePatientParamName, String theSubjectParamName, SearchParameterMap theSearchParameterMap) {
List<String> subjectReferenceCriteria = new ArrayList<>(); List<String> subjectReferenceCriteria = new ArrayList<>();
@ -231,7 +238,7 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc {
return referenceList; return referenceList;
} }
private CompositeAggregationBuilder createObservationSubjectAggregationBuilder(int theMaxNumberObservationsPerCode, String[] theTopHitsInclude) { private CompositeAggregationBuilder createObservationSubjectAggregationBuilder(Integer theMaxNumberObservationsPerCode, String[] theTopHitsInclude) {
CompositeValuesSourceBuilder<?> subjectValuesBuilder = new TermsValuesSourceBuilder("subject").field("subject"); CompositeValuesSourceBuilder<?> subjectValuesBuilder = new TermsValuesSourceBuilder("subject").field("subject");
List<CompositeValuesSourceBuilder<?>> compositeAggSubjectSources = new ArrayList<>(); List<CompositeValuesSourceBuilder<?>> compositeAggSubjectSources = new ArrayList<>();
compositeAggSubjectSources.add(subjectValuesBuilder); compositeAggSubjectSources.add(subjectValuesBuilder);

View File

@ -12,13 +12,13 @@
"type" : "keyword" "type" : "keyword"
}, },
"codingdisplay" : { "codingdisplay" : {
"type" : "text" "type" : "keyword"
}, },
"codingsystem" : { "codingsystem" : {
"type" : "keyword" "type" : "keyword"
}, },
"text" : { "text" : {
"type" : "text" "type" : "keyword"
} }
} }
} }

View File

@ -208,7 +208,6 @@ public class BaseR4SearchLastN extends BaseJpaTest {
public void testLastNNoPatients() { public void testLastNNoPatients() {
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
params.setLastNMax(1);
params.setLastN(true); params.setLastN(true);
Map<String, String[]> requestParameters = new HashMap<>(); Map<String, String[]> requestParameters = new HashMap<>();