-
Notifications
You must be signed in to change notification settings - Fork 39
/
Copy pathkeys.hs
92 lines (79 loc) · 3.14 KB
/
keys.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
import Control.Monad.IO.Class (liftIO)
import Database.Groundhog.Sqlite
import Database.Groundhog.TH
data Artist = Artist {artistName :: String} deriving (Eq, Show)
data Album = Album {albumName :: String} deriving (Eq, Show)
-- We cannot use regular deriving because when it works, the Key Eq and Show instances for (DefaultKey Album) are not created yet
data Track = Track {albumTrack :: DefaultKey Album, trackName :: String}
deriving instance Eq Track
deriving instance Show Track
-- It is phantom datatype of the ArtistName unique key. Usually they are generated by Template Haskell, but we define it manually to use in ArtistAlbum datatype
data ArtistName v where
ArtistName :: ArtistName (UniqueMarker Artist)
-- Many-to-many relation.
data ArtistAlbum = ArtistAlbum {artist :: Key Artist (Unique ArtistName), album :: DefaultKey Album}
deriving instance Eq ArtistAlbum
deriving instance Show ArtistAlbum
mkPersist
defaultCodegenConfig
[groundhog|
definitions:
- entity: Artist
autoKey:
constrName: AutoKey
default: false # Defines if this key is used when an entity is stored directly, for example, data Ref = Ref Artist
keys:
- name: ArtistName
default: true
constructors:
- name: Artist
uniques:
- name: ArtistName
# Optional parameter type can be constraint (by default), index, or primary
type: constraint
fields: [artistName]
- entity: Album
- entity: Track
constructors:
- name: Track
fields:
- name: albumTrack
# Configure actions on parent table changes
reference:
onDelete: cascade
onUpdate: restrict
- entity: ArtistAlbum
autoKey: null # Disable creation of the autoincrement integer key
keys:
- name: ArtistAlbumKey
default: true
constructors:
- name: ArtistAlbum
uniques:
- name: ArtistAlbumKey
fields: [artist, album]
|]
main :: IO ()
main = withSqliteConn ":memory:" $
runDbConn $ do
let artists = [Artist "John Lennon", Artist "George Harrison"]
imagineAlbum = Album "Imagine"
runMigration $ do
migrate (undefined :: ArtistAlbum)
migrate (undefined :: Track)
mapM_ insert artists
imagineKey <- insert imagineAlbum
let tracks = map (Track imagineKey) ["Imagine", "Crippled Inside", "Jealous Guy", "It's So Hard", "I Don't Want to Be a Soldier, Mama, I Don't Want to Die", "Gimme Some Truth", "Oh My Love", "How Do You Sleep?", "How?", "Oh Yoko!"]
mapM_ insert tracks
mapM_ (\artist -> insert $ ArtistAlbum (extractUnique artist) imagineKey) artists
-- print first 3 tracks from any album with John Lennon
[albumKey'] <- project AlbumField $ (ArtistField ==. ArtistNameKey "John Lennon") `limitTo` 1
-- order by primary key
tracks' <- select $ (AlbumTrackField ==. albumKey') `orderBy` [Desc AutoKeyField] `limitTo` 3
liftIO $ print tracks'