001package ca.uhn.fhir.jpa.bulk.export.job;
002
003/*-
004 * #%L
005 * HAPI FHIR JPA Server
006 * %%
007 * Copyright (C) 2014 - 2021 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.context.RuntimeSearchParam;
024import ca.uhn.fhir.interceptor.model.RequestPartitionId;
025import ca.uhn.fhir.jpa.api.config.DaoConfig;
026import ca.uhn.fhir.jpa.batch.log.Logs;
027import ca.uhn.fhir.jpa.dao.IResultIterator;
028import ca.uhn.fhir.jpa.dao.ISearchBuilder;
029import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
030import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
031import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
032import ca.uhn.fhir.rest.param.ReferenceParam;
033import org.slf4j.Logger;
034import org.springframework.batch.item.ItemReader;
035import org.springframework.beans.factory.annotation.Autowired;
036
037import java.util.ArrayList;
038import java.util.Iterator;
039import java.util.List;
040
041/**
042 * Bulk Item reader for the Patient Bulk Export job.
043 * Instead of performing a normal query on the resource type using type filters, we instead
044 *
045 * 1. Determine the resourcetype
046 * 2. Search for anything that has `patient-compartment-search-param:missing=false`
047 */
048public class PatientBulkItemReader extends BaseJpaBulkItemReader implements ItemReader<List<ResourcePersistentId>> {
049        @Autowired
050        private DaoConfig myDaoConfig;
051
052        private static final Logger ourLog = Logs.getBatchTroubleshootingLog();
053
054
055        private RuntimeSearchParam validateSearchParameters(SearchParameterMap expandedSpMap) {
056                RuntimeSearchParam runtimeSearchParam = getPatientSearchParamForCurrentResourceType();
057                if (expandedSpMap.get(runtimeSearchParam.getName()) != null) {
058                        throw new IllegalArgumentException(String.format("Patient Bulk Export manually modifies the Search Parameter called [%s], so you may not include this search parameter in your _typeFilter!", runtimeSearchParam.getName()));
059                }
060                return runtimeSearchParam;
061        }
062
063        @Override
064        protected Iterator<ResourcePersistentId> getResourcePidIterator() {
065                if (myDaoConfig.getIndexMissingFields() == DaoConfig.IndexEnabledEnum.DISABLED) {
066                        String errorMessage = "You attempted to start a Patient Bulk Export, but the system has `Index Missing Fields` disabled. It must be enabled for Patient Bulk Export";
067                        ourLog.error(errorMessage);
068                        throw new IllegalStateException(errorMessage);
069                }
070
071                List<ResourcePersistentId> myReadPids = new ArrayList<>();
072
073                //use _typeFilter and _since and all those fancy bits and bobs to generate our basic SP map.
074                List<SearchParameterMap> maps = createSearchParameterMapsForResourceType();
075
076                String patientSearchParam = getPatientSearchParamForCurrentResourceType().getName();
077                for (SearchParameterMap map: maps) {
078                        //Ensure users did not monkey with the patient compartment search parameter.
079                        validateSearchParameters(map);
080
081                        //Skip adding the parameter querying for patient= if we are in fact querying the patient resource type.
082                        if (!myResourceType.equalsIgnoreCase("Patient")) {
083                                map.add(patientSearchParam, new ReferenceParam().setMissing(false));
084                        }
085
086                        ourLog.debug("About to execute query {}", map.toNormalizedQueryString(myContext));
087                        ISearchBuilder sb = getSearchBuilderForLocalResourceType();
088                        IResultIterator myResultIterator = sb.createQuery(map, new SearchRuntimeDetails(null, myJobUUID), null, RequestPartitionId.allPartitions());
089
090                        while (myResultIterator.hasNext()) {
091                                myReadPids.add(myResultIterator.next());
092                        }
093                }
094                return myReadPids.iterator();
095        }
096
097
098}