001/*-
002 * #%L
003 * HAPI FHIR Storage api
004 * %%
005 * Copyright (C) 2014 - 2025 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;
021
022import ca.uhn.fhir.context.FhirContext;
023import ca.uhn.fhir.interceptor.model.RequestPartitionId;
024import ca.uhn.fhir.jpa.model.search.SearchBuilderLoadIncludesParameters;
025import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
026import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
027import ca.uhn.fhir.model.api.Include;
028import ca.uhn.fhir.rest.api.server.RequestDetails;
029import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
030import ca.uhn.fhir.rest.param.DateRangeParam;
031import com.google.common.collect.Streams;
032import jakarta.annotation.Nonnull;
033import jakarta.persistence.EntityManager;
034import org.apache.commons.io.IOUtils;
035import org.hl7.fhir.instance.model.api.IBaseResource;
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038
039import java.util.ArrayList;
040import java.util.Collection;
041import java.util.List;
042import java.util.Set;
043import java.util.stream.Stream;
044
045public interface ISearchBuilder<T extends IResourcePersistentId<?>> {
046        static final Logger ourLog = LoggerFactory.getLogger(ISearchBuilder.class);
047
048        String SEARCH_BUILDER_BEAN_NAME = "SearchBuilder";
049
050        IResultIterator<T> createQuery(
051                        SearchParameterMap theParams,
052                        SearchRuntimeDetails theSearchRuntime,
053                        RequestDetails theRequest,
054                        @Nonnull RequestPartitionId theRequestPartitionId);
055
056        /**
057         * Stream equivalent of createQuery.
058         * Note: the Stream must be closed.
059         */
060        default Stream<T> createQueryStream(
061                        SearchParameterMap theParams,
062                        SearchRuntimeDetails theSearchRuntime,
063                        RequestDetails theRequest,
064                        @Nonnull RequestPartitionId theRequestPartitionId) {
065                IResultIterator<T> iter = createQuery(theParams, theSearchRuntime, theRequest, theRequestPartitionId);
066                // Adapt IResultIterator to stream
067                Stream<T> stream = Streams.stream(iter);
068                // The iterator might have an open ResultSet. Connect the close handler.
069                return stream.onClose(() -> IOUtils.closeQuietly(iter));
070        }
071
072        Long createCountQuery(
073                        SearchParameterMap theParams,
074                        String theSearchUuid,
075                        RequestDetails theRequest,
076                        RequestPartitionId theRequestPartitionId);
077
078        void setMaxResultsToFetch(Integer theMaxResultsToFetch);
079
080        void setDeduplicateInDatabase(boolean theShouldDeduplicateInDB);
081
082        void setRequireTotal(boolean theRequireTotal);
083
084        /**
085         * True if the results should have a 'total' value
086         */
087        boolean requiresTotal();
088
089        void loadResourcesByPid(
090                        Collection<T> thePids,
091                        Collection<T> theIncludedPids,
092                        List<IBaseResource> theResourceListToPopulate,
093                        boolean theForHistoryOperation,
094                        RequestDetails theDetails);
095
096        default List<IBaseResource> loadResourcesByPid(Collection<T> thePids, RequestDetails theDetails) {
097                ArrayList<IBaseResource> result = new ArrayList<>();
098                loadResourcesByPid(thePids, List.of(), result, false, theDetails);
099                if (result.size() != thePids.size()) {
100                        ourLog.warn("Only found {} resources for {} pids", result.size(), thePids.size());
101                }
102                return result;
103        }
104
105        /**
106         * Use the loadIncludes that takes a parameters object instead.
107         */
108        @Deprecated
109        Set<T> loadIncludes(
110                        FhirContext theContext,
111                        EntityManager theEntityManager,
112                        Collection<T> theMatches,
113                        Collection<Include> theRevIncludes,
114                        boolean theReverseMode,
115                        DateRangeParam theLastUpdated,
116                        String theSearchIdOrDescription,
117                        RequestDetails theRequest,
118                        Integer theMaxCount);
119
120        default Set<T> loadIncludes(SearchBuilderLoadIncludesParameters<T> theParameters) {
121                return this.loadIncludes(
122                                theParameters.getFhirContext(),
123                                theParameters.getEntityManager(),
124                                theParameters.getMatches(),
125                                theParameters.getIncludeFilters(),
126                                theParameters.isReverseMode(),
127                                theParameters.getLastUpdated(),
128                                theParameters.getSearchIdOrDescription(),
129                                theParameters.getRequestDetails(),
130                                theParameters.getMaxCount());
131        }
132
133        /**
134         * How many results may be fetched at once
135         */
136        void setFetchSize(int theFetchSize);
137
138        void setPreviouslyAddedResourcePids(List<T> thePreviouslyAddedResourcePids);
139}