{-# language FlexibleContexts #-}
{-# language MonoLocalBinds #-}
{-# language ScopedTypeVariables #-}

module Rel8.Query.Aggregate
  ( aggregate
  , aggregate1
  , aggregateU
  , countRows
  )
where

-- base
import Control.Applicative (liftA2)
import Data.Int ( Int64 )
import Prelude

-- opaleye
import qualified Opaleye.Adaptors as Opaleye
import qualified Opaleye.Aggregate as Opaleye

-- rel8
import Rel8.Aggregate (Aggregator' (Aggregator), Aggregator)
import Rel8.Aggregate.Fold (Fallback (Fallback))
import Rel8.Expr ( Expr )
import Rel8.Expr.Aggregate ( countStar )
import Rel8.Expr.Bool (true)
import Rel8.Query ( Query )
import Rel8.Query.Maybe ( optional )
import Rel8.Query.Opaleye ( mapOpaleye )
import Rel8.Table (Table)
import Rel8.Table.Maybe (fromMaybeTable)
import Rel8.Table.Opaleye (unpackspec)


-- | Apply an 'Aggregator' to all rows returned by a 'Query'. If the 'Query'
-- is empty, then a single \"fallback\" row is returned, composed of the
-- identity elements of the constituent aggregation functions.
aggregate :: (Table Expr i, Table Expr a) => Aggregator i a -> Query i -> Query a
aggregate :: forall i a.
(Table Expr i, Table Expr a) =>
Aggregator i a -> Query i -> Query a
aggregate aggregator :: Aggregator i a
aggregator@(Aggregator (Fallback a
fallback) Aggregator i a
_) =
  (MaybeTable Expr a -> a) -> Query (MaybeTable Expr a) -> Query a
forall a b. (a -> b) -> Query a -> Query b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (a -> MaybeTable Expr a -> a
forall a. Table Expr a => a -> MaybeTable Expr a -> a
fromMaybeTable a
fallback) (Query (MaybeTable Expr a) -> Query a)
-> (Query i -> Query (MaybeTable Expr a)) -> Query i -> Query a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query a -> Query (MaybeTable Expr a)
forall a. Query a -> Query (MaybeTable Expr a)
optional (Query a -> Query (MaybeTable Expr a))
-> (Query i -> Query a) -> Query i -> Query (MaybeTable Expr a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Aggregator i a -> Query i -> Query a
forall i (fold :: Fold) a.
Table Expr i =>
Aggregator' fold i a -> Query i -> Query a
aggregate1 Aggregator i a
aggregator


-- | Apply an 'Rel8.Aggregator1' to all rows returned by a 'Query'. If
-- the 'Query' is empty, then zero rows are returned.
aggregate1 :: Table Expr i => Aggregator' fold i a -> Query i -> Query a
aggregate1 :: forall i (fold :: Fold) a.
Table Expr i =>
Aggregator' fold i a -> Query i -> Query a
aggregate1 = Unpackspec i i -> Aggregator' fold i a -> Query i -> Query a
forall i (fold :: Fold) a.
Unpackspec i i -> Aggregator' fold i a -> Query i -> Query a
aggregateU Unpackspec i i
forall a. Table Expr a => Unpackspec a a
unpackspec


aggregateU :: Opaleye.Unpackspec i i -> Aggregator' fold i a -> Query i -> Query a
aggregateU :: forall i (fold :: Fold) a.
Unpackspec i i -> Aggregator' fold i a -> Query i -> Query a
aggregateU Unpackspec i i
unpack (Aggregator Fallback fold a
_ Aggregator i a
aggregator) =
  (Select i -> Select a) -> Query i -> Query a
forall a b. (Select a -> Select b) -> Query a -> Query b
mapOpaleye (Unpackspec i i -> Aggregator i a -> Select i -> Select a
forall a a' b.
Unpackspec a a' -> Aggregator a' b -> Select a -> Select b
Opaleye.aggregateExplicit Unpackspec i i
unpack Aggregator i a
aggregator)


-- | Count the number of rows returned by a query. Note that this is different
-- from @countStar@, as even if the given query yields no rows, @countRows@
-- will return @0@.
countRows :: Query a -> Query (Expr Int64)
countRows :: forall a. Query a -> Query (Expr Int64)
countRows = Aggregator (Expr Bool) (Expr Int64)
-> Query (Expr Bool) -> Query (Expr Int64)
forall i a.
(Table Expr i, Table Expr a) =>
Aggregator i a -> Query i -> Query a
aggregate Aggregator (Expr Bool) (Expr Int64)
forall (fold :: Fold) i. Aggregator' fold i (Expr Int64)
countStar (Query (Expr Bool) -> Query (Expr Int64))
-> (Query a -> Query (Expr Bool)) -> Query a -> Query (Expr Int64)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Expr Bool
true Expr Bool -> Query a -> Query (Expr Bool)
forall a b. a -> Query b -> Query a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$)