module Database.Persist.Sql.Lifted.Expression.ArrayAggregate.PostgreSQL
  ( AggMode (..)
  , arrayAggDistinct
  , arrayAgg
  , arrayAggWith
  , arrayRemove
  , arrayRemoveNull
  , maybeArray
  , arrayAggById
  , arrayAggByIdMaybe
  , arrayAggByMaybe
  , arrayAggBy
  ) where

import Prelude

import Database.Esqueleto.Experimental
  ( Entity
  , EntityField
  , PersistEntity
  , PersistField
  , SqlExpr
  , Value
  , asc
  , persistIdField
  , (^.)
  )
import Database.Esqueleto.Internal.Internal
  ( (??.)
  )
import Database.Esqueleto.PostgreSQL
  ( AggMode (..)
  , arrayAgg
  , arrayAggDistinct
  , arrayAggWith
  , arrayRemove
  , arrayRemoveNull
  , maybeArray
  )

-- | Aggregrate the given column with stable ordering (by ID)
--
-- This ensures that if you aggregrate two columns:
--
-- @
-- pure
--   ( 'arrayAggById' students StudentFirstName
--   , 'arrayAggById' students StudentLastName
--   )
-- @
--
-- The list, if zipped, will be as expected.
--
-- See <https://stackoverflow.com/a/7317520>.
--
-- Also replaces the 'Maybe' result with an empty list, because really that's
-- what you always want.
arrayAggById
  :: forall val typ
   . (PersistEntity val, PersistField [typ], PersistField typ)
  => SqlExpr (Entity val)
  -> EntityField val typ
  -> SqlExpr (Value [typ])
arrayAggById :: forall val typ.
(PersistEntity val, PersistField [typ], PersistField typ) =>
SqlExpr (Entity val)
-> EntityField val typ -> SqlExpr (Value [typ])
arrayAggById SqlExpr (Entity val)
es EntityField val typ
f = SqlExpr (Value typ)
-> SqlExpr (Value (Key val)) -> SqlExpr (Value [typ])
forall a b.
(PersistField [a], PersistField a, PersistField b) =>
SqlExpr (Value a) -> SqlExpr (Value b) -> SqlExpr (Value [a])
arrayAggBy (SqlExpr (Entity val)
es SqlExpr (Entity val) -> EntityField val typ -> SqlExpr (Value typ)
forall typ val.
(PersistEntity val, PersistField typ) =>
SqlExpr (Entity val) -> EntityField val typ -> SqlExpr (Value typ)
^. EntityField val typ
f) (SqlExpr (Entity val)
es SqlExpr (Entity val)
-> EntityField val (Key val) -> SqlExpr (Value (Key val))
forall typ val.
(PersistEntity val, PersistField typ) =>
SqlExpr (Entity val) -> EntityField val typ -> SqlExpr (Value typ)
^. EntityField val (Key val)
forall record.
PersistEntity record =>
EntityField record (Key record)
persistIdField)

-- | 'arrayAggById' but for a left-outer-joined entity
--
-- If you're using '(?.)' instead of '(^.)', use this instead of 'arrayAggById'.
arrayAggByIdMaybe
  :: forall val typ
   . (PersistEntity val, PersistField typ)
  => SqlExpr (Maybe (Entity val))
  -> EntityField val typ
  -> SqlExpr (Value [typ])
arrayAggByIdMaybe :: forall val typ.
(PersistEntity val, PersistField typ) =>
SqlExpr (Maybe (Entity val))
-> EntityField val typ -> SqlExpr (Value [typ])
arrayAggByIdMaybe SqlExpr (Maybe (Entity val))
es EntityField val typ
f =
  SqlExpr (Value [Maybe typ]) -> SqlExpr (Value [typ])
forall a. SqlExpr (Value [Maybe a]) -> SqlExpr (Value [a])
arrayRemoveNull (SqlExpr (Value [Maybe typ]) -> SqlExpr (Value [typ]))
-> SqlExpr (Value [Maybe typ]) -> SqlExpr (Value [typ])
forall a b. (a -> b) -> a -> b
$ SqlExpr (Value (Maybe typ))
-> SqlExpr (Value (Maybe (Key val))) -> SqlExpr (Value [Maybe typ])
forall a b.
(PersistField [a], PersistField a, PersistField b) =>
SqlExpr (Value a) -> SqlExpr (Value b) -> SqlExpr (Value [a])
arrayAggBy (SqlExpr (Maybe (Entity val))
es SqlExpr (Maybe (Entity val))
-> EntityField val typ -> SqlExpr (Value (Maybe typ))
forall val typ.
(PersistEntity val, PersistField typ) =>
SqlExpr (Maybe (Entity val))
-> EntityField val typ -> SqlExpr (Value (Maybe typ))
??. EntityField val typ
f) (SqlExpr (Maybe (Entity val))
es SqlExpr (Maybe (Entity val))
-> EntityField val (Key val) -> SqlExpr (Value (Maybe (Key val)))
forall val typ.
(PersistEntity val, PersistField typ) =>
SqlExpr (Maybe (Entity val))
-> EntityField val typ -> SqlExpr (Value (Maybe typ))
??. EntityField val (Key val)
forall record.
PersistEntity record =>
EntityField record (Key record)
persistIdField)

-- | 'arrayAggBy' but for a left-outer-joined entity
--
-- If you're using '(?.)' instead of '(^.)', use this instead of 'arrayAggBy'.
arrayAggByMaybe
  :: forall a b
   . (PersistField a, PersistField b)
  => SqlExpr (Value (Maybe a))
  -> SqlExpr (Value b)
  -> SqlExpr (Value [a])
arrayAggByMaybe :: forall a b.
(PersistField a, PersistField b) =>
SqlExpr (Value (Maybe a))
-> SqlExpr (Value b) -> SqlExpr (Value [a])
arrayAggByMaybe SqlExpr (Value (Maybe a))
a = SqlExpr (Value [Maybe a]) -> SqlExpr (Value [a])
forall a. SqlExpr (Value [Maybe a]) -> SqlExpr (Value [a])
arrayRemoveNull (SqlExpr (Value [Maybe a]) -> SqlExpr (Value [a]))
-> (SqlExpr (Value b) -> SqlExpr (Value [Maybe a]))
-> SqlExpr (Value b)
-> SqlExpr (Value [a])
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SqlExpr (Value (Maybe a))
-> SqlExpr (Value b) -> SqlExpr (Value [Maybe a])
forall a b.
(PersistField [a], PersistField a, PersistField b) =>
SqlExpr (Value a) -> SqlExpr (Value b) -> SqlExpr (Value [a])
arrayAggBy SqlExpr (Value (Maybe a))
a

arrayAggBy
  :: forall a b
   . (PersistField [a], PersistField a, PersistField b)
  => SqlExpr (Value a)
  -> SqlExpr (Value b)
  -> SqlExpr (Value [a])
arrayAggBy :: forall a b.
(PersistField [a], PersistField a, PersistField b) =>
SqlExpr (Value a) -> SqlExpr (Value b) -> SqlExpr (Value [a])
arrayAggBy SqlExpr (Value a)
a SqlExpr (Value b)
b = SqlExpr (Value (Maybe [a])) -> SqlExpr (Value [a])
forall a.
(PersistField a, PersistField [a]) =>
SqlExpr (Value (Maybe [a])) -> SqlExpr (Value [a])
maybeArray (SqlExpr (Value (Maybe [a])) -> SqlExpr (Value [a]))
-> SqlExpr (Value (Maybe [a])) -> SqlExpr (Value [a])
forall a b. (a -> b) -> a -> b
$ AggMode
-> SqlExpr (Value a)
-> [OrderByClause]
-> SqlExpr (Value (Maybe [a]))
forall a.
AggMode
-> SqlExpr (Value a)
-> [OrderByClause]
-> SqlExpr (Value (Maybe [a]))
arrayAggWith AggMode
AggModeAll SqlExpr (Value a)
a [SqlExpr (Value b) -> OrderByClause
forall a. PersistField a => SqlExpr (Value a) -> OrderByClause
asc SqlExpr (Value b)
b]