如何使用Esqueleto做一个“SELECT ... IN(SELECT ...)”?

问题描述:

考虑到以下两种模式和GET /articles/:slug/comments请求,我想根据其slug检索属于文章的评论。如何使用Esqueleto做一个“SELECT ... IN(SELECT ...)”?

Article json sql=articles 
    slug  Slug 
    title  Text 
    description Text 
    body  Text 
    createdAt UTCTime default=now() 
    updatedAt UTCTime Maybe default=NULL 
    userId  UserId 
    UniqueSlug slug 

Comment json sql=comments 
    body  Text 
    createdAt UTCTime default=now() 
    updatedAt UTCTime Maybe default=NULL 
    articleId ArticleId 
    userId UserId 

使用持久性的rawSql,我们可以做到这一点如下

getCommentsForArticle :: Slug -> App (Cmts [Entity Comment]) 
getCommentsForArticle slug = do 
    comments <- runDb $ rawSql stm [toPersistValue slug] 
    return (Cmts comments) 
     where stm = "SELECT ?? FROM comments \ 
        \WHERE article_id IN (\ 
         \SELECT id FROM articles WHERE slug = ?)" 

然而,由于我想保持Haskell和SQL之间的类型安全,我想改写这个使用esqueleto。这是我正在努力的部分。通过阅读文档,sub_select似乎是工作的工具。下面是我有:

getCommentsForArticle :: Slug -> App (Cmts [Comment]) 
getCommentsForArticle slug = do 
    comments <- E.select $ 
     E.from $ \cmts -> do 
      let subQuery = 
        E.from $ \arts -> do 
         E.where_ $ arts ^. ArticleSlug ==. E.val slug 
         return (arts ^. ArticleId) 
      E.where_ $ cmts ^. CommentArticleId ==. E.sub_select subQuery 
      return cmts 
    return $ Cmts comments 

我也注意到了in_ operator,但我无法弄清楚如何使用它也不是,如果它比sub_select更合适。

我错过了什么?语法是否正确?谢谢。

+0

无关,但'SELECT * ...'是一个糟糕的风格:)。为什么不使用SQLite或任何其他SQL DB?他们都很好打字;) – Igor

+0

@Igor由不良风格你的意思是不只选择什么是必需的?根据你的第二个问题,我编辑了这个问题来指定我正在使用'persistent'和'postgresql'。 –

+0

是的。为什么不在需要SQL时使用SQL? – Igor

你会想这样的事情

getCommentsForArticle slug = do 
    c <- select $ from $ \cmts -> do 
     let a = subList_select from $ \arts -> do 
       where_ $ arts ^. ArticleSlug ==. val slug 
       return $ arts ^. ArticleId 
     where_ $ cmts ^. CommentArticleId `in_` a 
     return cmts 
    return $ Cmts c