on 55fef87 help sign in
rel8/src/Rel8/Expr/Aggregate.hs
176 lines 5.0 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 , listAggExpr, nonEmptyAggExpr
16 )
17where
19-- base
20import Data.Int ( Int64 )
21import Data.List.NonEmpty ( NonEmpty )
22import Prelude hiding ( and, max, min, null, or, sum )
24-- opaleye
25import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
27-- rel8
28import Rel8.Aggregate ( Aggregate, Aggregator(..), unsafeMakeAggregate )
29import Rel8.Expr ( Expr )
30import Rel8.Expr.Bool ( caseExpr )
31import Rel8.Expr.Opaleye
32 ( castExpr
33 , fromPrimExpr
34 , fromPrimExpr
35 , toPrimExpr
36 )
37import Rel8.Expr.Null ( null )
38import Rel8.Expr.Serialize ( litExpr )
39import Rel8.Schema.Null ( Sql )
40import Rel8.Type.Array ( fromPrimArray )
41import Rel8.Type.Eq ( DBEq )
42import Rel8.Type.Num ( DBNum )
43import Rel8.Type.Ord ( DBMax, DBMin )
44import Rel8.Type.String ( DBString )
45import Rel8.Type.Sum ( DBSum )
48-- | Count the occurances of a single column. Corresponds to @COUNT(a)@
49count :: Expr a -> Aggregate (Expr Int64)
50count = unsafeMakeAggregate toPrimExpr fromPrimExpr $
51 Just Aggregator
52 { operation = Opaleye.AggrCount
53 , ordering = []
54 , distinction = Opaleye.AggrAll
55 }
58-- | Count the number of distinct occurances of a single column. Corresponds to
59-- @COUNT(DISTINCT a)@
60countDistinct :: Sql DBEq a => Expr a -> Aggregate (Expr Int64)
61countDistinct = unsafeMakeAggregate toPrimExpr fromPrimExpr $
62 Just Aggregator
63 { operation = Opaleye.AggrCount
64 , ordering = []
65 , distinction = Opaleye.AggrDistinct
66 }
69-- | Corresponds to @COUNT(*)@.
70countStar :: Aggregate (Expr Int64)
71countStar = count (litExpr True)
74-- | A count of the number of times a given expression is @true@.
75countWhere :: Expr Bool -> Aggregate (Expr Int64)
76countWhere condition = count (caseExpr [(condition, litExpr (Just True))] null)
79-- | Corresponds to @bool_and@.
80and :: Expr Bool -> Aggregate (Expr Bool)
81and = unsafeMakeAggregate toPrimExpr fromPrimExpr $
82 Just Aggregator
83 { operation = Opaleye.AggrBoolAnd
84 , ordering = []
85 , distinction = Opaleye.AggrAll
86 }
89-- | Corresponds to @bool_or@.
90or :: Expr Bool -> Aggregate (Expr Bool)
91or = unsafeMakeAggregate toPrimExpr fromPrimExpr $
92 Just Aggregator
93 { operation = Opaleye.AggrBoolOr
94 , ordering = []
95 , distinction = Opaleye.AggrAll
96 }
99-- | Produce an aggregation for @Expr a@ using the @max@ function.
100max :: Sql DBMax a => Expr a -> Aggregate (Expr a)
101max = unsafeMakeAggregate toPrimExpr fromPrimExpr $
102 Just Aggregator
103 { operation = Opaleye.AggrMax
104 , ordering = []
105 , distinction = Opaleye.AggrAll
106 }
109-- | Produce an aggregation for @Expr a@ using the @max@ function.
110min :: Sql DBMin a => Expr a -> Aggregate (Expr a)
111min = unsafeMakeAggregate toPrimExpr fromPrimExpr $
112 Just Aggregator
113 { operation = Opaleye.AggrMin
114 , ordering = []
115 , distinction = Opaleye.AggrAll
116 }
118-- | Corresponds to @sum@. Note that in SQL, @sum@ is type changing - for
119-- example the @sum@ of @integer@ returns a @bigint@. Rel8 doesn't support
120-- this, and will add explicit cast back to the original input type. This can
121-- lead to overflows, and if you anticipate very large sums, you should upcast
122-- your input.
123sum :: Sql DBSum a => Expr a -> Aggregate (Expr a)
124sum = unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) $
125 Just Aggregator
126 { operation = Opaleye.AggrSum
127 , ordering = []
128 , distinction = Opaleye.AggrAll
129 }
132-- | Take the sum of all expressions that satisfy a predicate.
133sumWhere :: (Sql DBNum a, Sql DBSum a)
134 => Expr Bool -> Expr a -> Aggregate (Expr a)
135sumWhere condition a = sum (caseExpr [(condition, a)] 0)
138-- | Corresponds to @string_agg()@.
139stringAgg :: Sql DBString a
140 => Expr db -> Expr a -> Aggregate (Expr a)
141stringAgg delimiter =
142 unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) $
143 Just Aggregator
144 { operation = Opaleye.AggrStringAggr (toPrimExpr delimiter)
145 , ordering = []
146 , distinction = Opaleye.AggrAll
147 }
150-- | Aggregate a value by grouping by it.
151groupByExpr :: Sql DBEq a => Expr a -> Aggregate (Expr a)
152groupByExpr = unsafeMakeAggregate toPrimExpr fromPrimExpr Nothing
155-- | Collect expressions values as a list.
156listAggExpr :: Expr a -> Aggregate (Expr [a])
157listAggExpr = unsafeMakeAggregate toPrimExpr from $ Just
158 Aggregator
159 { operation = Opaleye.AggrArr
160 , ordering = []
161 , distinction = Opaleye.AggrAll
162 }
163 where
164 from = fromPrimExpr . fromPrimArray
167-- | Collect expressions values as a non-empty list.
168nonEmptyAggExpr :: Expr a -> Aggregate (Expr (NonEmpty a))
169nonEmptyAggExpr = unsafeMakeAggregate toPrimExpr from $ Just
170 Aggregator
171 { operation = Opaleye.AggrArr
172 , ordering = []
173 , distinction = Opaleye.AggrAll
174 }
175 where
176 from = fromPrimExpr . fromPrimArray