{-# language BlockArguments #-}
{-# language FlexibleContexts #-}
{-# language NamedFieldPuns #-}
{-# language ScopedTypeVariables #-}
{-# language TypeApplications #-}
{-# language TypeFamilies #-}

module Rel8.Table.Aggregate
  ( groupBy, groupByOn
  , listAgg, listAggOn, nonEmptyAgg, nonEmptyAggOn
  , listCat, listCatOn, nonEmptyCat, nonEmptyCatOn
  , filterWhere, filterWhereOptional
  , orderAggregateBy
  , optionalAggregate
  )
where

-- base
import Prelude

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

-- profunctors
import Data.Profunctor (dimap, lmap)

-- rel8
import Rel8.Aggregate
  ( Aggregator,  Aggregator' (Aggregator), Aggregator1
  , toAggregator
  )
import Rel8.Aggregate.Fold (Fallback (Fallback))
import Rel8.Expr ( Expr )
import Rel8.Expr.Aggregate
  ( filterWhereExplicit
  , groupByExprOn
  , slistAggExpr
  , slistCatExpr
  , snonEmptyAggExpr
  , snonEmptyCatExpr
  )
import Rel8.Expr.Opaleye (toColumn, toPrimExpr)
import Rel8.Order (Order (Order))
import Rel8.Schema.Dict ( Dict( Dict ) )
import Rel8.Schema.HTable (HTable, hfield, hspecs, htabulateA)
import Rel8.Schema.HTable.Vectorize (htraverseVectorP, hvectorizeA)
import Rel8.Schema.Null ( Sql )
import Rel8.Schema.Spec ( Spec( Spec, info ) )
import Rel8.Table (Table, toColumns, fromColumns)
import Rel8.Table.Eq ( EqTable, eqTable )
import Rel8.Table.List ( ListTable )
import Rel8.Table.Maybe (MaybeTable, makeMaybeTable, justTable, nothingTable)
import Rel8.Table.NonEmpty ( NonEmptyTable )
import Rel8.Table.Opaleye (ifPP)
import Rel8.Type.Eq ( DBEq )


-- | Group equal tables together. This works by aggregating each column in the
-- given table with 'groupByExpr'.
--
-- For example, if we have a table of items, we could group the items by the
-- order they belong to:
--
-- @
-- itemsByOrder :: Query (OrderId Expr, ListTable Expr (Item Expr))
-- itemsByOrder =
--   aggregate
--     do
--       orderId <- groupByOn (.orderId)
--       items <- listAgg
--       pure (orderId, items)
--     do
--       each itemSchema
-- @
groupBy :: forall a. EqTable a => Aggregator1 a a
groupBy :: forall a. EqTable a => Aggregator1 a a
groupBy = (a -> Columns a Expr)
-> (Columns a Expr -> a)
-> Aggregator' 'Semi (Columns a Expr) (Columns a Expr)
-> Aggregator' 'Semi a a
forall a b c d.
(a -> b)
-> (c -> d) -> Aggregator' 'Semi b c -> Aggregator' 'Semi a d
forall (p :: * -> Context) a b c d.
Profunctor p =>
(a -> b) -> (c -> d) -> p b c -> p a d
dimap a -> Columns a Expr
forall (context :: Context) a.
Table context a =>
a -> Columns a context
toColumns Columns a Expr -> a
forall (context :: Context) a.
Table context a =>
Columns a context -> a
fromColumns (Columns a (Dict (Sql DBEq))
-> Aggregator' 'Semi (Columns a Expr) (Columns a Expr)
forall (t :: HTable).
HTable t =>
t (Dict (Sql DBEq)) -> Aggregator1 (t Expr) (t Expr)
hgroupBy (forall a. EqTable a => Columns a (Dict (Sql DBEq))
eqTable @a))


-- | Applies 'groupBy' to the columns selected by the given function.
groupByOn :: EqTable a => (i -> a) -> Aggregator1 i a
groupByOn :: forall a i. EqTable a => (i -> a) -> Aggregator1 i a
groupByOn i -> a
f = (i -> a) -> Aggregator' 'Semi a a -> Aggregator' 'Semi i a
forall a b c.
(a -> b) -> Aggregator' 'Semi b c -> Aggregator' 'Semi a c
forall (p :: * -> Context) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap i -> a
f Aggregator' 'Semi a a
forall a. EqTable a => Aggregator1 a a
groupBy


hgroupBy :: HTable t => t (Dict (Sql DBEq)) -> Aggregator1 (t Expr) (t Expr)
hgroupBy :: forall (t :: HTable).
HTable t =>
t (Dict (Sql DBEq)) -> Aggregator1 (t Expr) (t Expr)
hgroupBy t (Dict (Sql DBEq))
eqs = (forall a. HField t a -> Aggregator' 'Semi (t Expr) (Expr a))
-> Aggregator' 'Semi (t Expr) (t Expr)
forall (t :: HTable) (m :: Context) (context :: Context).
(HTable t, Apply m) =>
(forall a. HField t a -> m (context a)) -> m (t context)
htabulateA ((forall a. HField t a -> Aggregator' 'Semi (t Expr) (Expr a))
 -> Aggregator' 'Semi (t Expr) (t Expr))
-> (forall a. HField t a -> Aggregator' 'Semi (t Expr) (Expr a))
-> Aggregator' 'Semi (t Expr) (t Expr)
forall a b. (a -> b) -> a -> b
$ \HField t a
field -> case t (Dict (Sql DBEq)) -> HField t a -> Dict (Sql DBEq) a
forall (context :: Context) a. t context -> HField t a -> context a
forall (t :: HTable) (context :: Context) a.
HTable t =>
t context -> HField t a -> context a
hfield t (Dict (Sql DBEq))
eqs HField t a
field of
  Dict (Sql DBEq) a
Dict -> (t Expr -> Expr a) -> Aggregator' 'Semi (t Expr) (Expr a)
forall a i. Sql DBEq a => (i -> Expr a) -> Aggregator1 i (Expr a)
groupByExprOn (t Expr -> HField t a -> Expr a
forall (context :: Context) a. t context -> HField t a -> context a
forall (t :: HTable) (context :: Context) a.
HTable t =>
t context -> HField t a -> context a
`hfield` HField t a
field)


-- | 'filterWhere' allows an 'Aggregator' to filter out rows from the input
-- query before considering them for aggregation. Note that because the
-- predicate supplied to 'filterWhere' could return 'Rel8.false' for every
-- row, 'filterWhere' needs an 'Aggregator' as opposed to an 'Aggregator1', so
-- that it can return a default value in such a case. For a variant of
-- 'filterWhere' that can work with 'Aggregator1's, see 'filterWhereOptional'.
filterWhere :: Table Expr a
  => (i -> Expr Bool) -> Aggregator i a -> Aggregator' fold i a
filterWhere :: forall a i (fold :: Fold).
Table Expr a =>
(i -> Expr Bool) -> Aggregator i a -> Aggregator' fold i a
filterWhere = IfPP a a
-> (i -> Expr Bool) -> Aggregator i a -> Aggregator' fold i a
forall a i (fold :: Fold).
IfPP a a
-> (i -> Expr Bool) -> Aggregator i a -> Aggregator' fold i a
filterWhereExplicit IfPP a a
forall a. Table Expr a => IfPP a a
ifPP


-- | A variant of 'filterWhere' that can be used with an 'Aggregator1'
-- (upgrading it to an 'Aggregator' in the process). It returns
-- 'nothingTable' in the case where the predicate matches zero rows.
filterWhereOptional :: Table Expr a
  => (i -> Expr Bool) -> Aggregator' fold i a -> Aggregator' fold' i (MaybeTable Expr a)
filterWhereOptional :: forall a i (fold :: Fold) (fold' :: Fold).
Table Expr a =>
(i -> Expr Bool)
-> Aggregator' fold i a -> Aggregator' fold' i (MaybeTable Expr a)
filterWhereOptional i -> Expr Bool
f (Aggregator Fallback fold a
_ Aggregator i a
aggregator) =
  Fallback fold' (MaybeTable Expr a)
-> Aggregator i (MaybeTable Expr a)
-> Aggregator' fold' i (MaybeTable Expr a)
forall (fold :: Fold) i a.
Fallback fold a -> Aggregator i a -> Aggregator' fold i a
Aggregator (MaybeTable Expr a -> Fallback fold' (MaybeTable Expr a)
forall a (fold :: Fold). a -> Fallback fold a
Fallback MaybeTable Expr a
forall a. Table Expr a => MaybeTable Expr a
nothingTable) (Aggregator i (MaybeTable Expr a)
 -> Aggregator' fold' i (MaybeTable Expr a))
-> Aggregator i (MaybeTable Expr a)
-> Aggregator' fold' i (MaybeTable Expr a)
forall a b. (a -> b) -> a -> b
$
    (FieldNullable SqlBool -> a -> MaybeTable Expr a)
-> (i -> Field SqlBool)
-> Aggregator i a
-> Aggregator i (MaybeTable Expr a)
forall b mb a.
(FieldNullable SqlBool -> b -> mb)
-> (a -> Field SqlBool) -> Aggregator a b -> Aggregator a mb
Opaleye.filterWhereInternal FieldNullable SqlBool -> a -> MaybeTable Expr a
forall a. FieldNullable SqlBool -> a -> MaybeTable Expr a
makeMaybeTable (PrimExpr -> Field SqlBool
forall (n :: Nullability) b. PrimExpr -> Field_ n b
toColumn (PrimExpr -> Field SqlBool)
-> (i -> PrimExpr) -> i -> Field SqlBool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Expr Bool -> PrimExpr
forall a. Expr a -> PrimExpr
toPrimExpr (Expr Bool -> PrimExpr) -> (i -> Expr Bool) -> i -> PrimExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. i -> Expr Bool
f) Aggregator i a
aggregator


-- | Aggregate rows into a single row containing an array of all aggregated
-- rows. This can be used to associate multiple rows with a single row, without
-- changing the over cardinality of the query. This allows you to essentially
-- return a tree-like structure from queries.
--
-- For example, if we have a table of orders and each orders contains multiple
-- items, we could aggregate the table of orders, pairing each order with its
-- items:
--
-- @
-- ordersWithItems :: Query (Order Expr, ListTable Expr (Item Expr))
-- ordersWithItems = do
--   order <- each orderSchema
--   items <- aggregate listAgg (itemsFromOrder order)
--   return (order, items)
-- @
listAgg :: Table Expr a => Aggregator' fold a (ListTable Expr a)
listAgg :: forall a (fold :: Fold).
Table Expr a =>
Aggregator' fold a (ListTable Expr a)
listAgg =
  Columns (ListTable Expr a) Expr -> ListTable Expr a
HListTable (Columns a) Expr -> ListTable Expr a
forall (context :: Context) a.
Table context a =>
Columns a context -> a
fromColumns (HListTable (Columns a) Expr -> ListTable Expr a)
-> Aggregator' fold a (HListTable (Columns a) Expr)
-> Aggregator' fold a (ListTable Expr a)
forall (f :: Context) a b. Functor f => (a -> b) -> f a -> f b
<$>
  (forall a.
 Spec a -> HField (Columns a) a -> Aggregator' fold a (Expr [a]))
-> Aggregator' fold a (HListTable (Columns a) Expr)
forall (t :: HTable) (f :: Context) (list :: Context)
       (context' :: Context).
(HTable t, Apply f, Vector list) =>
(forall a. Spec a -> HField t a -> f (context' (list a)))
-> f (HVectorize list t context')
hvectorizeA \Spec {TypeInformation (Unnullify a)
info :: forall a. Spec a -> TypeInformation (Unnullify a)
info :: TypeInformation (Unnullify a)
info} HField (Columns a) a
field ->
    (a -> Expr a)
-> Aggregator' fold (Expr a) (Expr [a])
-> Aggregator' fold a (Expr [a])
forall a b c.
(a -> b) -> Aggregator' fold b c -> Aggregator' fold a c
forall (p :: * -> Context) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap ((Columns a Expr -> HField (Columns a) a -> Expr a
forall (context :: Context) a.
Columns a context -> HField (Columns a) a -> context a
forall (t :: HTable) (context :: Context) a.
HTable t =>
t context -> HField t a -> context a
`hfield` HField (Columns a) a
field) (Columns a Expr -> Expr a) -> (a -> Columns a Expr) -> a -> Expr a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Columns a Expr
forall (context :: Context) a.
Table context a =>
a -> Columns a context
toColumns) (Aggregator' fold (Expr a) (Expr [a])
 -> Aggregator' fold a (Expr [a]))
-> Aggregator' fold (Expr a) (Expr [a])
-> Aggregator' fold a (Expr [a])
forall a b. (a -> b) -> a -> b
$ TypeInformation (Unnullify a)
-> Aggregator' fold (Expr a) (Expr [a])
forall a (fold :: Fold).
TypeInformation (Unnullify a)
-> Aggregator' fold (Expr a) (Expr [a])
slistAggExpr TypeInformation (Unnullify a)
info


-- | Applies 'listAgg' to the columns selected by the given function.
listAggOn :: Table Expr a => (i -> a) -> Aggregator' fold i (ListTable Expr a)
listAggOn :: forall a i (fold :: Fold).
Table Expr a =>
(i -> a) -> Aggregator' fold i (ListTable Expr a)
listAggOn i -> a
f = (i -> a)
-> Aggregator' fold a (ListTable Expr a)
-> Aggregator' fold i (ListTable Expr a)
forall a b c.
(a -> b) -> Aggregator' fold b c -> Aggregator' fold a c
forall (p :: * -> Context) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap i -> a
f Aggregator' fold a (ListTable Expr a)
forall a (fold :: Fold).
Table Expr a =>
Aggregator' fold a (ListTable Expr a)
listAgg


-- | Like 'listAgg', but the result is guaranteed to be a non-empty list.
nonEmptyAgg :: Table Expr a => Aggregator1 a (NonEmptyTable Expr a)
nonEmptyAgg :: forall a. Table Expr a => Aggregator1 a (NonEmptyTable Expr a)
nonEmptyAgg =
  Columns (NonEmptyTable Expr a) Expr -> NonEmptyTable Expr a
HNonEmptyTable (Columns a) Expr -> NonEmptyTable Expr a
forall (context :: Context) a.
Table context a =>
Columns a context -> a
fromColumns (HNonEmptyTable (Columns a) Expr -> NonEmptyTable Expr a)
-> Aggregator' 'Semi a (HNonEmptyTable (Columns a) Expr)
-> Aggregator' 'Semi a (NonEmptyTable Expr a)
forall (f :: Context) a b. Functor f => (a -> b) -> f a -> f b
<$>
  (forall a.
 Spec a
 -> HField (Columns a) a -> Aggregator' 'Semi a (Expr (NonEmpty a)))
-> Aggregator' 'Semi a (HNonEmptyTable (Columns a) Expr)
forall (t :: HTable) (f :: Context) (list :: Context)
       (context' :: Context).
(HTable t, Apply f, Vector list) =>
(forall a. Spec a -> HField t a -> f (context' (list a)))
-> f (HVectorize list t context')
hvectorizeA \Spec {TypeInformation (Unnullify a)
info :: forall a. Spec a -> TypeInformation (Unnullify a)
info :: TypeInformation (Unnullify a)
info} HField (Columns a) a
field ->
    (a -> Expr a)
-> Aggregator' 'Semi (Expr a) (Expr (NonEmpty a))
-> Aggregator' 'Semi a (Expr (NonEmpty a))
forall a b c.
(a -> b) -> Aggregator' 'Semi b c -> Aggregator' 'Semi a c
forall (p :: * -> Context) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap ((Columns a Expr -> HField (Columns a) a -> Expr a
forall (context :: Context) a.
Columns a context -> HField (Columns a) a -> context a
forall (t :: HTable) (context :: Context) a.
HTable t =>
t context -> HField t a -> context a
`hfield` HField (Columns a) a
field) (Columns a Expr -> Expr a) -> (a -> Columns a Expr) -> a -> Expr a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Columns a Expr
forall (context :: Context) a.
Table context a =>
a -> Columns a context
toColumns) (Aggregator' 'Semi (Expr a) (Expr (NonEmpty a))
 -> Aggregator' 'Semi a (Expr (NonEmpty a)))
-> Aggregator' 'Semi (Expr a) (Expr (NonEmpty a))
-> Aggregator' 'Semi a (Expr (NonEmpty a))
forall a b. (a -> b) -> a -> b
$ TypeInformation (Unnullify a)
-> Aggregator' 'Semi (Expr a) (Expr (NonEmpty a))
forall a.
TypeInformation (Unnullify a)
-> Aggregator1 (Expr a) (Expr (NonEmpty a))
snonEmptyAggExpr TypeInformation (Unnullify a)
info


-- | Applies 'nonEmptyAgg' to the columns selected by the given function.
nonEmptyAggOn :: Table Expr a
  => (i -> a) -> Aggregator1 i (NonEmptyTable Expr a)
nonEmptyAggOn :: forall a i.
Table Expr a =>
(i -> a) -> Aggregator1 i (NonEmptyTable Expr a)
nonEmptyAggOn i -> a
f = (i -> a)
-> Aggregator' 'Semi a (NonEmptyTable Expr a)
-> Aggregator' 'Semi i (NonEmptyTable Expr a)
forall a b c.
(a -> b) -> Aggregator' 'Semi b c -> Aggregator' 'Semi a c
forall (p :: * -> Context) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap i -> a
f Aggregator' 'Semi a (NonEmptyTable Expr a)
forall a. Table Expr a => Aggregator1 a (NonEmptyTable Expr a)
nonEmptyAgg


-- | Concatenate lists into a single list.
listCat :: Table Expr a
  => Aggregator' fold (ListTable Expr a) (ListTable Expr a)
listCat :: forall a (fold :: Fold).
Table Expr a =>
Aggregator' fold (ListTable Expr a) (ListTable Expr a)
listCat = (ListTable Expr a -> HVectorize [] (Columns a) Expr)
-> (HVectorize [] (Columns a) Expr -> ListTable Expr a)
-> Aggregator'
     fold
     (HVectorize [] (Columns a) Expr)
     (HVectorize [] (Columns a) Expr)
-> Aggregator' fold (ListTable Expr a) (ListTable Expr a)
forall a b c d.
(a -> b)
-> (c -> d) -> Aggregator' fold b c -> Aggregator' fold a d
forall (p :: * -> Context) a b c d.
Profunctor p =>
(a -> b) -> (c -> d) -> p b c -> p a d
dimap ListTable Expr a -> Columns (ListTable Expr a) Expr
ListTable Expr a -> HVectorize [] (Columns a) Expr
forall (context :: Context) a.
Table context a =>
a -> Columns a context
toColumns Columns (ListTable Expr a) Expr -> ListTable Expr a
HVectorize [] (Columns a) Expr -> ListTable Expr a
forall (context :: Context) a.
Table context a =>
Columns a context -> a
fromColumns (Aggregator'
   fold
   (HVectorize [] (Columns a) Expr)
   (HVectorize [] (Columns a) Expr)
 -> Aggregator' fold (ListTable Expr a) (ListTable Expr a))
-> Aggregator'
     fold
     (HVectorize [] (Columns a) Expr)
     (HVectorize [] (Columns a) Expr)
-> Aggregator' fold (ListTable Expr a) (ListTable Expr a)
forall a b. (a -> b) -> a -> b
$
  (forall a.
 HField (Columns a) a -> Aggregator' fold (Expr [a]) (Expr [a]))
-> Aggregator'
     fold
     (HVectorize [] (Columns a) Expr)
     (HVectorize [] (Columns a) Expr)
forall (t :: HTable) (p :: * -> Context) (f :: Context)
       (list :: Context) (g :: Context) (list' :: Context).
(HTable t, ProductProfunctor p) =>
(forall a. HField t a -> p (f (list a)) (g (list' a)))
-> p (HVectorize list t f) (HVectorize list' t g)
htraverseVectorP (\HField (Columns a) a
field -> case Columns a Spec -> HField (Columns a) a -> Spec a
forall (context :: Context) a.
Columns a context -> HField (Columns a) a -> context a
forall (t :: HTable) (context :: Context) a.
HTable t =>
t context -> HField t a -> context a
hfield Columns a Spec
forall (t :: HTable). HTable t => t Spec
hspecs HField (Columns a) a
field of
    Spec {TypeInformation (Unnullify a)
info :: forall a. Spec a -> TypeInformation (Unnullify a)
info :: TypeInformation (Unnullify a)
info} -> TypeInformation (Unnullify a)
-> Aggregator' fold (Expr [a]) (Expr [a])
forall a (fold :: Fold).
TypeInformation (Unnullify a)
-> Aggregator' fold (Expr [a]) (Expr [a])
slistCatExpr TypeInformation (Unnullify a)
info)


-- | Applies 'listCat' to the list selected by the given function.
listCatOn :: Table Expr a
  => (i -> ListTable Expr a) -> Aggregator' fold i (ListTable Expr a)
listCatOn :: forall a i (fold :: Fold).
Table Expr a =>
(i -> ListTable Expr a) -> Aggregator' fold i (ListTable Expr a)
listCatOn i -> ListTable Expr a
f = (i -> ListTable Expr a)
-> Aggregator' fold (ListTable Expr a) (ListTable Expr a)
-> Aggregator' fold i (ListTable Expr a)
forall a b c.
(a -> b) -> Aggregator' fold b c -> Aggregator' fold a c
forall (p :: * -> Context) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap i -> ListTable Expr a
f Aggregator' fold (ListTable Expr a) (ListTable Expr a)
forall a (fold :: Fold).
Table Expr a =>
Aggregator' fold (ListTable Expr a) (ListTable Expr a)
listCat


-- | Concatenate non-empty lists into a single non-empty list.
nonEmptyCat :: Table Expr a
  => Aggregator1 (NonEmptyTable Expr a) (NonEmptyTable Expr a)
nonEmptyCat :: forall a.
Table Expr a =>
Aggregator1 (NonEmptyTable Expr a) (NonEmptyTable Expr a)
nonEmptyCat = (NonEmptyTable Expr a -> HVectorize NonEmpty (Columns a) Expr)
-> (HVectorize NonEmpty (Columns a) Expr -> NonEmptyTable Expr a)
-> Aggregator'
     'Semi
     (HVectorize NonEmpty (Columns a) Expr)
     (HVectorize NonEmpty (Columns a) Expr)
-> Aggregator' 'Semi (NonEmptyTable Expr a) (NonEmptyTable Expr a)
forall a b c d.
(a -> b)
-> (c -> d) -> Aggregator' 'Semi b c -> Aggregator' 'Semi a d
forall (p :: * -> Context) a b c d.
Profunctor p =>
(a -> b) -> (c -> d) -> p b c -> p a d
dimap NonEmptyTable Expr a -> Columns (NonEmptyTable Expr a) Expr
NonEmptyTable Expr a -> HVectorize NonEmpty (Columns a) Expr
forall (context :: Context) a.
Table context a =>
a -> Columns a context
toColumns Columns (NonEmptyTable Expr a) Expr -> NonEmptyTable Expr a
HVectorize NonEmpty (Columns a) Expr -> NonEmptyTable Expr a
forall (context :: Context) a.
Table context a =>
Columns a context -> a
fromColumns (Aggregator'
   'Semi
   (HVectorize NonEmpty (Columns a) Expr)
   (HVectorize NonEmpty (Columns a) Expr)
 -> Aggregator' 'Semi (NonEmptyTable Expr a) (NonEmptyTable Expr a))
-> Aggregator'
     'Semi
     (HVectorize NonEmpty (Columns a) Expr)
     (HVectorize NonEmpty (Columns a) Expr)
-> Aggregator' 'Semi (NonEmptyTable Expr a) (NonEmptyTable Expr a)
forall a b. (a -> b) -> a -> b
$
  (forall a.
 HField (Columns a) a
 -> Aggregator' 'Semi (Expr (NonEmpty a)) (Expr (NonEmpty a)))
-> Aggregator'
     'Semi
     (HVectorize NonEmpty (Columns a) Expr)
     (HVectorize NonEmpty (Columns a) Expr)
forall (t :: HTable) (p :: * -> Context) (f :: Context)
       (list :: Context) (g :: Context) (list' :: Context).
(HTable t, ProductProfunctor p) =>
(forall a. HField t a -> p (f (list a)) (g (list' a)))
-> p (HVectorize list t f) (HVectorize list' t g)
htraverseVectorP (\HField (Columns a) a
field -> case Columns a Spec -> HField (Columns a) a -> Spec a
forall (context :: Context) a.
Columns a context -> HField (Columns a) a -> context a
forall (t :: HTable) (context :: Context) a.
HTable t =>
t context -> HField t a -> context a
hfield Columns a Spec
forall (t :: HTable). HTable t => t Spec
hspecs HField (Columns a) a
field of
    Spec {TypeInformation (Unnullify a)
info :: forall a. Spec a -> TypeInformation (Unnullify a)
info :: TypeInformation (Unnullify a)
info} -> TypeInformation (Unnullify a)
-> Aggregator' 'Semi (Expr (NonEmpty a)) (Expr (NonEmpty a))
forall a.
TypeInformation (Unnullify a)
-> Aggregator1 (Expr (NonEmpty a)) (Expr (NonEmpty a))
snonEmptyCatExpr TypeInformation (Unnullify a)
info)


-- | Applies 'nonEmptyCat' to the non-empty list selected by the given
-- function.
nonEmptyCatOn :: Table Expr a
  => (i -> NonEmptyTable Expr a) -> Aggregator1 i (NonEmptyTable Expr a)
nonEmptyCatOn :: forall a i.
Table Expr a =>
(i -> NonEmptyTable Expr a) -> Aggregator1 i (NonEmptyTable Expr a)
nonEmptyCatOn i -> NonEmptyTable Expr a
f = (i -> NonEmptyTable Expr a)
-> Aggregator' 'Semi (NonEmptyTable Expr a) (NonEmptyTable Expr a)
-> Aggregator' 'Semi i (NonEmptyTable Expr a)
forall a b c.
(a -> b) -> Aggregator' 'Semi b c -> Aggregator' 'Semi a c
forall (p :: * -> Context) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap i -> NonEmptyTable Expr a
f Aggregator' 'Semi (NonEmptyTable Expr a) (NonEmptyTable Expr a)
forall a.
Table Expr a =>
Aggregator1 (NonEmptyTable Expr a) (NonEmptyTable Expr a)
nonEmptyCat


-- | Order the values within each aggregation in an `Aggregator` using the
-- given ordering. This is only relevant for aggregations that depend on the
-- order they get their elements, like `Rel8.listAgg` and `Rel8.stringAgg`.
orderAggregateBy :: Order i -> Aggregator' fold i a -> Aggregator' fold i a
orderAggregateBy :: forall i (fold :: Fold) a.
Order i -> Aggregator' fold i a -> Aggregator' fold i a
orderAggregateBy (Order Order i
order) (Aggregator Fallback fold a
fallback Aggregator i a
aggregator) =
  Fallback fold a -> Aggregator i a -> Aggregator' fold i a
forall (fold :: Fold) i a.
Fallback fold a -> Aggregator i a -> Aggregator' fold i a
Aggregator Fallback fold a
fallback (Aggregator i a -> Aggregator' fold i a)
-> Aggregator i a -> Aggregator' fold i a
forall a b. (a -> b) -> a -> b
$ Order i -> Aggregator i a -> Aggregator i a
forall a b. Order a -> Aggregator a b -> Aggregator a b
Opaleye.orderAggregate Order i
order Aggregator i a
aggregator


-- | 'optionalAggregate' upgrades an 'Aggregator1' into an 'Aggregator' by
-- having it return 'nothingTable' when aggregating over an empty collection
-- of rows.
optionalAggregate :: Table Expr a
  => Aggregator' fold i a -> Aggregator' fold' i (MaybeTable Expr a)
optionalAggregate :: forall a (fold :: Fold) i (fold' :: Fold).
Table Expr a =>
Aggregator' fold i a -> Aggregator' fold' i (MaybeTable Expr a)
optionalAggregate = MaybeTable Expr a
-> Aggregator' fold i (MaybeTable Expr a)
-> Aggregator' fold' i (MaybeTable Expr a)
forall a (fold :: Fold) i (fold' :: Fold).
a -> Aggregator' fold i a -> Aggregator' fold' i a
toAggregator MaybeTable Expr a
forall a. Table Expr a => MaybeTable Expr a
nothingTable (Aggregator' fold i (MaybeTable Expr a)
 -> Aggregator' fold' i (MaybeTable Expr a))
-> (Aggregator' fold i a -> Aggregator' fold i (MaybeTable Expr a))
-> Aggregator' fold i a
-> Aggregator' fold' i (MaybeTable Expr a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> MaybeTable Expr a)
-> Aggregator' fold i a -> Aggregator' fold i (MaybeTable Expr a)
forall a b.
(a -> b) -> Aggregator' fold i a -> Aggregator' fold i b
forall (f :: Context) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> MaybeTable Expr a
forall a. a -> MaybeTable Expr a
justTable