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