001/*-
002 * #%L
003 * HAPI FHIR Storage api
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;
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 loadResourcesByPid(
081                        Collection<T> thePids,
082                        Collection<T> theIncludedPids,
083                        List<IBaseResource> theResourceListToPopulate,
084                        boolean theForHistoryOperation,
085                        RequestDetails theDetails);
086
087        default List<IBaseResource> loadResourcesByPid(Collection<T> thePids, RequestDetails theDetails) {
088                ArrayList<IBaseResource> result = new ArrayList<>();
089                loadResourcesByPid(thePids, List.of(), result, false, theDetails);
090                if (result.size() != thePids.size()) {
091                        ourLog.warn("Only found {} resources for {} pids", result.size(), thePids.size());
092                }
093                return result;
094        }
095
096        /**
097         * Use the loadIncludes that takes a parameters object instead.
098         */
099        @Deprecated
100        Set<T> loadIncludes(
101                        FhirContext theContext,
102                        EntityManager theEntityManager,
103                        Collection<T> theMatches,
104                        Collection<Include> theRevIncludes,
105                        boolean theReverseMode,
106                        DateRangeParam theLastUpdated,
107                        String theSearchIdOrDescription,
108                        RequestDetails theRequest,
109                        Integer theMaxCount);
110
111        default Set<T> loadIncludes(SearchBuilderLoadIncludesParameters<T> theParameters) {
112                return this.loadIncludes(
113                                theParameters.getFhirContext(),
114                                theParameters.getEntityManager(),
115                                theParameters.getMatches(),
116                                theParameters.getIncludeFilters(),
117                                theParameters.isReverseMode(),
118                                theParameters.getLastUpdated(),
119                                theParameters.getSearchIdOrDescription(),
120                                theParameters.getRequestDetails(),
121                                theParameters.getMaxCount());
122        }
123
124        /**
125         * How many results may be fetched at once
126         */
127        void setFetchSize(int theFetchSize);
128
129        void setPreviouslyAddedResourcePids(List<T> thePreviouslyAddedResourcePids);
130}