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.data;
021
022import ca.uhn.fhir.jpa.dao.data.custom.IForcedIdQueries;
023import ca.uhn.fhir.jpa.model.entity.ResourceTable;
024import org.springframework.data.domain.Pageable;
025import org.springframework.data.domain.Slice;
026import org.springframework.data.jpa.repository.JpaRepository;
027import org.springframework.data.jpa.repository.Modifying;
028import org.springframework.data.jpa.repository.Query;
029import org.springframework.data.repository.query.Param;
030import org.springframework.transaction.annotation.Propagation;
031import org.springframework.transaction.annotation.Transactional;
032
033import java.util.Collection;
034import java.util.Date;
035import java.util.List;
036import java.util.Map;
037import java.util.Optional;
038import java.util.stream.Stream;
039
040@Transactional(propagation = Propagation.MANDATORY)
041public interface IResourceTableDao
042                extends JpaRepository<ResourceTable, Long>, IHapiFhirJpaRepository, IForcedIdQueries {
043
044        @Query("SELECT t.myId FROM ResourceTable t WHERE t.myDeleted IS NOT NULL")
045        Slice<Long> findIdsOfDeletedResources(Pageable thePageable);
046
047        @Query("SELECT t.myId FROM ResourceTable t WHERE t.myResourceType = :restype AND t.myDeleted IS NOT NULL")
048        Slice<Long> findIdsOfDeletedResourcesOfType(Pageable thePageable, @Param("restype") String theResourceName);
049
050        @Query(
051                        "SELECT t.myId FROM ResourceTable t WHERE t.myId = :resid AND t.myResourceType = :restype AND t.myDeleted IS NOT NULL")
052        Slice<Long> findIdsOfDeletedResourcesOfType(
053                        Pageable thePageable, @Param("resid") Long theResourceId, @Param("restype") String theResourceName);
054
055        @Query(
056                        "SELECT t.myResourceType as type, COUNT(t.myResourceType) as count FROM ResourceTable t GROUP BY t.myResourceType")
057        List<Map<?, ?>> getResourceCounts();
058
059        @Query(
060                        "SELECT t.myId FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high ORDER BY t.myUpdated DESC")
061        Slice<Long> findIdsOfResourcesWithinUpdatedRangeOrderedFromNewest(
062                        Pageable thePage, @Param("low") Date theLow, @Param("high") Date theHigh);
063
064        @Query(
065                        "SELECT t.myId FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high ORDER BY t.myUpdated ASC")
066        Slice<Long> findIdsOfResourcesWithinUpdatedRangeOrderedFromOldest(
067                        Pageable thePage, @Param("low") Date theLow, @Param("high") Date theHigh);
068
069        @Query(
070                        "SELECT t.myId, t.myResourceType, t.myUpdated FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high ORDER BY t.myUpdated ASC")
071        Stream<Object[]> streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldest(
072                        @Param("low") Date theLow, @Param("high") Date theHigh);
073
074        /**
075         * @return List of arrays containing [PID, resourceType, lastUpdated]
076         */
077        @Query(
078                        "SELECT t.myId, t.myResourceType, t.myUpdated FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high AND t.myPartitionIdValue IN (:partition_ids) ORDER BY t.myUpdated ASC")
079        Slice<Object[]> findIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForPartitionIds(
080                        Pageable thePage,
081                        @Param("low") Date theLow,
082                        @Param("high") Date theHigh,
083                        @Param("partition_ids") List<Integer> theRequestPartitionIds);
084
085        @Query(
086                        "SELECT t.myId, t.myResourceType, t.myUpdated FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high AND t.myPartitionIdValue IN (:partition_ids) ORDER BY t.myUpdated ASC")
087        Stream<Object[]> streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForPartitionIds(
088                        @Param("low") Date theLow,
089                        @Param("high") Date theHigh,
090                        @Param("partition_ids") List<Integer> theRequestPartitionIds);
091
092        /**
093         * @return List of arrays containing [PID, resourceType, lastUpdated]
094         */
095        @Query(
096                        "SELECT t.myId, t.myResourceType, t.myUpdated FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high ORDER BY t.myUpdated ASC")
097        Slice<Object[]> findIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForDefaultPartition(
098                        Pageable thePage, @Param("low") Date theLow, @Param("high") Date theHigh);
099
100        @Query(
101                        "SELECT t.myId, t.myResourceType, t.myUpdated FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high ORDER BY t.myUpdated ASC")
102        Stream<Object[]> streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForDefaultPartition(
103                        @Param("low") Date theLow, @Param("high") Date theHigh);
104
105        // TODO in the future, consider sorting by pid as well so batch jobs process in the same order across restarts
106        @Query(
107                        "SELECT t.myId FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high AND t.myPartitionIdValue = :partition_id ORDER BY t.myUpdated ASC")
108        Slice<Long> findIdsOfPartitionedResourcesWithinUpdatedRangeOrderedFromOldest(
109                        Pageable thePage,
110                        @Param("low") Date theLow,
111                        @Param("high") Date theHigh,
112                        @Param("partition_id") Integer theRequestPartitionId);
113
114        @Query(
115                        "SELECT t.myId FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high AND t.myResourceType = :restype ORDER BY t.myUpdated ASC")
116        Slice<Long> findIdsOfResourcesWithinUpdatedRangeOrderedFromOldest(
117                        Pageable thePage,
118                        @Param("restype") String theResourceType,
119                        @Param("low") Date theLow,
120                        @Param("high") Date theHigh);
121
122        @Modifying
123        @Query("UPDATE ResourceTable t SET t.myIndexStatus = :status WHERE t.myId = :id")
124        void updateIndexStatus(@Param("id") Long theId, @Param("status") Long theIndexStatus);
125
126        @Modifying
127        @Query("UPDATE ResourceTable t SET t.myUpdated = :updated WHERE t.myId = :id")
128        void updateLastUpdated(@Param("id") Long theId, @Param("updated") Date theUpdated);
129
130        @Modifying
131        @Query("DELETE FROM ResourceTable t WHERE t.myId = :pid")
132        void deleteByPid(@Param("pid") Long theId);
133
134        /**
135         * This method returns a Collection where each row is an element in the collection. Each element in the collection
136         * is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
137         */
138        @Query("SELECT t.myResourceType, t.myId, t.myDeleted FROM ResourceTable t WHERE t.myId IN (:pid)")
139        Collection<Object[]> findLookupFieldsByResourcePid(@Param("pid") List<Long> thePids);
140
141        /**
142         * This method returns a Collection where each row is an element in the collection. Each element in the collection
143         * is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
144         */
145        @Query(
146                        "SELECT t.myResourceType, t.myId, t.myDeleted FROM ResourceTable t WHERE t.myId IN (:pid) AND t.myPartitionIdValue IN :partition_id")
147        Collection<Object[]> findLookupFieldsByResourcePidInPartitionIds(
148                        @Param("pid") List<Long> thePids, @Param("partition_id") Collection<Integer> thePartitionId);
149
150        /**
151         * This method returns a Collection where each row is an element in the collection. Each element in the collection
152         * is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
153         */
154        @Query(
155                        "SELECT t.myResourceType, t.myId, t.myDeleted FROM ResourceTable t WHERE t.myId IN (:pid) AND (t.myPartitionIdValue IS NULL OR t.myPartitionIdValue IN :partition_id)")
156        Collection<Object[]> findLookupFieldsByResourcePidInPartitionIdsOrNullPartition(
157                        @Param("pid") List<Long> thePids, @Param("partition_id") Collection<Integer> thePartitionId);
158
159        /**
160         * This method returns a Collection where each row is an element in the collection. Each element in the collection
161         * is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
162         */
163        @Query(
164                        "SELECT t.myResourceType, t.myId, t.myDeleted FROM ResourceTable t WHERE t.myId IN (:pid) AND t.myPartitionIdValue IS NULL")
165        Collection<Object[]> findLookupFieldsByResourcePidInPartitionNull(@Param("pid") List<Long> thePids);
166
167        @Query("SELECT t.myVersion FROM ResourceTable t WHERE t.myId = :pid")
168        Long findCurrentVersionByPid(@Param("pid") Long thePid);
169
170        /**
171         * This query will return rows with the following values:
172         * Id (resource pid - long), ResourceType (Patient, etc), version (long)
173         * Order matters!
174         * @param pid - list of pids to get versions for
175         * @return
176         */
177        @Query("SELECT t.myId, t.myResourceType, t.myVersion FROM ResourceTable t WHERE t.myId IN ( :pid )")
178        Collection<Object[]> getResourceVersionsForPid(@Param("pid") List<Long> pid);
179
180        @Query("SELECT t FROM ResourceTable t WHERE t.myPartitionId.myPartitionId IS NULL AND t.myId = :pid")
181        Optional<ResourceTable> readByPartitionIdNull(@Param("pid") Long theResourceId);
182
183        @Query("SELECT t FROM ResourceTable t WHERE t.myPartitionId.myPartitionId = :partitionId AND t.myId = :pid")
184        Optional<ResourceTable> readByPartitionId(
185                        @Param("partitionId") int thePartitionId, @Param("pid") Long theResourceId);
186
187        @Query(
188                        "SELECT t FROM ResourceTable t WHERE (t.myPartitionId.myPartitionId IS NULL OR t.myPartitionId.myPartitionId IN (:partitionIds)) AND t.myId = :pid")
189        Optional<ResourceTable> readByPartitionIdsOrNull(
190                        @Param("partitionIds") Collection<Integer> thrValues, @Param("pid") Long theResourceId);
191
192        @Query("SELECT t FROM ResourceTable t WHERE t.myPartitionId.myPartitionId IN (:partitionIds) AND t.myId = :pid")
193        Optional<ResourceTable> readByPartitionIds(
194                        @Param("partitionIds") Collection<Integer> thrValues, @Param("pid") Long theResourceId);
195
196        @Query("SELECT t FROM ResourceTable t WHERE t.myId IN :pids")
197        List<ResourceTable> findAllByIdAndLoadForcedIds(@Param("pids") List<Long> thePids);
198
199        @Query("SELECT t FROM ResourceTable t where t.myResourceType = :restype and t.myFhirId = :fhirId")
200        Optional<ResourceTable> findByTypeAndFhirId(
201                        @Param("restype") String theResourceName, @Param("fhirId") String theFhirId);
202}