{-# LANGUAGE BangPatterns, TypeFamilies, DataKinds, PolyKinds, TypeApplications, ScopedTypeVariables, ConstraintKinds, TypeOperators, GADTs, UndecidableInstances, StandaloneDeriving, FunctionalDependencies, FlexibleContexts, InstanceSigs, AllowAmbiguousTypes, DeriveAnyClass #-}
{-|
Module: IHP.QueryBuilder.Order
Description: ORDER BY, LIMIT, OFFSET, and DISTINCT operations for QueryBuilder
Copyright: (c) digitally induced GmbH, 2020

This module provides functions for ordering, limiting, and deduplicating query results.
-}
module IHP.QueryBuilder.Order
( orderBy
, orderByAsc
, orderByDesc
, orderByJoinedTable
, orderByAscJoinedTable
, orderByDescJoinedTable
, limit
, offset
, distinct
, distinctOn
) where

import IHP.Prelude
import IHP.ModelSupport
import IHP.QueryBuilder.Types
import IHP.QueryBuilder.Compiler (qualifiedColumnName)

-- | Adds an @ORDER BY .. ASC@ to your query.
--
-- Use 'orderByDesc' for descending order.
--
-- __Example:__ Fetch the 10 oldest books.
--
-- > query @Book
-- >     |> orderBy #createdAt -- >     |> limit 10
-- >     |> fetch
-- > -- SELECT * FROM books LIMIT 10 ORDER BY created_at ASC
orderByAsc :: forall name model table value queryBuilderProvider joinRegister. (KnownSymbol table, KnownSymbol name, HasField name model value, model ~ GetModelByTableName table, HasQueryBuilder queryBuilderProvider joinRegister, Table model) => Proxy name -> queryBuilderProvider table -> queryBuilderProvider table
orderByAsc :: forall {k} (name :: Symbol) model (table :: Symbol) value
       (queryBuilderProvider :: Symbol -> *) (joinRegister :: k).
(KnownSymbol table, KnownSymbol name, HasField name model value,
 model ~ GetModelByTableName table,
 HasQueryBuilder queryBuilderProvider joinRegister, Table model) =>
Proxy name
-> queryBuilderProvider table -> queryBuilderProvider table
orderByAsc !Proxy name
name queryBuilderProvider table
queryBuilderProvider = QueryBuilder table -> queryBuilderProvider table
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
QueryBuilder table -> queryBuilderProvider table
forall (table :: Symbol).
QueryBuilder table -> queryBuilderProvider table
injectQueryBuilder OrderByQueryBuilder { QueryBuilder table
queryBuilder :: QueryBuilder table
queryBuilder :: QueryBuilder table
queryBuilder, queryOrderByClause :: OrderByClause
queryOrderByClause = OrderByClause { orderByColumn :: Text
orderByColumn = Text
columnName, orderByDirection :: OrderByDirection
orderByDirection = OrderByDirection
Asc } }
    where
        columnName :: Text
columnName = Text -> Text -> Text
qualifiedColumnName (forall record. Table record => Text
tableName @model) (forall (symbol :: Symbol). KnownSymbol symbol => Text
symbolToText @name)
        queryBuilder :: QueryBuilder table
queryBuilder = queryBuilderProvider table -> QueryBuilder table
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
queryBuilderProvider table -> QueryBuilder table
forall (table :: Symbol).
queryBuilderProvider table -> QueryBuilder table
getQueryBuilder queryBuilderProvider table
queryBuilderProvider
{-# INLINE orderByAsc #-}

-- | Adds an @ORDER BY .. ASC@ on a joined table column to your query.
--
-- Use 'orderByDescJoinedTable' for descending order.
--
-- __Example:__ Order joined `User` records by `username` ascending.
--
-- > query @Project
-- >     |> innerJoin @User (#id, #projectId)
-- >     |> orderByAscJoinedTable #username
-- >     |> fetch
-- > -- SELECT ... FROM projects
-- > -- INNER JOIN users ON projects.id = users.project_id
-- > -- ORDER BY users.username ASC
orderByAscJoinedTable :: forall model name table value queryBuilderProvider joinRegister table'. ( KnownSymbol table, KnownSymbol name, HasField name model value, table ~ GetTableName model, HasQueryBuilder queryBuilderProvider joinRegister, IsJoined model joinRegister, Table model) => Proxy name -> queryBuilderProvider table' -> queryBuilderProvider table'
orderByAscJoinedTable :: forall {k1} model (name :: Symbol) (table :: Symbol) value
       (queryBuilderProvider :: Symbol -> *) (joinRegister :: k1)
       (table' :: Symbol).
(KnownSymbol table, KnownSymbol name, HasField name model value,
 table ~ GetTableName model,
 HasQueryBuilder queryBuilderProvider joinRegister,
 IsJoined model joinRegister, Table model) =>
Proxy name
-> queryBuilderProvider table' -> queryBuilderProvider table'
orderByAscJoinedTable !Proxy name
name queryBuilderProvider table'
queryBuilderProvider = QueryBuilder table' -> queryBuilderProvider table'
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
QueryBuilder table -> queryBuilderProvider table
forall (table :: Symbol).
QueryBuilder table -> queryBuilderProvider table
injectQueryBuilder OrderByQueryBuilder { queryBuilder :: QueryBuilder table'
queryBuilder = QueryBuilder table'
queryBuilder, queryOrderByClause :: OrderByClause
queryOrderByClause = OrderByClause { orderByColumn :: Text
orderByColumn = Text
columnName, orderByDirection :: OrderByDirection
orderByDirection = OrderByDirection
Asc } }
    where
        columnName :: Text
columnName = Text -> Text -> Text
qualifiedColumnName (forall record. Table record => Text
tableName @model) (forall (symbol :: Symbol). KnownSymbol symbol => Text
symbolToText @name)
        queryBuilder :: QueryBuilder table'
queryBuilder = queryBuilderProvider table' -> QueryBuilder table'
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
queryBuilderProvider table -> QueryBuilder table
forall (table :: Symbol).
queryBuilderProvider table -> QueryBuilder table
getQueryBuilder queryBuilderProvider table'
queryBuilderProvider
{-# INLINE orderByAscJoinedTable #-}

-- | Adds an @ORDER BY .. DESC@ to your query.
--
-- Use 'orderBy' for ascending order.
--
-- __Example:__ Fetch the 10 newest projects (ordered by creation time).
--
-- > query @Project
-- >     |> orderByDesc #createdAt
-- >     |> limit 10
-- >     |> fetch
-- > -- SELECT * FROM projects LIMIT 10 ORDER BY created_at DESC
orderByDesc :: forall name model table value queryBuilderProvider joinRegister. (KnownSymbol table, KnownSymbol name, HasField name model value, model ~ GetModelByTableName table, HasQueryBuilder queryBuilderProvider joinRegister, Table model) => Proxy name -> queryBuilderProvider table -> queryBuilderProvider table
orderByDesc :: forall {k} (name :: Symbol) model (table :: Symbol) value
       (queryBuilderProvider :: Symbol -> *) (joinRegister :: k).
(KnownSymbol table, KnownSymbol name, HasField name model value,
 model ~ GetModelByTableName table,
 HasQueryBuilder queryBuilderProvider joinRegister, Table model) =>
Proxy name
-> queryBuilderProvider table -> queryBuilderProvider table
orderByDesc !Proxy name
name queryBuilderProvider table
queryBuilderProvider = QueryBuilder table -> queryBuilderProvider table
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
QueryBuilder table -> queryBuilderProvider table
forall (table :: Symbol).
QueryBuilder table -> queryBuilderProvider table
injectQueryBuilder OrderByQueryBuilder { QueryBuilder table
queryBuilder :: QueryBuilder table
queryBuilder :: QueryBuilder table
queryBuilder, queryOrderByClause :: OrderByClause
queryOrderByClause = OrderByClause { orderByColumn :: Text
orderByColumn = Text
columnName, orderByDirection :: OrderByDirection
orderByDirection = OrderByDirection
Desc } }
    where
        columnName :: Text
columnName = Text -> Text -> Text
qualifiedColumnName (forall record. Table record => Text
tableName @model) (forall (symbol :: Symbol). KnownSymbol symbol => Text
symbolToText @name)
        queryBuilder :: QueryBuilder table
queryBuilder = queryBuilderProvider table -> QueryBuilder table
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
queryBuilderProvider table -> QueryBuilder table
forall (table :: Symbol).
queryBuilderProvider table -> QueryBuilder table
getQueryBuilder queryBuilderProvider table
queryBuilderProvider
{-# INLINE orderByDesc #-}

-- | Adds an @ORDER BY .. DESC@ on a joined table column to your query.
--
-- Use 'orderByAscJoinedTable' for ascending order.
--
-- __Example:__ Order joined `User` records by `username` descending.
--
-- > query @Project
-- >     |> innerJoin @User (#id, #projectId)
-- >     |> orderByDescJoinedTable #username
-- >     |> fetch
-- > -- SELECT ... FROM projects
-- > -- INNER JOIN users ON projects.id = users.project_id
-- > -- ORDER BY users.username DESC
orderByDescJoinedTable :: forall model name table value queryBuilderProvider joinRegister table'. ( KnownSymbol table, KnownSymbol name, HasField name model value, table ~ GetTableName model, HasQueryBuilder queryBuilderProvider joinRegister, IsJoined model joinRegister, Table model) => Proxy name -> queryBuilderProvider table' -> queryBuilderProvider table'
orderByDescJoinedTable :: forall {k1} model (name :: Symbol) (table :: Symbol) value
       (queryBuilderProvider :: Symbol -> *) (joinRegister :: k1)
       (table' :: Symbol).
(KnownSymbol table, KnownSymbol name, HasField name model value,
 table ~ GetTableName model,
 HasQueryBuilder queryBuilderProvider joinRegister,
 IsJoined model joinRegister, Table model) =>
Proxy name
-> queryBuilderProvider table' -> queryBuilderProvider table'
orderByDescJoinedTable !Proxy name
name queryBuilderProvider table'
queryBuilderProvider = QueryBuilder table' -> queryBuilderProvider table'
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
QueryBuilder table -> queryBuilderProvider table
forall (table :: Symbol).
QueryBuilder table -> queryBuilderProvider table
injectQueryBuilder OrderByQueryBuilder { queryBuilder :: QueryBuilder table'
queryBuilder = QueryBuilder table'
queryBuilder, queryOrderByClause :: OrderByClause
queryOrderByClause = OrderByClause { orderByColumn :: Text
orderByColumn = Text
columnName, orderByDirection :: OrderByDirection
orderByDirection = OrderByDirection
Desc } }
    where
        columnName :: Text
columnName = Text -> Text -> Text
qualifiedColumnName (forall record. Table record => Text
tableName @model) (forall (symbol :: Symbol). KnownSymbol symbol => Text
symbolToText @name)
        queryBuilder :: QueryBuilder table'
queryBuilder = queryBuilderProvider table' -> QueryBuilder table'
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
queryBuilderProvider table -> QueryBuilder table
forall (table :: Symbol).
queryBuilderProvider table -> QueryBuilder table
getQueryBuilder queryBuilderProvider table'
queryBuilderProvider
{-# INLINE orderByDescJoinedTable #-}

-- | Alias for 'orderByAsc'
orderBy :: (KnownSymbol table, KnownSymbol name, HasField name model value, model ~ GetModelByTableName table, HasQueryBuilder queryBuilderProvider joinRegister, Table model) => Proxy name -> queryBuilderProvider table -> queryBuilderProvider table
orderBy :: forall {k} (table :: Symbol) (name :: Symbol) model value
       (queryBuilderProvider :: Symbol -> *) (joinRegister :: k).
(KnownSymbol table, KnownSymbol name, HasField name model value,
 model ~ GetModelByTableName table,
 HasQueryBuilder queryBuilderProvider joinRegister, Table model) =>
Proxy name
-> queryBuilderProvider table -> queryBuilderProvider table
orderBy !Proxy name
name = Proxy name
-> queryBuilderProvider table -> queryBuilderProvider table
forall {k} (name :: Symbol) model (table :: Symbol) value
       (queryBuilderProvider :: Symbol -> *) (joinRegister :: k).
(KnownSymbol table, KnownSymbol name, HasField name model value,
 model ~ GetModelByTableName table,
 HasQueryBuilder queryBuilderProvider joinRegister, Table model) =>
Proxy name
-> queryBuilderProvider table -> queryBuilderProvider table
orderByAsc Proxy name
name
{-# INLINE orderBy #-}

-- | Alias for 'orderByAscJoinedTable'
orderByJoinedTable :: forall model name table value queryBuilderProvider joinRegister table'. (KnownSymbol table, KnownSymbol name, HasField name model value, table ~ GetTableName model, HasQueryBuilder queryBuilderProvider joinRegister, IsJoined model joinRegister, Table model) => Proxy name -> queryBuilderProvider table' -> queryBuilderProvider table'
orderByJoinedTable :: forall {k1} model (name :: Symbol) (table :: Symbol) value
       (queryBuilderProvider :: Symbol -> *) (joinRegister :: k1)
       (table' :: Symbol).
(KnownSymbol table, KnownSymbol name, HasField name model value,
 table ~ GetTableName model,
 HasQueryBuilder queryBuilderProvider joinRegister,
 IsJoined model joinRegister, Table model) =>
Proxy name
-> queryBuilderProvider table' -> queryBuilderProvider table'
orderByJoinedTable !Proxy name
name = forall {k1} model (name :: Symbol) (table :: Symbol) value
       (queryBuilderProvider :: Symbol -> *) (joinRegister :: k1)
       (table' :: Symbol).
(KnownSymbol table, KnownSymbol name, HasField name model value,
 table ~ GetTableName model,
 HasQueryBuilder queryBuilderProvider joinRegister,
 IsJoined model joinRegister, Table model) =>
Proxy name
-> queryBuilderProvider table' -> queryBuilderProvider table'
forall model (name :: Symbol) (table :: Symbol) value
       (queryBuilderProvider :: Symbol -> *) (joinRegister :: k1)
       (table' :: Symbol).
(KnownSymbol table, KnownSymbol name, HasField name model value,
 table ~ GetTableName model,
 HasQueryBuilder queryBuilderProvider joinRegister,
 IsJoined model joinRegister, Table model) =>
Proxy name
-> queryBuilderProvider table' -> queryBuilderProvider table'
orderByAscJoinedTable @model @name @table @value @queryBuilderProvider @joinRegister @table' Proxy name
name
{-# INLINE orderByJoinedTable #-}

-- | Adds an @LIMIT ..@ to your query.
--
--
-- __Example:__ Fetch 10 posts
--
-- > query @Post
-- >     |> limit 10
-- >     |> fetch
-- > -- SELECT * FROM posts LIMIT 10
limit :: (HasQueryBuilder queryBuilderProvider joinRegister) => Int -> queryBuilderProvider model -> queryBuilderProvider model
limit :: forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (model :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
Int -> queryBuilderProvider model -> queryBuilderProvider model
limit !Int
queryLimit queryBuilderProvider model
queryBuilderProvider = QueryBuilder model -> queryBuilderProvider model
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
QueryBuilder table -> queryBuilderProvider table
forall (table :: Symbol).
QueryBuilder table -> queryBuilderProvider table
injectQueryBuilder LimitQueryBuilder { QueryBuilder model
queryBuilder :: QueryBuilder model
queryBuilder :: QueryBuilder model
queryBuilder, Int
queryLimit :: Int
queryLimit :: Int
queryLimit }
    where
        queryBuilder :: QueryBuilder model
queryBuilder = queryBuilderProvider model -> QueryBuilder model
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
queryBuilderProvider table -> QueryBuilder table
forall (table :: Symbol).
queryBuilderProvider table -> QueryBuilder table
getQueryBuilder queryBuilderProvider model
queryBuilderProvider
{-# INLINE limit #-}

-- | Adds an @OFFSET ..@ to your query. Most often used together with @LIMIT...@
--
--
-- __Example:__ Fetch posts 10-20
--
-- > query @Post
-- >     |> limit 10
-- >     |> offset 10
-- >     |> fetch
-- > -- SELECT * FROM posts LIMIT 10 OFFSET 10
offset :: (HasQueryBuilder queryBuilderProvider joinRegister) => Int -> queryBuilderProvider model -> queryBuilderProvider model
offset :: forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (model :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
Int -> queryBuilderProvider model -> queryBuilderProvider model
offset !Int
queryOffset queryBuilderProvider model
queryBuilderProvider = QueryBuilder model -> queryBuilderProvider model
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
QueryBuilder table -> queryBuilderProvider table
forall (table :: Symbol).
QueryBuilder table -> queryBuilderProvider table
injectQueryBuilder OffsetQueryBuilder { QueryBuilder model
queryBuilder :: QueryBuilder model
queryBuilder :: QueryBuilder model
queryBuilder, Int
queryOffset :: Int
queryOffset :: Int
queryOffset }
    where
        queryBuilder :: QueryBuilder model
queryBuilder = queryBuilderProvider model -> QueryBuilder model
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
queryBuilderProvider table -> QueryBuilder table
forall (table :: Symbol).
queryBuilderProvider table -> QueryBuilder table
getQueryBuilder queryBuilderProvider model
queryBuilderProvider
{-# INLINE offset #-}

-- | Adds a @DISTINCT@ to your query.
--
-- Use 'distinct' to remove all duplicate rows from the result
--
-- __Example:__ Fetch distinct books
--
-- > query @Book
-- >     |> distinct
-- >     |> fetch
-- > -- SELECT DISTINCT * FROM books
distinct :: (HasQueryBuilder queryBuilderProvider joinRegister) => queryBuilderProvider table -> queryBuilderProvider table
distinct :: forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
queryBuilderProvider table -> queryBuilderProvider table
distinct = QueryBuilder table -> queryBuilderProvider table
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
QueryBuilder table -> queryBuilderProvider table
forall (table :: Symbol).
QueryBuilder table -> queryBuilderProvider table
injectQueryBuilder (QueryBuilder table -> queryBuilderProvider table)
-> (queryBuilderProvider table -> QueryBuilder table)
-> queryBuilderProvider table
-> queryBuilderProvider table
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. QueryBuilder table -> QueryBuilder table
forall (table :: Symbol). QueryBuilder table -> QueryBuilder table
DistinctQueryBuilder (QueryBuilder table -> QueryBuilder table)
-> (queryBuilderProvider table -> QueryBuilder table)
-> queryBuilderProvider table
-> QueryBuilder table
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. queryBuilderProvider table -> QueryBuilder table
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
queryBuilderProvider table -> QueryBuilder table
forall (table :: Symbol).
queryBuilderProvider table -> QueryBuilder table
getQueryBuilder
{-# INLINE distinct #-}

-- | Adds an @DISTINCT ON .. to your query.
--
-- Use 'distinctOn' to return a single row for each distinct value provided.
--
-- __Example:__ Fetch one book for each categoryId field
--
-- > query @Book
-- >     |> distinctOn #categoryId
-- >     |> fetch
-- > -- SELECT DISTINCT ON (category_id) * FROM books
distinctOn :: forall name model value table queryBuilderProvider joinRegister. (KnownSymbol table, KnownSymbol name, HasField name model value, model ~ GetModelByTableName table, HasQueryBuilder queryBuilderProvider joinRegister, Table model) => Proxy name -> queryBuilderProvider table -> queryBuilderProvider table
distinctOn :: forall {k} (name :: Symbol) model value (table :: Symbol)
       (queryBuilderProvider :: Symbol -> *) (joinRegister :: k).
(KnownSymbol table, KnownSymbol name, HasField name model value,
 model ~ GetModelByTableName table,
 HasQueryBuilder queryBuilderProvider joinRegister, Table model) =>
Proxy name
-> queryBuilderProvider table -> queryBuilderProvider table
distinctOn !Proxy name
name queryBuilderProvider table
queryBuilderProvider = QueryBuilder table -> queryBuilderProvider table
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
QueryBuilder table -> queryBuilderProvider table
forall (table :: Symbol).
QueryBuilder table -> queryBuilderProvider table
injectQueryBuilder DistinctOnQueryBuilder { distinctOnColumn :: Text
distinctOnColumn = Text
columnName, queryBuilder :: QueryBuilder table
queryBuilder = queryBuilderProvider table -> QueryBuilder table
forall {k} (queryBuilderProvider :: Symbol -> *)
       (joinRegister :: k) (table :: Symbol).
HasQueryBuilder queryBuilderProvider joinRegister =>
queryBuilderProvider table -> QueryBuilder table
forall (table :: Symbol).
queryBuilderProvider table -> QueryBuilder table
getQueryBuilder queryBuilderProvider table
queryBuilderProvider}
    where
        columnName :: Text
columnName = Text -> Text -> Text
qualifiedColumnName (forall record. Table record => Text
tableName @model) (forall (symbol :: Symbol). KnownSymbol symbol => Text
symbolToText @name)
{-# INLINE distinctOn #-}