packages feed

rel8-1.3.0.0: src/Rel8/Query/Aggregate.hs

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

module Rel8.Query.Aggregate
  ( aggregate
  , countRows
  , mode
  )
where

-- base
import Data.Functor.Contravariant ( (>$<) )
import Data.Int ( Int64 )
import Prelude

-- opaleye
import qualified Opaleye.Aggregate as Opaleye

-- rel8
import Rel8.Aggregate ( Aggregates )
import Rel8.Expr ( Expr )
import Rel8.Expr.Aggregate ( countStar )
import Rel8.Expr.Order ( desc )
import Rel8.Query ( Query )
import Rel8.Query.Limit ( limit )
import Rel8.Query.Maybe ( optional )
import Rel8.Query.Opaleye ( mapOpaleye )
import Rel8.Query.Order ( orderBy )
import Rel8.Table ( toColumns )
import Rel8.Table.Aggregate ( hgroupBy )
import Rel8.Table.Cols ( Cols( Cols ), fromCols )
import Rel8.Table.Eq ( EqTable, eqTable )
import Rel8.Table.Opaleye ( aggregator )
import Rel8.Table.Maybe ( maybeTable )


-- | Apply an aggregation to all rows returned by a 'Query'.
aggregate :: Aggregates aggregates exprs => Query aggregates -> Query exprs
aggregate = mapOpaleye (Opaleye.aggregate 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 = fmap (maybeTable 0 id) . optional . aggregate . fmap (const countStar)


-- | Return the most common row in a query.
mode :: forall a. EqTable a => Query a -> Query a
mode rows = limit 1 $ fmap (fromCols . snd) $ orderBy (fst >$< desc) $ do
  aggregate $ do
    row <- toColumns <$> rows
    pure (countStar, Cols $ hgroupBy (eqTable @a) row)