persistent-2.18.0.0: Database/Persist/Class/PersistQuery.hs
{-# LANGUAGE ExplicitForAll #-}
module Database.Persist.Class.PersistQuery
( selectList
, PersistQueryRead (..)
, PersistQueryWrite (..)
, selectSource
, selectKeys
, selectKeysList
) where
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Reader (MonadReader, ReaderT)
import Control.Monad.Trans.Resource (MonadResource, release)
import Data.Acquire (Acquire, allocateAcquire, with)
import Data.Conduit (ConduitM, await, runConduit, (.|))
import qualified Data.Conduit.List as CL
import Database.Persist.Class.PersistEntity
import Database.Persist.Class.PersistStore
-- | Backends supporting conditional read operations.
class (PersistCore backend, PersistStoreRead backend) => PersistQueryRead backend where
-- | Get all records matching the given criterion in the specified order.
-- Returns also the identifiers.
--
-- NOTE: This function returns an 'Acquire' and a 'ConduitM', which implies
-- that it streams from the database. It does not. Please use 'selectList'
-- to simplify the code. If you want streaming behavior, consider
-- @persistent-pagination@ which efficiently chunks a query into ranges, or
-- investigate a backend-specific streaming solution.
selectSourceRes
:: (PersistRecordBackend record backend, MonadIO m1, MonadIO m2)
=> [Filter record]
-> [SelectOpt record]
-> ReaderT backend m1 (Acquire (ConduitM () (Entity record) m2 ()))
-- | Get just the first record for the criterion.
selectFirst
:: (MonadIO m, PersistRecordBackend record backend)
=> [Filter record]
-> [SelectOpt record]
-> ReaderT backend m (Maybe (Entity record))
selectFirst filts opts = do
srcRes <- selectSourceRes filts (LimitTo 1 : opts)
liftIO $ with srcRes (\src -> runConduit $ src .| await)
-- | Get the 'Key's of all records matching the given criterion.
selectKeysRes
:: (MonadIO m1, MonadIO m2, PersistRecordBackend record backend)
=> [Filter record]
-> [SelectOpt record]
-> ReaderT backend m1 (Acquire (ConduitM () (Key record) m2 ()))
-- | The total number of records fulfilling the given criterion.
count
:: (MonadIO m, PersistRecordBackend record backend)
=> [Filter record] -> ReaderT backend m Int
-- | Check if there is at least one record fulfilling the given criterion.
--
-- @since 2.11
exists
:: (MonadIO m, PersistRecordBackend record backend)
=> [Filter record] -> ReaderT backend m Bool
-- | Backends supporting conditional write operations
class
(PersistQueryRead backend, PersistStoreWrite backend) =>
PersistQueryWrite backend
where
-- | Update individual fields on any record matching the given criterion.
updateWhere
:: (MonadIO m, PersistRecordBackend record backend)
=> [Filter record] -> [Update record] -> ReaderT backend m ()
-- | Delete all records matching the given criterion.
deleteWhere
:: (MonadIO m, PersistRecordBackend record backend)
=> [Filter record] -> ReaderT backend m ()
-- | Get all records matching the given criterion in the specified order.
-- Returns also the identifiers.
--
-- WARNING: This function returns a 'ConduitM', which suggests that it streams
-- the results. It does not stream results on most backends. If you need
-- streaming, see @persistent-pagination@ for a means of chunking results based
-- on indexed ranges.
selectSource
:: forall record backend m
. ( PersistQueryRead backend
, MonadResource m
, PersistRecordBackend record backend
, MonadReader backend m
)
=> [Filter record]
-> [SelectOpt record]
-> ConduitM () (Entity record) m ()
selectSource filts opts = do
srcRes <- liftPersist $ selectSourceRes filts opts
(releaseKey, src) <- allocateAcquire srcRes
src
release releaseKey
-- | Get the 'Key's of all records matching the given criterion.
--
-- For an example, see 'selectList'.
selectKeys
:: forall record backend m
. ( PersistQueryRead backend
, MonadResource m
, PersistRecordBackend record backend
, MonadReader backend m
)
=> [Filter record]
-> [SelectOpt record]
-> ConduitM () (Key record) m ()
selectKeys filts opts = do
srcRes <- liftPersist $ selectKeysRes filts opts
(releaseKey, src) <- allocateAcquire srcRes
src
release releaseKey
-- | Returns a @['Entity' record]@ corresponding to the filters and options
-- provided.
--
-- Filters are constructed using the operators defined in "Database.Persist"
-- (and re-exported from "Database.Persist.Sql"). Let's look at some examples:
--
-- @
-- usersWithAgeOver40 :: 'SqlPersistT' 'IO' ['Entity' User]
-- usersWithAgeOver40 =
-- 'selectList' [UserAge 'Database.Persist.>=.' 40] []
-- @
--
-- If you provide multiple values in the list, the conditions are @AND@ed
-- together.
--
-- @
-- usersWithAgeBetween30And50 :: 'SqlPersistT' 'IO' ['Entity' User]
-- usersWithAgeBetween30And50 =
-- 'selectList'
-- [ UserAge 'Database.Persist.>=.' 30
-- , UserAge 'Database.Persist.<=.' 50
-- ]
-- []
-- @
--
-- The second list contains the 'SelectOpt' for a record. We can select the
-- first ten records with 'LimitTo'
--
-- @
-- firstTenUsers =
-- 'selectList' [] ['LimitTo' 10]
-- @
--
-- And we can select the second ten users with 'OffsetBy'.
--
-- @
-- secondTenUsers =
-- 'selectList' [] ['LimitTo' 10, 'OffsetBy' 10]
-- @
--
-- <https://use-the-index-luke.com/sql/partial-results/fetch-next-page Warning that LIMIT/OFFSET is bad for pagination!>
--
-- The type of record can usually be infered from the types of the provided filters
-- and select options. In the previous two examples, though, you'll notice that the
-- select options are polymorphic, applying to any record type. In order to help
-- type inference in such situations, or simply as an enhancement to readability,
-- you might find type application useful, illustrated below.
--
-- @
-- {-# LANGUAGE TypeApplications #-}
-- ...
--
-- firstTenUsers =
-- 'selectList' @User [] ['LimitTo' 10]
--
-- secondTenUsers =
-- 'selectList' @User [] ['LimitTo' 10, 'OffsetBy' 10]
-- @
--
-- With 'Asc' and 'Desc', we can provide the field we want to sort on. We can
-- provide multiple sort orders - later ones are used to sort records that are
-- equal on the first field.
--
-- @
-- newestUsers =
-- selectList [] ['Desc' UserCreatedAt, 'LimitTo' 10]
--
-- oldestUsers =
-- selectList [] ['Asc' UserCreatedAt, 'LimitTo' 10]
-- @
selectList
:: forall record backend m
. (MonadIO m, PersistQueryRead backend, PersistRecordBackend record backend)
=> [Filter record]
-> [SelectOpt record]
-> ReaderT backend m [Entity record]
selectList filts opts = do
srcRes <- selectSourceRes filts opts
liftIO $ with srcRes (\src -> runConduit $ src .| CL.consume)
-- | Call 'selectKeys' but return the result as a list.
selectKeysList
:: forall record backend m
. (MonadIO m, PersistQueryRead backend, PersistRecordBackend record backend)
=> [Filter record]
-> [SelectOpt record]
-> ReaderT backend m [Key record]
selectKeysList filts opts = do
srcRes <- selectKeysRes filts opts
liftIO $ with srcRes (\src -> runConduit $ src .| CL.consume)