001/*- 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.dao.search; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.jpa.model.entity.ResourceTable; 024import ca.uhn.fhir.jpa.model.entity.StorageSettings; 025import ca.uhn.fhir.jpa.model.search.ExtendedHSearchBuilderConsumeAdvancedQueryClausesParams; 026import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; 027import ca.uhn.fhir.jpa.searchparam.util.LastNParameterHelper; 028import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; 029import com.google.gson.JsonObject; 030import org.hibernate.search.backend.elasticsearch.ElasticsearchExtension; 031import org.hibernate.search.engine.search.aggregation.AggregationKey; 032import org.hibernate.search.engine.search.query.SearchResult; 033import org.hibernate.search.mapper.orm.session.SearchSession; 034 035import java.util.List; 036import java.util.Objects; 037 038public class LastNOperation { 039 public static final String OBSERVATION_RES_TYPE = "Observation"; 040 private final SearchSession mySession; 041 private final FhirContext myFhirContext; 042 private final StorageSettings myStorageSettings; 043 private final ISearchParamRegistry mySearchParamRegistry; 044 private final ExtendedHSearchSearchBuilder myExtendedHSearchSearchBuilder = new ExtendedHSearchSearchBuilder(); 045 046 public LastNOperation( 047 SearchSession theSession, 048 FhirContext theFhirContext, 049 StorageSettings theStorageSettings, 050 ISearchParamRegistry theSearchParamRegistry) { 051 mySession = theSession; 052 myFhirContext = theFhirContext; 053 myStorageSettings = theStorageSettings; 054 mySearchParamRegistry = theSearchParamRegistry; 055 } 056 057 public List<Long> executeLastN(SearchParameterMap theParams, Integer theMaximumResults) { 058 boolean lastNGroupedBySubject = isLastNGroupedBySubject(theParams); 059 LastNAggregation lastNAggregation = 060 new LastNAggregation(getLastNMaxParamValue(theParams), lastNGroupedBySubject); 061 AggregationKey<JsonObject> observationsByCodeKey = AggregationKey.of("lastN_aggregation"); 062 063 SearchResult<ResourceTable> result = mySession 064 .search(ResourceTable.class) 065 .extension(ElasticsearchExtension.get()) 066 .where(f -> f.bool(b -> { 067 // Must match observation type 068 b.must(f.match().field("myResourceType").matching(OBSERVATION_RES_TYPE)); 069 ExtendedHSearchClauseBuilder builder = 070 new ExtendedHSearchClauseBuilder(myFhirContext, myStorageSettings, b, f); 071 ExtendedHSearchBuilderConsumeAdvancedQueryClausesParams params = 072 new ExtendedHSearchBuilderConsumeAdvancedQueryClausesParams(); 073 params.setResourceType(OBSERVATION_RES_TYPE) 074 .setSearchParameterMap(theParams.clone()) 075 .setSearchParamRegistry(mySearchParamRegistry); 076 myExtendedHSearchSearchBuilder.addAndConsumeAdvancedQueryClauses(builder, params); 077 })) 078 .aggregation(observationsByCodeKey, f -> f.fromJson(lastNAggregation.toAggregation())) 079 .fetch(0); 080 081 JsonObject resultAggregation = result.aggregation(observationsByCodeKey); 082 List<Long> pidList = lastNAggregation.extractResourceIds(resultAggregation); 083 if (theMaximumResults != null && theMaximumResults <= pidList.size()) { 084 return pidList.subList(0, theMaximumResults); 085 } 086 return pidList; 087 } 088 089 private boolean isLastNGroupedBySubject(SearchParameterMap theParams) { 090 String patientParamName = LastNParameterHelper.getPatientParamName(myFhirContext); 091 String subjectParamName = LastNParameterHelper.getSubjectParamName(myFhirContext); 092 return theParams.containsKey(patientParamName) || theParams.containsKey(subjectParamName); 093 } 094 095 private int getLastNMaxParamValue(SearchParameterMap theParams) { 096 return Objects.isNull(theParams.getLastNMax()) ? 1 : theParams.getLastNMax(); 097 } 098}