@@ -0,0 +1,167 @@
4+This cookbook exists to help you easily form Rel8 queries. It's main purpose is
5+to help those familiar with SQL to translate their queries into Rel8
8+``SELECT * FROM table``
9+-----------------------
11+To select from a table, use ``each``.
16+To perform an inner join against two queries, use ``where_`` with a join
17+condition. For example, the following SQL:
21+ SELECT * FROM table_a JOIN table_b ON table_a.x = table_b.y
28+ where_ $ tableAX a ==. tableBY b
33+A ``LEFT JOIN`` query is performed by using ``optional``.
35+For example, the query:
39+ SELECT * FROM table_a LEFT JOIN table_b ON table_a.x = table_b.y
46+ maybeB <- optional do
48+ where_ $ tableAX a ==. tableBY b
52+Note that ``maybeB`` here will be a ``MaybeTable``, which is the Rel8
53+``Query``-equivalent of ``Maybe``. This allows you to observe if a left join
54+has succeeded or failed.
59+A ``Query`` by default has no ordering - just like in SQL. If you rows back in
60+a certain order, you can use ``orderBy`` with an ``Order``.
62+For example, the query:
66+ SELECT * FROM my_table ORDER BY my_table.id ASC, my_table.x DESC NULLS FIRST
71+ orderBy (mconcat [ myTableId >$< asc, myTableX >$< nullsFirst desc ]) $
74+Note that we use ``>$<`` (from ``Data.Functor.Contravariant``) to select table
75+columns, and we can use `mconcat` to combine orderings.
77+If all columns of a table have an ordering, you can also use ``ascTable`` and
78+``descTable``. For example::
80+ myQuery = orderBy ascTable $ each myTableSchema
85+Aggregations in Rel8 work by using ``aggregate``, which takes a ``Query
86+(Aggregate a)`` and gives you back a ``Query a``.
92+ SELECT sum(foo), count(distinct bar) FROM table_a
96+ myQuery = aggregate do
98+ return $ liftF2 (,) (sum (foo a)) (countDistinct (bar a))
100+where ``liftF2`` comes from ``Data.Functor.Apply`` from the ``semigroupoids``
103+Combining aggregations
104+----------------------
106+As ``Aggregate`` is an instance of ``Apply`` (which is very similar to
107+``Applicative``), individual aggregations can be combined. For example, one way
108+to take the average rating would be to write the query:
112+ SELECT sum(rating.score) / count(rating.score) FROM rating
114+In Rel8, we can write this as::
116+ myQuery = aggregate do
117+ rating <- each ratingSchema
118+ return $ liftF2 (/) (sum (score rating)) (count (score rating))
120+You can also use ``RebindableSyntax`` and ``ApplicativeDo``::
122+ {-# language ApplicativeDo, RebindableSyntax #-}
124+ import Data.Functor.Apply ((<.>))
126+ myQuery = aggregate do
127+ rating <- each ratingSchema
129+ scoreSum <- sum (score rating)
130+ numberOfRatings <- count (score rating)
131+ return (scoreSum / numberOfRatings)
134+For large aggregations, this can often make queries easier to read.
139+Rel8 has a fairly unique feature in that it's able to return not just lists of
140+rows, but can also return *tree*\s.
142+To understand what this means, we'll consider a small example query for blog
143+posts. We want our query to return:
145+1. The latest 5 blog posts.
146+2. For each blog post, all tags.
147+3. For each blog post, the latest 3 comments.
149+In Rel8, we can write this query as::
151+ latestBlogPosts = do
152+ post <- each postSchema
154+ tags <- aggregate do
155+ tag <- each tagSchema
156+ where_ (tagPostId tag ==. postId post)
157+ return (listAgg (tagName tag))
162+ orderBy (commentCreatedAt >$< desc) do
163+ comment <- each commentSchema
164+ where (commentPostId comment ==. postId post)
166+ return (post, tags, latestComments)