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 ORDER BY t.myUpdated ASC")
085        Stream<Object[]> streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForDefaultPartition(
086                        @Param("low") Date theLow, @Param("high") Date theHigh);
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("DELETE FROM ResourceTable t WHERE t.myPid = :pid")
106        void deleteByPid(@Param("pid") JpaPid theId);
107
108        /**
109         * This method returns a Collection where each row is an element in the collection. Each element in the collection
110         * 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.
111         */
112        @Query(
113                        "SELECT t.myResourceType, t.myPid.myId, t.myDeleted, t.myPartitionIdValue, t.myPartitionDateValue FROM ResourceTable t WHERE t.myPid.myId IN (:pid)")
114        Collection<Object[]> findLookupFieldsByResourcePid(@Param("pid") List<Long> thePids);
115
116        @Query(
117                        "SELECT t.myResourceType, t.myPid.myId, t.myDeleted, t.myPartitionIdValue, t.myPartitionDateValue FROM ResourceTable t WHERE t.myPid IN (:pid)")
118        Collection<Object[]> findLookupFieldsByResourcePidWithPartitionId(@Param("pid") List<JpaPid> thePids);
119
120        /**
121         * This method returns a Collection where each row is an element in the collection. Each element in the collection
122         * 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.
123         */
124        @Query(
125                        "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")
126        Collection<Object[]> findLookupFieldsByResourcePidInPartitionIds(
127                        @Param("pid") List<Long> thePids, @Param("partition_id") Collection<Integer> thePartitionId);
128
129        /**
130         * This method returns a Collection where each row is an element in the collection. Each element in the collection
131         * 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.
132         */
133        @Query(
134                        "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)")
135        Collection<Object[]> findLookupFieldsByResourcePidInPartitionIdsOrNullPartition(
136                        @Param("pid") List<Long> thePids, @Param("partition_id") Collection<Integer> thePartitionId);
137
138        /**
139         * This method returns a Collection where each row is an element in the collection. Each element in the collection
140         * 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.
141         */
142        @Query(
143                        "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")
144        Collection<Object[]> findLookupFieldsByResourcePidInPartitionNull(@Param("pid") List<Long> thePids);
145
146        @Query("SELECT t.myVersion FROM ResourceTable t WHERE t.myPid = :pid")
147        Long findCurrentVersionByPid(@Param("pid") JpaPid thePid);
148
149        /**
150         * This query will return rows with the following values:
151         * Id (JpaPid), FhirId, ResourceType (Patient, etc), version (long)
152         * Order matters!
153         *
154         * @param pid - list of pids to get versions for
155         */
156        @Query("SELECT t.myPid, t.myResourceType, t.myFhirId, t.myVersion FROM ResourceTable t WHERE t.myPid IN ( :pid )")
157        Collection<Object[]> getResourceVersionsForPid(@Param("pid") Collection<JpaPid> pid);
158
159        @Query("SELECT t FROM ResourceTable t WHERE t.myPartitionIdValue IS NULL AND t.myPid.myId = :pid")
160        Optional<ResourceTable> readByPartitionIdNull(@Param("pid") Long theResourceId);
161
162        @Query("SELECT t FROM ResourceTable t WHERE t.myPartitionIdValue = :partitionId AND t.myPid.myId = :pid")
163        Optional<ResourceTable> readByPartitionId(
164                        @Param("partitionId") int thePartitionId, @Param("pid") Long theResourceId);
165
166        @Query(
167                        "SELECT t FROM ResourceTable t WHERE (t.myPartitionIdValue IS NULL OR t.myPartitionIdValue IN (:partitionIds)) AND t.myPid.myId = :pid")
168        Optional<ResourceTable> readByPartitionIdsOrNull(
169                        @Param("partitionIds") Collection<Integer> thrValues, @Param("pid") Long theResourceId);
170
171        @Query("SELECT t FROM ResourceTable t WHERE t.myPartitionIdValue IN (:partitionIds) AND t.myPid.myId = :pid")
172        Optional<ResourceTable> readByPartitionIds(
173                        @Param("partitionIds") Collection<Integer> thrValues, @Param("pid") Long theResourceId);
174
175        @Query("SELECT t FROM ResourceTable t WHERE t.myPid IN :pids")
176        List<ResourceTable> findAllByIdAndLoadForcedIds(@Param("pids") List<JpaPid> thePids);
177
178        @Query("SELECT t FROM ResourceTable t where t.myResourceType = :restype and t.myFhirId = :fhirId")
179        Optional<ResourceTable> findByTypeAndFhirId(
180                        @Param("restype") String theResourceName, @Param("fhirId") String theFhirId);
181
182        /**
183         * @deprecated Use {@link #findById(Object)}
184         */
185        default Optional<ResourceTable> findById(Long theId) {
186                return findById(JpaPid.fromId(theId));
187        }
188}