2014-12-21 10:16:54 +03:00
|
|
|
% Relational Record Released!
|
|
|
|
% 2014-12-20
|
|
|
|
% Kei Hibino
|
|
|
|
|
|
|
|
Basics
|
|
|
|
-----
|
|
|
|
|
|
|
|
Building join like List Comprehension or List Monad
|
|
|
|
|
|
|
|
$$\{ (x, y) | x \in X, y \in Y, \pi_1(x) = \pi_2(y) \}$$
|
|
|
|
|
|
|
|
~~~~~ {#mycode .haskell}
|
|
|
|
[ (x, y) | x <- xs, y <- ys, fst x == snd y ] -- Comprehension
|
|
|
|
|
2016-09-17 10:00:14 +03:00
|
|
|
do { x <- xs; y <- ys; guard (fst x == snd y); return (x, y) } -- List Monad
|
2014-12-21 10:16:54 +03:00
|
|
|
|
|
|
|
personAndBirthday :: Relation () (Person, Birthday)
|
|
|
|
personAndBirthday = relation $ do
|
|
|
|
p <- query person -- Join product accumulated
|
|
|
|
b <- query birthday
|
|
|
|
wheres $ p ! Person.name' .=. b ! Birthday.name'
|
|
|
|
return $ p >< b
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
Outer Join
|
|
|
|
-----
|
|
|
|
|
|
|
|
Outer joining. Not matched records is mapped Maybe type.
|
|
|
|
|
|
|
|
~~~~~ {#mycode .haskell}
|
|
|
|
personAndBirthdayL :: Relation () (Person, Maybe Birthday)
|
|
|
|
personAndBirthdayL = relation $ do
|
|
|
|
p <- query person
|
|
|
|
b <- queryMaybe birthday -- Maybe not match
|
|
|
|
wheres $ just (p ! Person.name') .=. b ?! Birthday.name'
|
|
|
|
return $ p >< b
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
Aggregation
|
|
|
|
-----
|
|
|
|
|
|
|
|
* groupBy action only used in aggregated context Monad.
|
|
|
|
* Only aggregated context Monad with aggregated context result type is allowed to pass aggregateRelation.
|
|
|
|
|
|
|
|
~~~~~ {#mycode .haskell}
|
|
|
|
agesOfFamilies :: Relation () (String, Maybe Int32)
|
|
|
|
agesOfFamilies = aggregateRelation $ do
|
|
|
|
my <- query myTable
|
|
|
|
gFam <- groupBy $ my ! family' -- Specify grouping key
|
|
|
|
return $ gFam >< sum' (my ! age') -- Aggregated results
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
Ordering
|
|
|
|
-----
|
|
|
|
|
|
|
|
Ordering key can be incrementally accumulated.
|
|
|
|
|
|
|
|
~~~~~ {#mycode .haskell}
|
|
|
|
personAndBirthdayO :: Relation () (Person, Birthday)
|
|
|
|
personAndBirthdayO = relation $ do
|
|
|
|
p <- query person
|
|
|
|
b <- query birthday
|
|
|
|
wheres $ p ! Person.name' .=. b ! Birthday.name'
|
|
|
|
orderBy (b ! Birthday.day') Asc -- Specify ordering key
|
|
|
|
orderBy (p ! Person.name') Asc
|
|
|
|
return $ p >< b
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
Ordering
|
|
|
|
-----
|
|
|
|
|
|
|
|
Ordering key context type (not aggregated or aggregated) is checked.
|
|
|
|
|
|
|
|
~~~~~ {#mycode .haskell}
|
|
|
|
agesOfFamiliesO :: Relation () (String, Maybe Int32)
|
|
|
|
agesOfFamiliesO = aggregateRelation $ do
|
|
|
|
my <- query myTable
|
|
|
|
gFam <- groupBy $ my ! family'
|
|
|
|
let s = sum' (my ! age')
|
|
|
|
orderBy s Desc -- Only aggregated value is allowd to pass
|
|
|
|
orderBy gFam Asc
|
|
|
|
return $ gFam >< s
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
Placeholders
|
|
|
|
-----
|
|
|
|
|
|
|
|
Can embed placeholder with typed.
|
|
|
|
|
|
|
|
~~~~~ {#mycode .haskell}
|
|
|
|
specifyPerson :: Relation String (Person, Birthday)
|
|
|
|
specifyPerson = relation' $ do
|
|
|
|
pb <- query personAndBirthday -- Re-use pre-defined Relation
|
|
|
|
(ph, ()) <- placeholder (\ph' -> wheres $ pb ! fst' ! Person.name' .=. ph')
|
|
|
|
return (ph, pb)
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
Window Function
|
|
|
|
-----
|
|
|
|
|
|
|
|
Monadic style window building.
|
|
|
|
|
|
|
|
~~~~~ {#mycode .haskell}
|
|
|
|
ageRankOfFamilies :: Relation () ((Int64, String), Int32)
|
|
|
|
ageRankOfFamilies = relation $ do
|
|
|
|
my <- query myTable
|
|
|
|
return $
|
|
|
|
rank `over` do
|
|
|
|
partitionBy $ my ! family' -- Monad to build window
|
|
|
|
orderBy (my ! age') Desc
|
|
|
|
><
|
|
|
|
my ! family'
|
|
|
|
><
|
|
|
|
my ! age'
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
|
|
|
|
Record Mapping
|
|
|
|
-----
|
|
|
|
|
|
|
|
Applicative style
|
|
|
|
|
|
|
|
~~~~~ {#mycode .haskell}
|
|
|
|
(|$|) :: (a -> b) -> p a -> p b
|
|
|
|
(|*|) :: p (a -> b) -> p a -> p b
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
|
|
|
|
Record Mapping
|
|
|
|
-----
|
|
|
|
|
|
|
|
Assign record type to SQL projection
|
|
|
|
|
|
|
|
~~~~~ {#mycode .haskell}
|
|
|
|
personAndBirthdayT :: Relation () PersonAndBirthday
|
|
|
|
personAndBirthdayT = relation $ do
|
|
|
|
p <- query person
|
|
|
|
b <- query birthday
|
|
|
|
wheres $ p ! Person.name' .=. b ! Birthday.name'
|
|
|
|
return $ PersonAndBirthday |$| p |*| b -- Build record phantom type
|
|
|
|
|
|
|
|
(|$|) :: (a -> b) -> Projection c a -> Projection c b
|
|
|
|
(|*|) :: Projection c (a -> b) -> Projection c a -> Projection c b
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
Record Mapping
|
|
|
|
-----
|
|
|
|
|
|
|
|
Projection path can be map to record.
|
|
|
|
|
|
|
|
~~~~~ {#mycode .haskell}
|
|
|
|
Birthday.day' :: Pi Birthday Day
|
|
|
|
|
|
|
|
uncurryPB :: Pi (Person, Birthday) PersonAndBirthday
|
|
|
|
uncurryPB = PersonAndBirthday |$| fst' |*| snd'
|
|
|
|
|
|
|
|
(|$|) :: (a -> b) -> Pi r a -> Pi r b
|
|
|
|
(|*|) :: Pi r (a -> b) -> Pi r a -> Pi r b
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
Record Mapping
|
|
|
|
-----
|
|
|
|
|
|
|
|
Placeholder can be map to record.
|
|
|
|
|
|
|
|
~~~~~ {#mycode .haskell}
|
|
|
|
placeholder3 f =
|
|
|
|
placeholder (\p0 -> placeholder (\p1 -> placeholder (\p2 -> f p0 p1 p2)))
|
|
|
|
|
|
|
|
personAndBirthdayP2 :: Relation ((String, Int32), String) PersonAndBirthday
|
|
|
|
personAndBirthdayP2 = relation' $ do
|
|
|
|
p <- query person
|
|
|
|
b <- query birthday
|
|
|
|
(ph0, (ph1, (ph2, ()))) <-
|
|
|
|
placeholder3 (\ph0' ph1' ph2' ->
|
|
|
|
wheres $
|
|
|
|
(Person |$| p ! Person.name' |*| p ! Person.age' |*| p ! Person.address')
|
|
|
|
.=.
|
|
|
|
(Person |$| ph0' |*| ph1' |*| ph2') )
|
|
|
|
return $ (ph0 >< ph1 >< ph2, PersonAndBirthday |$| p |*| b)
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
Record Mapping
|
|
|
|
-----
|
|
|
|
|
|
|
|
Record typed placeholder.
|
|
|
|
|
|
|
|
~~~~~ {#mycode .haskell}
|
|
|
|
personAndBirthdayP :: Relation Person PersonAndBirthday
|
|
|
|
personAndBirthdayP = relation' $ do
|
|
|
|
p <- query person
|
|
|
|
b <- query birthday
|
|
|
|
(ph, ()) <- placeholder (\ph' -> wheres $ p .=. ph')
|
|
|
|
return $ (ph, PersonAndBirthday |$| p |*| b)
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
Relational Record and Opaleye
|
|
|
|
-----
|
|
|
|
|
|
|
|
* Notation differs
|
|
|
|
* Opaleye -- Arrow notation
|
|
|
|
* Relational Record -- Monad
|
|
|
|
* Not aggregated approach is same
|
|
|
|
* Differs on Aggregation, Ordering, Placeholder and Record mapping
|
|
|
|
|
|
|
|
|
|
|
|
Relational Record and Opaleye?
|
|
|
|
-----
|
|
|
|
|
|
|
|
Easy to wrap HRR combinators against arrow notations.
|
|
|
|
|
|
|
|
~~~~~ {#mycode .haskell}
|
|
|
|
personAndJoinA :: QuerySimple () (Projection Flat (Person, Birthday))
|
|
|
|
personAndJoinA = proc () -> do
|
|
|
|
p <- query -< person
|
|
|
|
b <- query -< birthday
|
|
|
|
wheres -< p ! Person.name' .=. b ! Birthday.name'
|
|
|
|
returnA -< p >< b
|
|
|
|
|
|
|
|
personAndBirthdayOP :: Query (PersonColumn, BirthdayColumn)
|
|
|
|
personAndBirthdayOP = proc () -> do
|
|
|
|
p <- personQuery -< ()
|
|
|
|
b <- birthdayQuery -< ()
|
|
|
|
restrict -< Person.name p .== Birthday.name b
|
|
|
|
returnA -< (p, b)
|
|
|
|
~~~~~
|
|
|
|
|
|
|
|
Discussion
|
|
|
|
-----
|