on 5a47018 help sign in
rel8/src/Rel8/Table/Aggregate.hs
82 lines 2.8 kB history raw
1{-# language FlexibleContexts #-}
2{-# language NamedFieldPuns #-}
3{-# language ScopedTypeVariables #-}
4{-# language TypeApplications #-}
5{-# language TypeFamilies #-}
6{-# language ViewPatterns #-}
8module Rel8.Table.Aggregate
9 ( groupBy, hgroupBy
10 , listAgg, nonEmptyAgg
11 )
12where
14-- base
15import Data.Functor.Identity ( Identity( Identity ) )
16import Prelude
18-- rel8
19import Rel8.Aggregate ( Aggregate, Aggregates, Col( A ) )
20import Rel8.Expr ( Expr, Col( E ) )
21import Rel8.Expr.Aggregate
22 ( groupByExpr
23 , slistAggExpr
24 , snonEmptyAggExpr
25 )
26import Rel8.Schema.Dict ( Dict( Dict ) )
27import Rel8.Schema.HTable ( HTable, hfield, htabulate )
28import Rel8.Schema.HTable.Vectorize ( hvectorize )
29import Rel8.Schema.Null ( Sql )
30import Rel8.Schema.Spec ( SSpec( SSpec, info ) )
31import Rel8.Schema.Spec.Constrain ( ConstrainSpec )
32import Rel8.Table ( toColumns, fromColumns )
33import Rel8.Table.Eq ( EqTable, eqTable )
34import Rel8.Table.List ( ListTable )
35import Rel8.Table.NonEmpty ( NonEmptyTable )
36import Rel8.Type.Eq ( DBEq )
39-- | Group equal tables together. This works by aggregating each column in the
40-- given table with 'groupByExpr'.
41groupBy :: forall exprs aggregates. (EqTable exprs, Aggregates aggregates exprs)
42 => exprs -> aggregates
43groupBy = fromColumns . hgroupBy (eqTable @exprs) . toColumns
46hgroupBy :: HTable t
47 => t (Dict (ConstrainSpec (Sql DBEq))) -> t (Col Expr) -> t (Col Aggregate)
48hgroupBy eqs exprs = fromColumns $ htabulate $ \field ->
49 case hfield eqs field of
50 Dict -> case hfield exprs field of
51 E expr -> A $ groupByExpr expr
54-- | Aggregate rows into a single row containing an array of all aggregated
55-- rows. This can be used to associate multiple rows with a single row, without
56-- changing the over cardinality of the query. This allows you to essentially
57-- return a tree-like structure from queries.
58--
59-- For example, if we have a table of orders and each orders contains multiple
60-- items, we could aggregate the table of orders, pairing each order with its
61-- items:
62--
63-- @
64-- ordersWithItems :: Query (Order Expr, ListTable Expr (Item Expr))
65-- ordersWithItems = do
66-- order <- each orderSchema
67-- items <- aggregate $ listAgg <$> itemsFromOrder order
68-- return (order, items)
69-- @
70listAgg :: Aggregates aggregates exprs => exprs -> ListTable Aggregate aggregates
71listAgg (toColumns -> exprs) = fromColumns $
72 hvectorize
73 (\SSpec {info} (Identity (E a)) -> A $ slistAggExpr info a)
74 (pure exprs)
77-- | Like 'listAgg', but the result is guaranteed to be a non-empty list.
78nonEmptyAgg :: Aggregates aggregates exprs => exprs -> NonEmptyTable Aggregate aggregates
79nonEmptyAgg (toColumns -> exprs) = fromColumns $
80 hvectorize
81 (\SSpec {info} (Identity (E a)) -> A $ snonEmptyAggExpr info a)
82 (pure exprs)