@@ -4,46 +4,76 @@
4 OverloadedLabels,
4 OverloadedLabels,
5 OverloadedStrings ,
5 OverloadedStrings ,
6 TypeApplications ,
6 TypeApplications ,
7− TypeOperators #-}
7+ TypeOperators,
8+ PartialTypeSignatures #-}
10+{-# OPTIONS_GHC -Wno-partial-type-signatures #-}
9module Data.Query (
13module Data.Query (
11 createTables,
15 createTables,
18 getLastPosts,
18 getLastPosts,
19− getPostsForUser
19+ getPostsForUser,
22import Squeal.PostgreSQL
24import Squeal.PostgreSQL
23import Data.User
25import Data.User
24import Data.Post
26import Data.Post
25import Data.Schema
27import Data.Schema
26−-- import Data.Function (on)
28+import Data.Function (on)
27−import qualified Data.Map.Lazy as M
28import Data.Connection
29import Data.Connection
29import Data.PostElement
30import Data.PostElement
30import qualified Servant as S
31import qualified Servant as S
31import Control.Monad.Morph
32import Control.Monad.Morph
33import Data.Word (Word64)
33import Data.Word (Word64)
34+import qualified Data.List as L
37+-- MARK: Generalized Post Queries
39+type TableEndo schema params from grouping = TableExpression schema params from grouping ->
40+ TableExpression schema params from grouping
42+postsQ :: TableEndo Schema params _ 'Ungrouped -> Query Schema params _
43+postsQ te = selectStar $ te (from $ table #posts)
45+postViewT :: TableEndo Schema params _ 'Ungrouped -> TableExpression Schema params _ 'Ungrouped
46+postViewT te = from $ subquery (postsQ te `As` #posts) &
47+ innerJoin (table #postElements)
48+ (#posts ! #postRowId .== #postElements ! #rowElementId)
36−getQuoteRowsQ :: Query Schema (TuplePG (Only QuoteId)) (RowPG (ElementRow Post))
50+getPostQ :: TableEndo Schema params _ 'Ungrouped -> TableEndo Schema params _ 'Ungrouped -> Query Schema params (RowPG PostRowResponse)
37−getQuoteRowsQ = UnsafeQuery "WITH RECURSIVE getQuote AS ( \
51+getPostQ oTe te = select (
52+ #posts ! #postRowId :*
53+ #posts ! #postRowAuthorId :*
38− \SELECT * FROM \"quoteElements\" quote WHERE \"rowElementId\" = $1 \
54+ -- #rowElementId :*
39− \UNION ALL \
55+ #postElements ! #rowElementOrd :*
40− \SELECT newQuote.* FROM \"quoteElements\" newQuote \
56+ #postElements ! #rowElementMarkdown :*
41− \INNER JOIN getQuote q ON q.\"rowElementQuote\" = newQuote.\"rowElementId\" \
57+ #postElements ! #rowElementLatex :*
42− \) \
58+ #postElements ! #rowElementImage :*
43− \SELECT * FROM getQuote"
59+ #postElements ! #rowElementQuote :*
60+ #postElements ! #rowElementAttachment)
64+-- MARK: Concrete Queries
66+-- getQuoteRowsQ :: Query Schema (TuplePG (Only QuoteId)) (RowPG (ElementRow Post))
67+-- getQuoteRowsQ = UnsafeQuery "WITH RECURSIVE getQuote AS ( \
68+-- \SELECT * FROM \"quoteElements\" quote WHERE \"rowElementId\" = $1 \
70+-- \SELECT newQuote.* FROM \"quoteElements\" newQuote \
71+-- \INNER JOIN getQuote q ON q.\"rowElementQuote\" = newQuote.\"rowElementId\" \
73+-- \SELECT * FROM getQuote"
45−getQuoteQ :: Query Schema (TuplePG (Only QuoteId)) (RowPG PostQuoteRow)
75+-- getQuoteQ :: Query Schema (TuplePG (Only QuoteId)) (RowPG PostQuoteRow)
46−getQuoteQ = selectStar $ from (table #quotes) & where_ (#quoteRowId .== param @1)
76+-- getQuoteQ = selectStar $ from (table #quotes) & where_ (#quoteRowId .== param @1)
48-- getQuote :: QuoteId -> PQ Quote
78-- getQuote :: QuoteId -> PQ Quote
49-- getQuote qId = do
79-- getQuote qId = do
@@ -55,62 +85,27 @@ getQuoteQ = selectStar $ from (table #quotes) & where_ (#quoteRowId .== param @1
55getUserQ :: Query Schema (TuplePG (Only UserId)) (RowPG User)
85getUserQ :: Query Schema (TuplePG (Only UserId)) (RowPG User)
56getUserQ = selectStar $ from (table #users) & where_ (#userId .== param @1)
86getUserQ = selectStar $ from (table #users) & where_ (#userId .== param @1)
58−getPostRowQ :: Query Schema (TuplePG (Only PostId)) (RowPG PostRow)
88+getPostByIdQ :: Query Schema (TuplePG (Only PostId)) (RowPG PostRowResponse)
59−getPostRowQ = selectStar $ from (table #posts) & where_ (#postRowId .== param @1)
89+getPostByIdQ = getPostQ id $ where_ $ #postRowId .== param @1
61−getDraftRowQ :: Query Schema (TuplePG (Only DraftId)) (RowPG PostRow)
62−getDraftRowQ = selectStar $ from (table #drafts) & where_ (#postRowId .== param @1)
64−getPostElementsQ :: Query Schema (TuplePG (Only PostId)) (RowPG (ElementRow Post))
65−getPostElementsQ = selectStar $ from (table #postElements) & where_ (#rowElementId .== param @1)
67−getDraftElementsQ :: Query Schema (TuplePG (Only DraftId)) (RowPG (ElementRow Draft))
91+type Limit = Word64
68−getDraftElementsQ = selectStar $ from (table #draftElements) & where_ (#rowElementId .== param @1)
70−getPost :: PostId -> StaticPQ (Maybe Post)
93+getLastNPostsQ :: Limit -> Query Schema params (RowPG PostRowResponse)
71−getPost pId = do
94+getLastNPostsQ lim = getPostQ
72− postRow <- runQueryParams getPostRowQ (Only pId) >>= getRow 0
73− postRowElements <- runQueryParams getPostElementsQ (Only pId) >>= getRows
74− return $ rowsToPost M.empty M.empty postRow postRowElements
76−getLastPostRowsQ :: Word64 -> Query Schema '[] (RowPG (Only PostId))
77−getLastPostRowsQ n = select (#postRowId `as` #fromOnly) $
78− (from $ table #posts) &
79− orderBy [#postRowId & Desc] &
95+ (limit lim . orderBy [#posts ! #postRowId & Desc]) id
82−getLastPosts :: Word64 -> StaticPQ [Post]
84− postIds <- runQuery (getLastPostRowsQ n) >>= getRows
85− catMaybes <$> traverse getPost (map fromOnly postIds)
87−getUser :: UserId -> StaticPQ User
88−getUser uId = runQueryParams getUserQ (Only uId) >>= getRow 0
91−createDraftRowQ :: Manipulation Schema (TuplePG (Only UserId)) (RowPG (Only DraftId))
92−createDraftRowQ = insertRow #drafts
93− (Default `as` #postRowId :* Set (param @1) `as` #postRowAuthorId)
95− (Returning $ #postRowId `as` #fromOnly)
97+getLastNPostsForUserQ :: Limit -> Query Schema (TuplePG (Only UserId)) (RowPG PostRowResponse)
98+getLastNPostsForUserQ lim = getPostQ
99+ (limit lim . orderBy [#posts ! #postRowId & Desc])
100+ (where_ $ #postRowAuthorId .== param @1)
97createPostRowQ :: Manipulation Schema (TuplePG (Only UserId)) (RowPG (Only PostId))
102createPostRowQ :: Manipulation Schema (TuplePG (Only UserId)) (RowPG (Only PostId))
98createPostRowQ = insertRow #posts
103createPostRowQ = insertRow #posts
99 (Default `as` #postRowId :* Set (param @1) `as` #postRowAuthorId)
104 (Default `as` #postRowId :* Set (param @1) `as` #postRowAuthorId)
100 OnConflictDoRaise
105 OnConflictDoRaise
101 (Returning $ #postRowId `as` #fromOnly)
106 (Returning $ #postRowId `as` #fromOnly)
103−createDraftElementRowsQ :: Manipulation Schema (TuplePG (ElementRow Draft)) '[]
104−createDraftElementRowsQ = insertRow_ #draftElements
105− (Set (param @1) `as` #rowElementId :*
106− Set (param @2) `as` #rowElementOrd :*
107− Set (param @3) `as` #rowElementMarkdown :*
108− Set (param @4) `as` #rowElementLatex :*
109− Set (param @5) `as` #rowElementImage :*
110− Set (param @6) `as` #rowElementQuote :*
111− Set (param @7) `as` #rowElementAttachment)
113−createPostElementRowsQ :: Manipulation Schema (TuplePG (ElementRow Post)) '[]
108+createPostElementRowsQ :: Manipulation Schema (TuplePG (PostElementRow Post)) '[]
114createPostElementRowsQ = insertRow_ #postElements
109createPostElementRowsQ = insertRow_ #postElements
115 (Set (param @1) `as` #rowElementId :*
110 (Set (param @1) `as` #rowElementId :*
116 Set (param @2) `as` #rowElementOrd :*
111 Set (param @2) `as` #rowElementOrd :*
@@ -120,84 +115,38 @@ createPostElementRowsQ = insertRow_ #postElements
120 Set (param @6) `as` #rowElementQuote :*
115 Set (param @6) `as` #rowElementQuote :*
121 Set (param @7) `as` #rowElementAttachment)
116 Set (param @7) `as` #rowElementAttachment)
123−createDraft :: UserId -> [PostElement Draft] -> StaticPQ Draft
118+-- MARK: FrontEnd functions
124−createDraft uId els = transactionally_ $ do
119+
125− dId' <- manipulateParams createDraftRowQ (Only uId)
126− dId <- lift $ fromOnly <$> getRow 0 dId'
127− traversePrepared_ createDraftElementRowsQ $ elementsToRows dId els
128− return $ mkDraft dId uId els
130−getDraftsForUserQ :: Query Schema (TuplePG (Only UserId)) (RowPG (Only DraftId))
131−getDraftsForUserQ = select (#postRowId `as` #fromOnly) $
132− (from $ table #drafts) &
133− where_ (#postRowAuthorId .== param @1)
135−getPostsForUserLimitingQ :: Word64 -> Query Schema (TuplePG (Only UserId)) (RowPG (Only DraftId))
136−getPostsForUserLimitingQ lim = select (#postRowId `as` #fromOnly) $
137− (from $ table #posts) &
138− where_ (#postRowAuthorId .== param @1) &
139− orderBy [#postRowId & Desc] &
142−getPostsForUser :: Word64 -> UserId -> StaticPQ [Post]
120+getPostsForUser :: Word64 -> UserId -> StaticPQ (Maybe [Post])
143getPostsForUser lim uId = do
121getPostsForUser lim uId = do
144− postIds <- runQueryParams (getPostsForUserLimitingQ lim) (Only uId) >>= getRows
122+ postRows <- runQueryParams (getLastNPostsForUserQ lim) (Only uId) >>= getRows
145− catMaybes <$> traverse getPost (map fromOnly postIds)
123+ return . foldMaybe . map rowsToPost . L.groupBy ((==) `on` postRowId) $ postRows
125+getPost :: PostId -> StaticPQ (Maybe Post)
127+ postRow <- runQueryParams getPostByIdQ (Only pId) >>= getRows
128+ return $ rowsToPost postRow
148−getDraft :: DraftId -> StaticPQ (Maybe Draft)
130+getLastPosts :: Word64 -> StaticPQ (Maybe [Post])
149−getDraft dId = do
131+getLastPosts n = do
150− draftRow <- runQueryParams getDraftRowQ (Only dId) >>= getRow 0
132+ postRows <- runQuery (getLastNPostsQ n) >>= getRows
151− draftRowElements <- runQueryParams getDraftElementsQ (Only dId) >>= getRows
133+ return . foldMaybe . map rowsToPost . L.groupBy ((==) `on` postRowId) $ postRows
152− return $ rowsToPost M.empty M.empty draftRow draftRowElements
154−getDrafts :: UserId -> StaticPQ [Draft]
156− draftRow <- runQueryParams getDraftsForUserQ (Only uId) >>= getRows
157− catMaybes <$> traverse getDraft (map fromOnly draftRow)
159−getDraftForUserWithIdQ :: Query Schema (TuplePG (UserId, DraftId)) (RowPG PostRow)
160−getDraftForUserWithIdQ = selectStar $
161− (from $ table #drafts) &
162− where_ (#postRowAuthorId .== param @1 .&& #postRowId .== param @2)
164−deleteElementsForIdQ :: Manipulation Schema (TuplePG (Only DraftId)) '[]
165−deleteElementsForIdQ = deleteFrom_ #draftElements (#rowElementId .== param @1)
167−deleteDraftQ :: Manipulation Schema (TuplePG (Only DraftId)) '[]
168−deleteDraftQ = deleteFrom_ #drafts (#postRowId .== param @1)
170−updateDraft :: UserId -> DraftId -> [PostElement Draft] -> StaticPQ ()
171−updateDraft uId dId els = transactionally_ $ do
172− rs <- runQueryParams getDraftForUserWithIdQ (uId, dId) >>= getRows
173− let c = length (rs :: [PostRow])
175− lift $ S.throwError S.err404
177− _ <- manipulateParams deleteElementsForIdQ (Only dId)
178− traversePrepared_ createDraftElementRowsQ $ elementsToRows dId els
180−getDraftElementRowsQ :: Query Schema (TuplePG (Only DraftId)) (RowPG (ElementRow Draft))
181−getDraftElementRowsQ = selectStar $
182− (from $ table #draftElements) &
183− where_ (#rowElementId .== param @1) &
184− orderBy [#rowElementOrd & Asc]
186−publishDraft :: UserId -> DraftId -> StaticPQ ()
187−publishDraft uId dId = transactionally_ $ do
188− rs <- runQueryParams getDraftForUserWithIdQ (uId, dId) >>= getRows
189− let c = length (rs :: [PostRow])
191− lift $ S.throwError S.err404
193− els <- runQueryParams getDraftElementRowsQ (Only dId) >>= getRows
194− pId' <- manipulateParams createPostRowQ (Only uId)
195− pId <- lift $ fromOnly <$> getRow 0 pId'
196− traversePrepared_ createPostElementRowsQ $ map (fromDraft pId) els
197− _ <- manipulateParams deleteDraftQ (Only dId)
201−-- tupleFirst :: (a -> b) -> a -> (b, a)
202−-- tupleFirst f x = (f x, x)
135+getUser :: UserId -> StaticPQ User
136+getUser uId = runQueryParams getUserQ (Only uId) >>= getRow 0
138+publishPost :: UserId -> [PostElement Post] -> StaticPQ PostId
139+publishPost uId els = transactionally_ $ do
140+ pId' <- manipulateParams createPostRowQ (Only uId) >>= firstRow
142+ (Just (Only pId)) -> do
143+ traversePrepared_ createPostElementRowsQ $ elementsToRows pId els
145+ _ -> lift $ S.throwError S.err404
149+foldMaybe :: [Maybe a] -> Maybe [a]
150+foldMaybe [] = Just []
151+foldMaybe (Nothing : _) = Nothing
152+foldMaybe (Just x : xs) = (x :) <$> foldMaybe xs