@@ -40,6 +40,39 @@ optional = mapOpaleye $ Opaleye.QueryArr . go
4040 condition = toPrimExpr true
4141
4242
43+-- | Filter out 'MaybeTable's, returning only the tables that are not-null.
44+--
45+-- This operation can be used to "undo" the effect of 'optional', which
46+-- operationally is like turning a @LEFT JOIN@ back into a full @JOIN@. You
47+-- can think of this as analogous to 'Data.Maybe.catMaybes'.
48+--
49+-- To see this in action, first consider the following 'optional' query:
50+--
51+-- >>> :{
52+-- select c $ do
53+-- author <- each authorSchema
54+-- maybeRel8 <- optional $
55+-- each projectSchema
56+-- >>= filter (\p -> projectAuthorId p ==. authorId author)
57+-- >>= filter (\p -> projectName p ==. "rel8")
58+-- return (authorName author, projectName <$> maybeRel8)
59+-- :}
60+-- [("Ollie",Just "rel8"),("Bryan O'Sullivan",Nothing),("Emily Pillmore",Nothing)]
61+--
62+-- Here @optional@ is acting as a @LEFT JOIN@. We can turn this into a proper
63+-- join by using @catMaybeTable@ to filter out rows where the join failed:
64+--
65+-- >>> :{
66+-- select c $ do
67+-- author <- each authorSchema
68+-- maybeRel8 <- optional $
69+-- each projectSchema
70+-- >>= filter (\p -> projectAuthorId p ==. authorId author)
71+-- >>= filter (\p -> projectName p ==. "rel8")
72+-- rel8 <- catMaybeTable maybeRel8
73+-- return (authorName author, projectName rel8)
74+-- :}
75+-- [("Ollie","rel8")]
4376catMaybeTable :: MaybeTable a -> Query a
4477catMaybeTable ma@(MaybeTable _ a) = do
4578 where_ $ isJustTable ma