Better documentation on associateJoin (#281)
* Better documentation on associateJoin * fix format
This commit is contained in:
parent
e8271a00d6
commit
f03bba5bf9
@ -3664,16 +3664,112 @@ deleteKey
|
|||||||
-> R.ReaderT backend m ()
|
-> R.ReaderT backend m ()
|
||||||
deleteKey = Database.Persist.delete
|
deleteKey = Database.Persist.delete
|
||||||
|
|
||||||
-- | Avoid N+1 queries and join entities into a map structure
|
-- | Avoid N+1 queries and join entities into a map structure.
|
||||||
|
--
|
||||||
|
-- This function is useful to call on the result of a single @JOIN@. For
|
||||||
|
-- example, suppose you have this query:
|
||||||
|
--
|
||||||
-- @
|
-- @
|
||||||
-- getFoosAndNestedBarsFromParent :: ParentId -> (Map (Key Foo) (Foo, [Maybe (Entity Bar)]))
|
-- getFoosAndNestedBarsFromParent
|
||||||
-- getFoosAndNestedBarsFromParent parentId = 'fmap' associateJoin $ 'select' $
|
-- :: ParentId
|
||||||
-- 'from' $ \\(foo `'LeftOuterJoin`` bar) -> do
|
-- -> SqlPersistT IO [(Entity Foo, Maybe (Entity Bar))]
|
||||||
-- 'on' (bar '?.' BarFooId '==.' foo '^.' FooId)
|
-- getFoosAndNestedBarsFromParent parentId =
|
||||||
-- 'where_' (foo '^.' FooParentId '==.' 'val' parentId)
|
-- 'select' $ do
|
||||||
-- 'pure' (foo, bar)
|
-- (foo :& bar) <- from $
|
||||||
|
-- table @Foo
|
||||||
|
-- ``LeftOuterJoin``
|
||||||
|
-- table @Bar
|
||||||
|
-- ``on`` do
|
||||||
|
-- \\(foo :& bar) ->
|
||||||
|
-- foo ^. FooId ==. bar ?. BarFooId
|
||||||
|
-- where_ $
|
||||||
|
-- foo ^. FooParentId ==. val parentId
|
||||||
|
-- pure (foo, bar)
|
||||||
-- @
|
-- @
|
||||||
--
|
--
|
||||||
|
-- This is a natural result type for SQL - a list of tuples. However, it's not
|
||||||
|
-- what we usually want in Haskell - each @Foo@ in the list will be represented
|
||||||
|
-- multiple times, once for each @Bar@.
|
||||||
|
--
|
||||||
|
-- We can write @'fmap' 'associateJoin'@ and it will translate it into a 'Map'
|
||||||
|
-- that is keyed on the 'Key' of the left 'Entity', and the value is a tuple of
|
||||||
|
-- the entity's value as well as the list of each coresponding entity.
|
||||||
|
--
|
||||||
|
-- @
|
||||||
|
-- getFoosAndNestedBarsFromParentHaskellese
|
||||||
|
-- :: ParentId
|
||||||
|
-- -> SqlPersistT (Map (Key Foo) (Foo, [Maybe (Entity Bar)]))
|
||||||
|
-- getFoosAndNestedBarsFromParentHaskellese parentId =
|
||||||
|
-- 'fmap' 'associateJoin' $ getFoosdAndNestedBarsFromParent parentId
|
||||||
|
-- @
|
||||||
|
--
|
||||||
|
-- What if you have multiple joins?
|
||||||
|
--
|
||||||
|
-- Let's use 'associateJoin' with a *two* join query.
|
||||||
|
--
|
||||||
|
-- @
|
||||||
|
-- userPostComments
|
||||||
|
-- :: SqlQuery (SqlExpr (Entity User, Entity Post, Entity Comment))
|
||||||
|
-- userPostsComment = do
|
||||||
|
-- (u :& p :& c) <- from $
|
||||||
|
-- table @User
|
||||||
|
-- ``InnerJoin``
|
||||||
|
-- table @Post
|
||||||
|
-- `on` do
|
||||||
|
-- \\(u :& p) ->
|
||||||
|
-- u ^. UserId ==. p ^. PostUserId
|
||||||
|
-- ``InnerJoin``
|
||||||
|
-- table @Comment
|
||||||
|
-- ``on`` do
|
||||||
|
-- \\(_ :& p :& c) ->
|
||||||
|
-- p ^. PostId ==. c ^. CommentPostId
|
||||||
|
-- pure (u, p, c)
|
||||||
|
-- @
|
||||||
|
--
|
||||||
|
-- This query returns a User, with all of the users Posts, and then all of the
|
||||||
|
-- Comments on that post.
|
||||||
|
--
|
||||||
|
-- First, we *nest* the tuple.
|
||||||
|
--
|
||||||
|
-- @
|
||||||
|
-- nest :: (a, b, c) -> (a, (b, c))
|
||||||
|
-- nest (a, b, c) = (a, (b, c))
|
||||||
|
-- @
|
||||||
|
--
|
||||||
|
-- This makes the return of the query conform to the input expected from
|
||||||
|
-- 'associateJoin'.
|
||||||
|
--
|
||||||
|
-- @
|
||||||
|
-- nestedUserPostComments
|
||||||
|
-- :: SqlPersistT IO [(Entity User, (Entity Post, Entity Comment))]
|
||||||
|
-- nestedUserPostComments =
|
||||||
|
-- fmap nest $ select userPostsComments
|
||||||
|
-- @
|
||||||
|
--
|
||||||
|
-- Now, we can call 'associateJoin' on it.
|
||||||
|
--
|
||||||
|
-- @
|
||||||
|
-- associateUsers
|
||||||
|
-- :: [(Entity User, (Entity Post, Entity Comment))]
|
||||||
|
-- -> Map UserId (User, [(Entity Post, Entity Comment)])
|
||||||
|
-- associateUsers =
|
||||||
|
-- associateJoin
|
||||||
|
-- @
|
||||||
|
--
|
||||||
|
-- Next, we'll use the 'Functor' instances for 'Map' and tuple to call
|
||||||
|
-- 'associateJoin' on the @[(Entity Post, Entity Comment)]@.
|
||||||
|
--
|
||||||
|
-- @
|
||||||
|
-- associatePostsAndComments
|
||||||
|
-- :: Map UserId (User, [(Entity Post, Entity Comment)])
|
||||||
|
-- -> Map UserId (User, Map PostId (Post, [Entity Comment]))
|
||||||
|
-- associatePostsAndComments =
|
||||||
|
-- fmap (fmap associateJoin)
|
||||||
|
-- @
|
||||||
|
--
|
||||||
|
-- For more reading on this topic, see
|
||||||
|
-- <https://www.foxhound.systems/blog/grouping-query-results-haskell/ this Foxhound Systems blog post>.
|
||||||
|
--
|
||||||
-- @since 3.1.2
|
-- @since 3.1.2
|
||||||
associateJoin
|
associateJoin
|
||||||
:: forall e1 e0. Ord (Key e0)
|
:: forall e1 e0. Ord (Key e0)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user