Composing Database.Esqueleto queries, conditional joins and counting

前端 未结 2 1148
忘掉有多难
忘掉有多难 2021-01-04 12:03

How can I compose Database.Esqueleto queries in a modular way such that after defining a \"base\" query and the corresponding result set, I can restrict the result set by ad

相关标签:
2条回答
  • 2021-01-04 12:17

    For LIMIT and COUNT, hammar's answer is entirely correct so I'll not delve into them. I'll just reiterate that once you use select you'll not be able to change the query in any way again.

    For JOINs, currently you are not able to do a INNER JOIN with a query that was defined in a different from (nor (FULL|LEFT|RIGHT) OUTER JOINs). However, you can do implicit joins. For example, if you have defined:

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

    Then you may just say:

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

    Esqueleto then will generate something along the lines of:

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

    Not pretty but gets the job done for INNER JOINs. If you need to do a OUTER JOIN then you'll have to refactor your code so that all the OUTER JOINs are in the same from (note that you can do an implicit join between OUTER JOINs just fine).

    0 讨论(0)
  • 2021-01-04 12:18

    Looking at the documentation and the type of select:

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

    It's clear that upon calling select, we leave the world of pure composable queries (SqlQuery a) and enter the world of side effects (SqlPersistT m [r]). So we simply need to compose before we 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
    

    This works for limiting and counting. I haven't figured out how to do it for joins yet, but it looks like it should be possible.

    0 讨论(0)
提交回复
热议问题