{-# language DataKinds #-}

module Rel8.Query.Exists
  ( exists, inQuery
  , whereExists, with, withBy
  , whereNotExists, without, withoutBy
  )
where

-- base
import Prelude hiding ( filter )

-- opaleye
import qualified Opaleye.Operators as Opaleye

-- rel8
import Rel8.Expr ( Expr )
import Rel8.Query ( Query )
import Rel8.Query.Filter ( filter )
import Rel8.Query.Maybe ( optional )
import Rel8.Query.Opaleye ( mapOpaleye )
import Rel8.Table.Eq ( EqTable, (==:) )
import Rel8.Table.Maybe ( isJustTable )


-- | Checks if a query returns at least one row.
exists :: Query a -> Query (Expr Bool)
exists :: Query a -> Query (Expr Bool)
exists = (MaybeTable () -> Expr Bool)
-> Query (MaybeTable ()) -> Query (Expr Bool)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap MaybeTable () -> Expr Bool
forall a. MaybeTable a -> Expr Bool
isJustTable (Query (MaybeTable ()) -> Query (Expr Bool))
-> (Query a -> Query (MaybeTable ()))
-> Query a
-> Query (Expr Bool)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query () -> Query (MaybeTable ())
forall a. Query a -> Query (MaybeTable a)
optional (Query () -> Query (MaybeTable ()))
-> (Query a -> Query ()) -> Query a -> Query (MaybeTable ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Query a -> Query ()
forall a. Query a -> Query ()
whereExists
-- FIXME: change this when b7aacc07c6392654cae439fc3b997620c3aa7a87 makes it
-- into a release of Opaleye


inQuery :: EqTable a => a -> Query a -> Query (Expr Bool)
inQuery :: a -> Query a -> Query (Expr Bool)
inQuery a
a = Query a -> Query (Expr Bool)
forall a. Query a -> Query (Expr Bool)
exists (Query a -> Query (Expr Bool))
-> (Query a -> Query a) -> Query a -> Query (Expr Bool)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Query a -> (a -> Query a) -> Query a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (a -> Expr Bool) -> a -> Query a
forall a. (a -> Expr Bool) -> a -> Query a
filter (a
a a -> a -> Expr Bool
forall a. EqTable a => a -> a -> Expr Bool
==:))


-- | Produce the empty query if the given query returns no rows. @whereExists@
-- is equivalent to @WHERE EXISTS@ in SQL.
whereExists :: Query a -> Query ()
whereExists :: Query a -> Query ()
whereExists = (Select a -> Select ()) -> Query a -> Query ()
forall a b. (Select a -> Select b) -> Query a -> Query b
mapOpaleye Select a -> Select ()
forall a b. SelectArr a b -> SelectArr a ()
Opaleye.restrictExists


-- | Produce the empty query if the given query returns rows. @whereNotExists@
-- is equivalent to @WHERE NOT EXISTS@ in SQL.
whereNotExists :: Query a -> Query ()
whereNotExists :: Query a -> Query ()
whereNotExists = (Select a -> Select ()) -> Query a -> Query ()
forall a b. (Select a -> Select b) -> Query a -> Query b
mapOpaleye Select a -> Select ()
forall a b. SelectArr a b -> SelectArr a ()
Opaleye.restrictNotExists


-- | @with@ is similar to 'filter', but allows the predicate to be a full query.
--
-- @with f a = a <$ whereExists (f a)@, but this form matches 'filter'.
with :: (a -> Query b) -> a -> Query a
with :: (a -> Query b) -> a -> Query a
with a -> Query b
f a
a = a
a a -> Query () -> Query a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Query b -> Query ()
forall a. Query a -> Query ()
whereExists (a -> Query b
f a
a)


-- | Like @with@, but with a custom membership test.
withBy :: (a -> b -> Expr Bool) -> Query b -> a -> Query a
withBy :: (a -> b -> Expr Bool) -> Query b -> a -> Query a
withBy a -> b -> Expr Bool
predicate Query b
bs = (a -> Query b) -> a -> Query a
forall a b. (a -> Query b) -> a -> Query a
with ((a -> Query b) -> a -> Query a) -> (a -> Query b) -> a -> Query a
forall a b. (a -> b) -> a -> b
$ \a
a -> Query b
bs Query b -> (b -> Query b) -> Query b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (b -> Expr Bool) -> b -> Query b
forall a. (a -> Expr Bool) -> a -> Query a
filter (a -> b -> Expr Bool
predicate a
a)


-- | Filter rows where @a -> Query b@ yields no rows.
without :: (a -> Query b) -> a -> Query a
without :: (a -> Query b) -> a -> Query a
without a -> Query b
f a
a = a
a a -> Query () -> Query a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Query b -> Query ()
forall a. Query a -> Query ()
whereNotExists (a -> Query b
f a
a)


-- | Like @without@, but with a custom membership test.
withoutBy :: (a -> b -> Expr Bool) -> Query b -> a -> Query a
withoutBy :: (a -> b -> Expr Bool) -> Query b -> a -> Query a
withoutBy a -> b -> Expr Bool
predicate Query b
bs = (a -> Query b) -> a -> Query a
forall a b. (a -> Query b) -> a -> Query a
without ((a -> Query b) -> a -> Query a) -> (a -> Query b) -> a -> Query a
forall a b. (a -> b) -> a -> b
$ \a
a -> Query b
bs Query b -> (b -> Query b) -> Query b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (b -> Expr Bool) -> b -> Query b
forall a. (a -> Expr Bool) -> a -> Query a
filter (a -> b -> Expr Bool
predicate a
a)