on 89ec060 help sign in
rel8/src/Rel8/Expr/Aggregate.hs
212 lines 6.2 kB history raw
1{-# language DataKinds #-}
2{-# language FlexibleContexts #-}
3{-# language ScopedTypeVariables #-}
4{-# language TypeFamilies #-}
6{-# options_ghc -fno-warn-redundant-constraints #-}
8module Rel8.Expr.Aggregate
9 ( count, countDistinct, countStar, countWhere
10 , and, or
11 , min, max
12 , sum, sumWhere
13 , stringAgg
14 , groupByExpr
15 , array1DAggExpr, listAggExpr, nonEmptyAggExpr
16 , listAggExprWithOrder, nonEmptyAggExprWithOrder
17 )
18where
20-- base
21import Data.Int ( Int64 )
22import Data.List.NonEmpty ( NonEmpty )
23import Prelude hiding ( and, max, min, null, or, sum )
25-- opaleye
26import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
28-- rel8
29import Rel8.Aggregate ( Aggregate, Aggregator(..), unsafeMakeAggregate )
30import Rel8.Expr ( Expr )
31import Rel8.Expr.Bool ( caseExpr )
32import Rel8.Expr.Opaleye
33 ( castExpr
34 , fromPrimExpr
35 , fromPrimExpr
36 , toPrimExpr
37 )
38import Rel8.Expr.Null ( null )
39import Rel8.Expr.Serialize ( litExpr )
40import Rel8.Order ( Order, toOrderExprs )
41import Rel8.Schema.Null ( Sql )
42import Rel8.Type.Array ( fromPrimArray )
43import Rel8.Type.Array1D ( Array1D, NotArray )
44import Rel8.Type.Eq ( DBEq )
45import Rel8.Type.Num ( DBNum )
46import Rel8.Type.Ord ( DBMax, DBMin )
47import Rel8.Type.String ( DBString )
48import Rel8.Type.Sum ( DBSum )
51-- | Count the occurances of a single column. Corresponds to @COUNT(a)@
52count :: Expr a -> Aggregate (Expr Int64)
53count = unsafeMakeAggregate toPrimExpr fromPrimExpr $
54 Just Aggregator
55 { operation = Opaleye.AggrCount
56 , ordering = []
57 , distinction = Opaleye.AggrAll
58 }
61-- | Count the number of distinct occurances of a single column. Corresponds to
62-- @COUNT(DISTINCT a)@
63countDistinct :: Sql DBEq a => Expr a -> Aggregate (Expr Int64)
64countDistinct = unsafeMakeAggregate toPrimExpr fromPrimExpr $
65 Just Aggregator
66 { operation = Opaleye.AggrCount
67 , ordering = []
68 , distinction = Opaleye.AggrDistinct
69 }
72-- | Corresponds to @COUNT(*)@.
73countStar :: Aggregate (Expr Int64)
74countStar = count (litExpr True)
77-- | A count of the number of times a given expression is @true@.
78countWhere :: Expr Bool -> Aggregate (Expr Int64)
79countWhere condition = count (caseExpr [(condition, litExpr (Just True))] null)
82-- | Corresponds to @bool_and@.
83and :: Expr Bool -> Aggregate (Expr Bool)
84and = unsafeMakeAggregate toPrimExpr fromPrimExpr $
85 Just Aggregator
86 { operation = Opaleye.AggrBoolAnd
87 , ordering = []
88 , distinction = Opaleye.AggrAll
89 }
92-- | Corresponds to @bool_or@.
93or :: Expr Bool -> Aggregate (Expr Bool)
94or = unsafeMakeAggregate toPrimExpr fromPrimExpr $
95 Just Aggregator
96 { operation = Opaleye.AggrBoolOr
97 , ordering = []
98 , distinction = Opaleye.AggrAll
99 }
102-- | Produce an aggregation for @Expr a@ using the @max@ function.
103max :: Sql DBMax a => Expr a -> Aggregate (Expr a)
104max = unsafeMakeAggregate toPrimExpr fromPrimExpr $
105 Just Aggregator
106 { operation = Opaleye.AggrMax
107 , ordering = []
108 , distinction = Opaleye.AggrAll
109 }
112-- | Produce an aggregation for @Expr a@ using the @max@ function.
113min :: Sql DBMin a => Expr a -> Aggregate (Expr a)
114min = unsafeMakeAggregate toPrimExpr fromPrimExpr $
115 Just Aggregator
116 { operation = Opaleye.AggrMin
117 , ordering = []
118 , distinction = Opaleye.AggrAll
119 }
121-- | Corresponds to @sum@. Note that in SQL, @sum@ is type changing - for
122-- example the @sum@ of @integer@ returns a @bigint@. Rel8 doesn't support
123-- this, and will add explicit cast back to the original input type. This can
124-- lead to overflows, and if you anticipate very large sums, you should upcast
125-- your input.
126sum :: Sql DBSum a => Expr a -> Aggregate (Expr a)
127sum = unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) $
128 Just Aggregator
129 { operation = Opaleye.AggrSum
130 , ordering = []
131 , distinction = Opaleye.AggrAll
132 }
135-- | Take the sum of all expressions that satisfy a predicate.
136sumWhere :: (Sql DBNum a, Sql DBSum a)
137 => Expr Bool -> Expr a -> Aggregate (Expr a)
138sumWhere condition a = sum (caseExpr [(condition, a)] 0)
141-- | Corresponds to @string_agg()@.
142stringAgg :: Sql DBString a
143 => Expr db -> Expr a -> Aggregate (Expr a)
144stringAgg delimiter =
145 unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) $
146 Just Aggregator
147 { operation = Opaleye.AggrStringAggr (toPrimExpr delimiter)
148 , ordering = []
149 , distinction = Opaleye.AggrAll
150 }
153-- | Aggregate a value by grouping by it.
154groupByExpr :: Sql DBEq a => Expr a -> Aggregate (Expr a)
155groupByExpr = unsafeMakeAggregate toPrimExpr fromPrimExpr Nothing
158-- | Collect expressions values as an array.
159array1DAggExpr :: NotArray a => Expr a -> Aggregate (Expr (Array1D a))
160array1DAggExpr = unsafeMakeAggregate toPrimExpr fromPrimExpr $ Just
161 Aggregator
162 { operation = Opaleye.AggrArr
163 , ordering = []
164 , distinction = Opaleye.AggrAll
165 }
168-- | Collect expressions values as a list.
169listAggExpr :: Expr a -> Aggregate (Expr [a])
170listAggExpr = unsafeMakeAggregate toPrimExpr from $ Just
171 Aggregator
172 { operation = Opaleye.AggrArr
173 , ordering = []
174 , distinction = Opaleye.AggrAll
175 }
176 where
177 from = fromPrimExpr . fromPrimArray
180-- | Collect expressions values as a non-empty list.
181nonEmptyAggExpr :: Expr a -> Aggregate (Expr (NonEmpty a))
182nonEmptyAggExpr = unsafeMakeAggregate toPrimExpr from $ Just
183 Aggregator
184 { operation = Opaleye.AggrArr
185 , ordering = []
186 , distinction = Opaleye.AggrAll
187 }
188 where
189 from = fromPrimExpr . fromPrimArray
192listAggExprWithOrder :: Order o -> o -> Expr a -> Aggregate (Expr [a])
193listAggExprWithOrder order o = unsafeMakeAggregate toPrimExpr from $ Just
194 Aggregator
195 { operation = Opaleye.AggrArr
196 , ordering = toOrderExprs order o
197 , distinction = Opaleye.AggrAll
198 }
199 where
200 from = fromPrimExpr . fromPrimArray
203nonEmptyAggExprWithOrder :: ()
204 => Order o -> o -> Expr a -> Aggregate (Expr (NonEmpty a))
205nonEmptyAggExprWithOrder order o = unsafeMakeAggregate toPrimExpr from $ Just
206 Aggregator
207 { operation = Opaleye.AggrArr
208 , ordering = toOrderExprs order o
209 , distinction = Opaleye.AggrAll
210 }
211 where
212 from = fromPrimExpr . fromPrimArray