Added support for (^.) and (?.) to aggregated entities. Allow grouping on Maybe Entity
This commit is contained in:
parent
b2a94c9e49
commit
65ac3c7e5a
@ -1,42 +1,69 @@
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE FunctionalDependencies #-}
|
||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE RankNTypes #-}
|
||||
{-# LANGUAGE UndecidableInstances #-}
|
||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE RankNTypes #-}
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
{-# LANGUAGE UndecidableInstances #-}
|
||||
|
||||
module Database.Esqueleto.Experimental.Aggregates
|
||||
where
|
||||
|
||||
import Control.Monad.IO.Class
|
||||
import qualified Control.Monad.Trans.Writer as W
|
||||
import Data.Coerce (Coercible, coerce)
|
||||
import Database.Esqueleto.Internal.Internal
|
||||
( GroupByClause(..)
|
||||
, SideData(..)
|
||||
, SqlExpr(..)
|
||||
, SqlQuery(..)
|
||||
, SqlSelect(..)
|
||||
, ToSomeValues(..)
|
||||
, Value(..)
|
||||
, select
|
||||
, unsafeSqlFunction
|
||||
)
|
||||
import Database.Esqueleto.Internal.PersistentImport (Entity, SqlReadT)
|
||||
import Control.Monad.IO.Class
|
||||
import qualified Control.Monad.Trans.Writer as W
|
||||
import Data.Coerce (Coercible,
|
||||
coerce)
|
||||
import Database.Esqueleto.Internal.Internal (EntityTy,
|
||||
EntityTyToValueTy,
|
||||
GroupByClause (..),
|
||||
MaybeEntityTy,
|
||||
MaybeEntityTyToMaybeValueTy,
|
||||
MaybeValueTy,
|
||||
MaybeValueTyToMaybeEntityTy,
|
||||
SideData (..),
|
||||
SqlExpr (..),
|
||||
SqlQuery (..),
|
||||
SqlSelect (..),
|
||||
ToSomeValues (..),
|
||||
UnMaybeTy,
|
||||
Value (..),
|
||||
ValueTy,
|
||||
ValueTyToEntityTy,
|
||||
noMeta, select,
|
||||
unsafeSqlFunction,
|
||||
(?.), (^.))
|
||||
import Database.Esqueleto.Internal.PersistentImport (Entity,
|
||||
EntityField,
|
||||
PersistEntity,
|
||||
PersistField,
|
||||
SqlReadT)
|
||||
|
||||
-- Phantom data type that doesn't admit a SqlSelect forcing the use of selectAggregate
|
||||
data Aggregate a
|
||||
|
||||
test :: Integral n
|
||||
=> SqlExpr (Value a)
|
||||
-> SqlExpr (Value b)
|
||||
-> SqlExpr (Value c)
|
||||
-> SqlQuery (SqlExpr (Value a), SqlExpr (Value b), SqlExpr (Value n))
|
||||
test x y other =
|
||||
groupBy (x, y) $ \(x', y') ->
|
||||
pure (x', y', sum_ other)
|
||||
type instance EntityTy (Aggregate (Entity ent)) ent = Entity ent
|
||||
type instance ValueTy (Aggregate (Value val)) val = Value val
|
||||
type instance EntityTyToValueTy (Aggregate (Entity ent)) val = Aggregate (Value val)
|
||||
type instance ValueTyToEntityTy (Aggregate (Value val)) ent = Aggregate (Entity ent)
|
||||
|
||||
type instance MaybeEntityTy (Aggregate (Maybe (Entity ent))) ent = Maybe (Entity ent)
|
||||
type instance MaybeValueTy (Aggregate (Value (Maybe val))) val = Value (Maybe val)
|
||||
type instance MaybeEntityTyToMaybeValueTy (Aggregate (Maybe (Entity ent))) val = Aggregate (Value (Maybe val))
|
||||
type instance MaybeValueTyToMaybeEntityTy (Aggregate (Value (Maybe val))) ent = Aggregate (Maybe (Entity ent))
|
||||
type instance UnMaybeTy (Aggregate (Value (Maybe val))) = Aggregate (Value val)
|
||||
type instance UnMaybeTy (Aggregate (Maybe (Entity ent))) = Aggregate (Entity ent)
|
||||
|
||||
test ent field y other = do
|
||||
groupBy (ent, y) $ \(ent', y') ->
|
||||
pure (ent' ?. field, y', sum_ other, countRows_)
|
||||
|
||||
class CountRowsFn a where
|
||||
countRows_ :: SqlExpr a
|
||||
countRows_ = ERaw noMeta $ \_ _ -> ("COUNT(*)", [])
|
||||
instance Integral n => CountRowsFn (Value n)
|
||||
instance Integral n => CountRowsFn (Aggregate (Value n))
|
||||
|
||||
-- Tuple magic, only SqlExprs are on the leaves.
|
||||
-- The Coercible instance from the SqlExpr a -> SqlExpr b allows 0 cost casting
|
||||
@ -45,8 +72,10 @@ class Coercible a r => Aggregateable a r | a -> r, r -> a where
|
||||
toAggregate = coerce
|
||||
fromAggregate :: r -> a
|
||||
fromAggregate = coerce
|
||||
instance Aggregateable () () where
|
||||
instance Aggregateable (SqlExpr (Value a)) (SqlExpr (Aggregate (Value a))) where
|
||||
instance Aggregateable (SqlExpr (Entity a)) (SqlExpr (Aggregate (Entity a))) where
|
||||
instance Aggregateable (SqlExpr (Maybe (Entity a))) (SqlExpr (Aggregate (Maybe (Entity a)))) where
|
||||
instance (Aggregateable a ra, Aggregateable b rb) => Aggregateable (a,b) (ra, rb) where
|
||||
instance
|
||||
( Aggregateable a ra
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# language DerivingStrategies, GeneralizedNewtypeDeriving #-}
|
||||
{-# LANGUAGE ConstraintKinds #-}
|
||||
{-# LANGUAGE QuantifiedConstraints #-}
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
{-# LANGUAGE EmptyDataDecls #-}
|
||||
{-# LANGUAGE FlexibleContexts #-}
|
||||
@ -14,6 +15,7 @@
|
||||
{-# LANGUAGE ScopedTypeVariables #-}
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
{-# LANGUAGE UndecidableInstances #-}
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
|
||||
-- | This is an internal module, anything exported by this module
|
||||
-- may change without a major version bump. Please use only
|
||||
@ -23,6 +25,7 @@
|
||||
-- tracker so we can safely support it.
|
||||
module Database.Esqueleto.Internal.Internal where
|
||||
|
||||
import Data.Kind (Constraint)
|
||||
import Control.Applicative ((<|>))
|
||||
import Data.Coerce (coerce)
|
||||
import Control.Arrow (first, (***))
|
||||
@ -532,15 +535,31 @@ subSelectForeign expr foreignKey k =
|
||||
subSelectUnsafe :: PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (Value a)
|
||||
subSelectUnsafe = sub SELECT
|
||||
|
||||
-- | Project a field of an entity.
|
||||
(^.)
|
||||
:: forall typ val. (PersistEntity val, PersistField typ)
|
||||
=> SqlExpr (Entity val)
|
||||
-> EntityField val typ
|
||||
-> SqlExpr (Value typ)
|
||||
ERaw m f ^. field
|
||||
type family EntityTy entity ent
|
||||
type family ValueTy value val
|
||||
type family EntityTyToValueTy entity val
|
||||
type family ValueTyToEntityTy value ent
|
||||
|
||||
type instance EntityTy (Entity ent) ent = Entity ent
|
||||
type instance ValueTy (Value val) val = Value val
|
||||
type instance EntityTyToValueTy (Entity ent) val = Value val
|
||||
type instance ValueTyToEntityTy (Value val) ent = Entity ent
|
||||
|
||||
type EntityValuePair entity ent value val =
|
||||
( EntityTyToValueTy entity val ~ value
|
||||
, ValueTyToEntityTy value ent ~ entity
|
||||
, EntityTy entity ent ~ Entity ent
|
||||
, ValueTy value val ~ Value val
|
||||
)
|
||||
|
||||
(^.) :: forall ent typ entity value.
|
||||
(PersistEntity ent, PersistField typ, EntityValuePair entity ent value typ)
|
||||
=> SqlExpr entity
|
||||
-> EntityField ent typ
|
||||
-> SqlExpr value
|
||||
(ERaw m f) ^. field
|
||||
| isIdField field = idFieldValue
|
||||
| Just alias <- sqlExprMetaAlias m =
|
||||
| Just alias <- sqlExprMetaAlias m =
|
||||
ERaw noMeta $ \_ info ->
|
||||
f Never info <> ("." <> useIdent info (aliasedEntityColumnIdent alias fieldDef), [])
|
||||
| otherwise = ERaw noMeta $ \_ info -> (dot info $ persistFieldDef field, [])
|
||||
@ -562,7 +581,7 @@ ERaw m f ^. field
|
||||
\p info -> (parensM p $ uncommas $ dot info <$> idFields, [])
|
||||
|
||||
|
||||
ed = entityDef $ getEntityVal (Proxy :: Proxy (SqlExpr (Entity val)))
|
||||
ed = entityDef $ getEntityVal (Proxy :: Proxy (SqlExpr (Entity ent)))
|
||||
|
||||
dot info fieldDef =
|
||||
sourceIdent info <> "." <> fieldIdent
|
||||
@ -584,13 +603,39 @@ withNonNull field f = do
|
||||
where_ $ not_ $ isNothing field
|
||||
f $ veryUnsafeCoerceSqlExprValue field
|
||||
|
||||
type family MaybeEntityTy mEntity ent
|
||||
type family MaybeValueTy mValue val
|
||||
type family MaybeEntityTyToMaybeValueTy mEntity val
|
||||
type family MaybeValueTyToMaybeEntityTy mValue ent
|
||||
type family UnMaybeTy mValue
|
||||
type MaybeEntityValuePair mEntity ent mValue val =
|
||||
( MaybeEntityTyToMaybeValueTy mEntity val ~ mValue
|
||||
, MaybeValueTyToMaybeEntityTy mValue ent ~ mEntity
|
||||
, MaybeEntityTy mEntity ent ~ Maybe (Entity ent)
|
||||
, MaybeValueTy mValue val ~ Value (Maybe val)
|
||||
)
|
||||
|
||||
type instance MaybeEntityTy (Maybe (Entity ent)) ent = Maybe (Entity ent)
|
||||
type instance MaybeValueTy (Value (Maybe val)) val = Value (Maybe val)
|
||||
type instance MaybeEntityTyToMaybeValueTy (Maybe (Entity ent)) val = Value (Maybe val)
|
||||
type instance MaybeValueTyToMaybeEntityTy (Value (Maybe val)) ent = Maybe (Entity ent)
|
||||
type instance UnMaybeTy (Value (Maybe val)) = Value val
|
||||
type instance UnMaybeTy (Maybe (Entity ent)) = Entity ent
|
||||
|
||||
-- | Project a field of an entity that may be null.
|
||||
(?.)
|
||||
:: (PersistEntity val, PersistField typ)
|
||||
=> SqlExpr (Maybe (Entity val))
|
||||
-> EntityField val typ
|
||||
-> SqlExpr (Value (Maybe typ))
|
||||
ERaw m f ?. field = just (ERaw m f ^. field)
|
||||
:: forall ent typ entity mEntity value mValue.
|
||||
( PersistEntity ent
|
||||
, PersistField typ
|
||||
, UnMaybeTy mEntity ~ entity
|
||||
, UnMaybeTy mValue ~ value
|
||||
, MaybeEntityValuePair mEntity ent mValue typ
|
||||
, EntityValuePair entity ent value typ
|
||||
)
|
||||
=> SqlExpr mEntity
|
||||
-> EntityField ent typ
|
||||
-> SqlExpr mValue
|
||||
e ?. field = coerce $ (coerce e :: SqlExpr entity) ^. field
|
||||
|
||||
-- | Lift a constant value from Haskell-land to the query.
|
||||
val :: PersistField typ => typ -> SqlExpr (Value typ)
|
||||
@ -1138,8 +1183,10 @@ data SomeValue where
|
||||
class ToSomeValues a where
|
||||
toSomeValues :: a -> [SomeValue]
|
||||
|
||||
instance PersistEntity ent => ToSomeValues (SqlExpr (Entity ent)) where
|
||||
toSomeValues ent = [SomeValue $ ent ^. persistIdField]
|
||||
instance (PersistEntity ent) => ToSomeValues (SqlExpr (Entity ent)) where
|
||||
toSomeValues ent = [SomeValue $ ent ^. persistIdField]
|
||||
instance (PersistEntity ent) => ToSomeValues (SqlExpr (Maybe (Entity ent))) where
|
||||
toSomeValues ent = [SomeValue $ ent ?. persistIdField]
|
||||
|
||||
instance
|
||||
( ToSomeValues a
|
||||
|
||||
Loading…
Reference in New Issue
Block a user