撰写Database.Esqueleto查询,有条件的连接和计数(Composing Databas

2019-09-03 19:54发布

我怎样才能构成Database.Esqueleto查询中,使得限定了“基地”查询和相应的结果集后,我可以限制结果通过添加额外的内部联接,并且其中表达式设定的模块化的方式。

另外,我怎么可以转换的基本查询返回的实体(或元组字段)的列表成计数结果,因为基础查询不这样执行设定的查询,但它的修改版本与LIMIT和OFFSET。

从通过了以下不正确的Haskell代码片段的耶索德书希望澄清什么,我瞄准。

{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies, OverloadedStrings #-}
{-# LANGUAGE GADTs, FlexibleContexts #-}
import qualified Database.Persist as P
import qualified Database.Persist.Sqlite as PS
import Database.Persist.TH
import Control.Monad.IO.Class (liftIO)
import Data.Conduit
import Control.Monad.Logger
import Database.Esqueleto
import Control.Applicative

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Person
    name String
    age Int Maybe
    deriving Show
BlogPost
    title String
    authorId PersonId
    deriving Show
Comment
    comment String
    blogPostId BlogPostId
|]

main :: IO ()
main = runStdoutLoggingT $ runResourceT $ PS.withSqliteConn ":memory:" $ PS.runSqlConn $ do
    runMigration migrateAll

    johnId <- P.insert $ Person "John Doe" $ Just 35
    janeId <- P.insert $ Person "Jane Doe" Nothing

    jackId <- P.insert $ Person "Jack Black" $ Just 45
    jillId <- P.insert $ Person "Jill Black" Nothing

    blogPostId <- P.insert $ BlogPost "My fr1st p0st" johnId
    P.insert $ BlogPost "One more for good measure" johnId
    P.insert $ BlogPost "Jane's" janeId

    P.insert $ Comment "great!" blogPostId

    let baseQuery = select $ from $ \(p `InnerJoin` b) -> do 
        on (p ^. PersonId ==. b ^. BlogPostAuthorId)
        where_ (p ^. PersonName `like` (val "J%"))
        return (p,b)

    -- Does not compile
    let baseQueryLimited = (,) <$> baseQuery <*> (limit 2)

    -- Does not compile
    let countingQuery = (,) <$> baseQuery <*> (return countRows)

    -- Results in invalid SQL 
    let commentsQuery = (,) <$> baseQuery
                <*> (select $ from $ \(b `InnerJoin` c) -> do
                        on (b ^. BlogPostId ==. c ^. CommentBlogPostId)
                        return ())

    somePosts <- baseQueryLimited
    count <- countingQuery
    withComments <- commentsQuery
    liftIO $ print somePosts
    liftIO $ print ((head count) :: Value Int)
    liftIO $ print withComments
    return ()

Answer 1:

对于LIMITCOUNT ,哈马尔的答案是完全正确的,所以我就没有深入他们。 我只想重申,一旦你使用了select ,你会无法再以任何方式更改查询。

对于JOIN S,目前你是不是能够做一个INNER JOIN与不同定义的一个查询from (也不是(FULL|LEFT|RIGHT) OUTER JOIN或多个)。 但是,你可以做隐式连接。 例如,如果你定义:

baseQuery = 
  from $ \(p `InnerJoin` b) -> do 
  on (p ^. PersonId ==. b ^. BlogPostAuthorId)
  where_ (p ^. PersonName `like` val "J%")
  return (p, b)

然后,你可能只是说:

commentsQuery = 
  from $ \c -> do
  (p, b) <- baseQuery
  where_ (b ^. BlogPostId ==. c ^. CommentBlogPostId)
  return (p, b, c)

然后Esqueleto将产生的线沿线的东西:

SELECT ...
FROM Comment, Person INNER JOIN BlogPost
ON    Person.id = BlogPost.authorId
WHERE Person.name LIKE "J%"
AND   BlogPost.id = Comment.blogPostId

不漂亮,但得到了完成任务INNER JOIN秒。 如果您需要做OUTER JOIN那么你就必须重构你的代码,使所有的OUTER JOIN s为在相同from (请注意,你可以做之间的隐式连接OUTER JOIN很好,没有问题)。



Answer 2:

综观文档和类型select

select :: (...) => SqlQuery a -> SqlPersistT m [r]

很明显,在调用时select ,我们离开纯组合的查询(世界SqlQuery a )和输入的副作用世界( SqlPersistT m [r] 因此,我们只需要编写我们之前select

let baseQuery = from $ \(p `InnerJoin` b) -> do 
      on (p ^. PersonId ==. b ^. BlogPostAuthorId)
      where_ (p ^. PersonName `like` (val "J%"))
      return (p,b)

let baseQueryLimited = do r <- baseQuery; limit 2; return r
let countingQuery    = do baseQuery; return countRows

somePosts <- select baseQueryLimited
count     <- select countingQuery

这适用于限制和计数。 我还没有想出如何做到这一点的加入还,但它看起来像它应该是可能的。



文章来源: Composing Database.Esqueleto queries, conditional joins and counting