on 06f34d2 help sign in
rel8/src/Rel8/Expr/Aggregate.hs
227 lines 6.5 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 , headAggExpr
16 , array1DAggExpr, listAggExpr, nonEmptyAggExpr
17 , listAggExprWithOrder, nonEmptyAggExprWithOrder
18 )
19where
21-- base
22import Data.Int ( Int64 )
23import Data.List.NonEmpty ( NonEmpty )
24import Prelude hiding ( and, max, min, null, or, sum )
26-- opaleye
27import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
29-- rel8
30import Rel8.Aggregate ( Aggregate, Aggregator(..), unsafeMakeAggregate )
31import Rel8.Expr ( Expr )
32import Rel8.Expr.Bool ( caseExpr )
33import Rel8.Expr.Opaleye
34 ( castExpr
35 , fromPrimExpr
36 , fromPrimExpr
37 , toPrimExpr
38 )
39import Rel8.Expr.Null ( null )
40import Rel8.Expr.Serialize ( litExpr )
41import Rel8.Order ( Order, toOrderExprs )
42import Rel8.Schema.Null ( Sql )
43import Rel8.Type.Array ( fromPrimArray )
44import Rel8.Type.Array1D ( Array1D, NotArray )
45import Rel8.Type.Eq ( DBEq )
46import Rel8.Type.Num ( DBNum )
47import Rel8.Type.Ord ( DBMax, DBMin )
48import Rel8.Type.String ( DBString )
49import Rel8.Type.Sum ( DBSum )
52-- | Count the occurances of a single column. Corresponds to @COUNT(a)@
53count :: Expr a -> Aggregate (Expr Int64)
54count = unsafeMakeAggregate toPrimExpr fromPrimExpr $
55 Just Aggregator
56 { operation = Opaleye.AggrCount
57 , ordering = []
58 , distinction = Opaleye.AggrAll
59 }
62-- | Count the number of distinct occurances of a single column. Corresponds to
63-- @COUNT(DISTINCT a)@
64countDistinct :: Sql DBEq a => Expr a -> Aggregate (Expr Int64)
65countDistinct = unsafeMakeAggregate toPrimExpr fromPrimExpr $
66 Just Aggregator
67 { operation = Opaleye.AggrCount
68 , ordering = []
69 , distinction = Opaleye.AggrDistinct
70 }
73-- | Corresponds to @COUNT(*)@.
74countStar :: Aggregate (Expr Int64)
75countStar = count (litExpr True)
78-- | A count of the number of times a given expression is @true@.
79countWhere :: Expr Bool -> Aggregate (Expr Int64)
80countWhere condition = count (caseExpr [(condition, litExpr (Just True))] null)
83-- | Corresponds to @bool_and@.
84and :: Expr Bool -> Aggregate (Expr Bool)
85and = unsafeMakeAggregate toPrimExpr fromPrimExpr $
86 Just Aggregator
87 { operation = Opaleye.AggrBoolAnd
88 , ordering = []
89 , distinction = Opaleye.AggrAll
90 }
93-- | Corresponds to @bool_or@.
94or :: Expr Bool -> Aggregate (Expr Bool)
95or = unsafeMakeAggregate toPrimExpr fromPrimExpr $
96 Just Aggregator
97 { operation = Opaleye.AggrBoolOr
98 , ordering = []
99 , distinction = Opaleye.AggrAll
100 }
103-- | Produce an aggregation for @Expr a@ using the @max@ function.
104max :: Sql DBMax a => Expr a -> Aggregate (Expr a)
105max = unsafeMakeAggregate toPrimExpr fromPrimExpr $
106 Just Aggregator
107 { operation = Opaleye.AggrMax
108 , ordering = []
109 , distinction = Opaleye.AggrAll
110 }
113-- | Produce an aggregation for @Expr a@ using the @max@ function.
114min :: Sql DBMin a => Expr a -> Aggregate (Expr a)
115min = unsafeMakeAggregate toPrimExpr fromPrimExpr $
116 Just Aggregator
117 { operation = Opaleye.AggrMin
118 , ordering = []
119 , distinction = Opaleye.AggrAll
120 }
122-- | Corresponds to @sum@. Note that in SQL, @sum@ is type changing - for
123-- example the @sum@ of @integer@ returns a @bigint@. Rel8 doesn't support
124-- this, and will add explicit cast back to the original input type. This can
125-- lead to overflows, and if you anticipate very large sums, you should upcast
126-- your input.
127sum :: Sql DBSum a => Expr a -> Aggregate (Expr a)
128sum = unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) $
129 Just Aggregator
130 { operation = Opaleye.AggrSum
131 , ordering = []
132 , distinction = Opaleye.AggrAll
133 }
136-- | Take the sum of all expressions that satisfy a predicate.
137sumWhere :: (Sql DBNum a, Sql DBSum a)
138 => Expr Bool -> Expr a -> Aggregate (Expr a)
139sumWhere condition a = sum (caseExpr [(condition, a)] 0)
142-- | Corresponds to @string_agg()@.
143stringAgg :: Sql DBString a
144 => Expr db -> Expr a -> Aggregate (Expr a)
145stringAgg delimiter =
146 unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) $
147 Just Aggregator
148 { operation = Opaleye.AggrStringAggr (toPrimExpr delimiter)
149 , ordering = []
150 , distinction = Opaleye.AggrAll
151 }
154-- | Aggregate a value by grouping by it.
155groupByExpr :: Sql DBEq a => Expr a -> Aggregate (Expr a)
156groupByExpr = unsafeMakeAggregate toPrimExpr fromPrimExpr Nothing
159-- | Keep only the first value.
160headAggExpr :: Expr a -> Aggregate (Expr a)
161headAggExpr = unsafeMakeAggregate toPrimExpr from $ Just
162 Aggregator
163 { operation = Opaleye.AggrArr
164 , ordering = []
165 , distinction = Opaleye.AggrAll
166 }
167 where
168 from = fromPrimExpr . flip Opaleye.ArrayIndex one
169 where
170 one = Opaleye.ConstExpr (Opaleye.NumericLit 1)
173-- | Collect expressions values as an array.
174array1DAggExpr :: NotArray a => Expr a -> Aggregate (Expr (Array1D a))
175array1DAggExpr = unsafeMakeAggregate toPrimExpr fromPrimExpr $ Just
176 Aggregator
177 { operation = Opaleye.AggrArr
178 , ordering = []
179 , distinction = Opaleye.AggrAll
180 }
183-- | Collect expressions values as a list.
184listAggExpr :: Expr a -> Aggregate (Expr [a])
185listAggExpr = unsafeMakeAggregate toPrimExpr from $ Just
186 Aggregator
187 { operation = Opaleye.AggrArr
188 , ordering = []
189 , distinction = Opaleye.AggrAll
190 }
191 where
192 from = fromPrimExpr . fromPrimArray
195-- | Collect expressions values as a non-empty list.
196nonEmptyAggExpr :: Expr a -> Aggregate (Expr (NonEmpty a))
197nonEmptyAggExpr = unsafeMakeAggregate toPrimExpr from $ Just
198 Aggregator
199 { operation = Opaleye.AggrArr
200 , ordering = []
201 , distinction = Opaleye.AggrAll
202 }
203 where
204 from = fromPrimExpr . fromPrimArray
207listAggExprWithOrder :: Order o -> o -> Expr a -> Aggregate (Expr [a])
208listAggExprWithOrder order o = unsafeMakeAggregate toPrimExpr from $ Just
209 Aggregator
210 { operation = Opaleye.AggrArr
211 , ordering = toOrderExprs order o
212 , distinction = Opaleye.AggrAll
213 }
214 where
215 from = fromPrimExpr . fromPrimArray
218nonEmptyAggExprWithOrder :: ()
219 => Order o -> o -> Expr a -> Aggregate (Expr (NonEmpty a))
220nonEmptyAggExprWithOrder order o = unsafeMakeAggregate toPrimExpr from $ Just
221 Aggregator
222 { operation = Opaleye.AggrArr
223 , ordering = toOrderExprs order o
224 , distinction = Opaleye.AggrAll
225 }
226 where
227 from = fromPrimExpr . fromPrimArray