2017-05-28 87 views
3

考慮到以下兩種模式和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

回答

0

你會想這樣的事情

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