{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies    #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Aws.DynamoDb.Commands.Query
-- Copyright   :  Soostone Inc
-- License     :  BSD3
--
-- Maintainer  :  Ozgun Ataman <ozgun.ataman@soostone.com>
-- Stability   :  experimental
--
-- Implementation of Amazon DynamoDb Query command.
--
-- See: @http:\/\/docs.aws.amazon.com\/amazondynamodb\/latest\/APIReference\/API_Query.html@
----------------------------------------------------------------------------

module Aws.DynamoDb.Commands.Query
    ( Query (..)
    , Slice (..)
    , query
    , QueryResponse (..)
    ) where

-------------------------------------------------------------------------------
import           Control.Applicative
import           Data.Aeson
import           Data.Default
import           Data.Maybe
import qualified Data.Text           as T
import           Data.Typeable
import qualified Data.Vector         as V
-------------------------------------------------------------------------------
import           Aws.Core
import           Aws.DynamoDb.Core
-------------------------------------------------------------------------------


-------------------------------------------------------------------------------
-- | 'Slice' is the primary constraint in a 'Query' command, per AWS
-- requirements.
--
-- All 'Query' commands must specify a hash attribute via 'DEq' and
-- optionally provide a secondary range attribute.
data Slice = Slice {
      Slice -> Attribute
sliceHash :: Attribute
    -- ^ Hash value of the primary key or index being used
    , Slice -> Maybe Condition
sliceCond :: Maybe Condition
    -- ^ An optional condition specified on the range component, if
    -- present, of the primary key or index being used.
    }  deriving (Slice -> Slice -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Slice -> Slice -> Bool
$c/= :: Slice -> Slice -> Bool
== :: Slice -> Slice -> Bool
$c== :: Slice -> Slice -> Bool
Eq,Int -> Slice -> ShowS
[Slice] -> ShowS
Slice -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Slice] -> ShowS
$cshowList :: [Slice] -> ShowS
show :: Slice -> String
$cshow :: Slice -> String
showsPrec :: Int -> Slice -> ShowS
$cshowsPrec :: Int -> Slice -> ShowS
Show,ReadPrec [Slice]
ReadPrec Slice
Int -> ReadS Slice
ReadS [Slice]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Slice]
$creadListPrec :: ReadPrec [Slice]
readPrec :: ReadPrec Slice
$creadPrec :: ReadPrec Slice
readList :: ReadS [Slice]
$creadList :: ReadS [Slice]
readsPrec :: Int -> ReadS Slice
$creadsPrec :: Int -> ReadS Slice
Read,Eq Slice
Slice -> Slice -> Bool
Slice -> Slice -> Ordering
Slice -> Slice -> Slice
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Slice -> Slice -> Slice
$cmin :: Slice -> Slice -> Slice
max :: Slice -> Slice -> Slice
$cmax :: Slice -> Slice -> Slice
>= :: Slice -> Slice -> Bool
$c>= :: Slice -> Slice -> Bool
> :: Slice -> Slice -> Bool
$c> :: Slice -> Slice -> Bool
<= :: Slice -> Slice -> Bool
$c<= :: Slice -> Slice -> Bool
< :: Slice -> Slice -> Bool
$c< :: Slice -> Slice -> Bool
compare :: Slice -> Slice -> Ordering
$ccompare :: Slice -> Slice -> Ordering
Ord,Typeable)



-- | A Query command that uses primary keys for an expedient scan.
data Query = Query {
      Query -> Text
qTableName     :: T.Text
    -- ^ Required.
    , Query -> Slice
qKeyConditions :: Slice
    -- ^ Required. Hash or hash-range main condition.
    , Query -> Conditions
qFilter        :: Conditions
    -- ^ Whether to filter results before returning to client
    , Query -> Maybe [Attribute]
qStartKey      :: Maybe [Attribute]
    -- ^ Exclusive start key to resume a previous query.
    , Query -> Maybe Int
qLimit         :: Maybe Int
    -- ^ Whether to limit result set size
    , Query -> Bool
qForwardScan   :: Bool
    -- ^ Set to False for descending results
    , Query -> QuerySelect
qSelect        :: QuerySelect
    -- ^ What to return from 'Query'
    , Query -> ReturnConsumption
qRetCons       :: ReturnConsumption
    , Query -> Maybe Text
qIndex         :: Maybe T.Text
    -- ^ Whether to use a secondary/global index
    , Query -> Bool
qConsistent    :: Bool
    } deriving (Query -> Query -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Query -> Query -> Bool
$c/= :: Query -> Query -> Bool
== :: Query -> Query -> Bool
$c== :: Query -> Query -> Bool
Eq,Int -> Query -> ShowS
[Query] -> ShowS
Query -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Query] -> ShowS
$cshowList :: [Query] -> ShowS
show :: Query -> String
$cshow :: Query -> String
showsPrec :: Int -> Query -> ShowS
$cshowsPrec :: Int -> Query -> ShowS
Show,ReadPrec [Query]
ReadPrec Query
Int -> ReadS Query
ReadS [Query]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Query]
$creadListPrec :: ReadPrec [Query]
readPrec :: ReadPrec Query
$creadPrec :: ReadPrec Query
readList :: ReadS [Query]
$creadList :: ReadS [Query]
readsPrec :: Int -> ReadS Query
$creadsPrec :: Int -> ReadS Query
Read,Eq Query
Query -> Query -> Bool
Query -> Query -> Ordering
Query -> Query -> Query
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Query -> Query -> Query
$cmin :: Query -> Query -> Query
max :: Query -> Query -> Query
$cmax :: Query -> Query -> Query
>= :: Query -> Query -> Bool
$c>= :: Query -> Query -> Bool
> :: Query -> Query -> Bool
$c> :: Query -> Query -> Bool
<= :: Query -> Query -> Bool
$c<= :: Query -> Query -> Bool
< :: Query -> Query -> Bool
$c< :: Query -> Query -> Bool
compare :: Query -> Query -> Ordering
$ccompare :: Query -> Query -> Ordering
Ord,Typeable)


-------------------------------------------------------------------------------
instance ToJSON Query where
    toJSON :: Query -> Value
toJSON Query{Bool
Maybe Int
Maybe [Attribute]
Maybe Text
Text
QuerySelect
ReturnConsumption
Conditions
Slice
qConsistent :: Bool
qIndex :: Maybe Text
qRetCons :: ReturnConsumption
qSelect :: QuerySelect
qForwardScan :: Bool
qLimit :: Maybe Int
qStartKey :: Maybe [Attribute]
qFilter :: Conditions
qKeyConditions :: Slice
qTableName :: Text
qConsistent :: Query -> Bool
qIndex :: Query -> Maybe Text
qRetCons :: Query -> ReturnConsumption
qSelect :: Query -> QuerySelect
qForwardScan :: Query -> Bool
qLimit :: Query -> Maybe Int
qStartKey :: Query -> Maybe [Attribute]
qFilter :: Query -> Conditions
qKeyConditions :: Query -> Slice
qTableName :: Query -> Text
..} = [Pair] -> Value
object forall a b. (a -> b) -> a -> b
$
      forall a. [Maybe a] -> [a]
catMaybes
        [ ((Key
"ExclusiveStartKey" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= ) forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Attribute] -> Value
attributesJson) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe [Attribute]
qStartKey
        , (Key
"Limit" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= ) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Int
qLimit
        , (Key
"IndexName" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= ) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Text
qIndex
        ] forall a. [a] -> [a] -> [a]
++
      Text -> Conditions -> [Pair]
conditionsJson Text
"QueryFilter" Conditions
qFilter forall a. [a] -> [a] -> [a]
++
      forall t. KeyValue t => QuerySelect -> [t]
querySelectJson QuerySelect
qSelect forall a. [a] -> [a] -> [a]
++
      [ Key
"ScanIndexForward" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= Bool
qForwardScan
      , Key
"TableName"forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= Text
qTableName
      , Key
"KeyConditions" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= Slice -> Value
sliceJson Slice
qKeyConditions
      , Key
"ReturnConsumedCapacity" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= ReturnConsumption
qRetCons
      , Key
"ConsistentRead" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= Bool
qConsistent
      ]


-------------------------------------------------------------------------------
-- | Construct a minimal 'Query' request.
query
    :: T.Text
    -- ^ Table name
    -> Slice
    -- ^ Primary key slice for query
    -> Query
query :: Text -> Slice -> Query
query Text
tn Slice
sl = Text
-> Slice
-> Conditions
-> Maybe [Attribute]
-> Maybe Int
-> Bool
-> QuerySelect
-> ReturnConsumption
-> Maybe Text
-> Bool
-> Query
Query Text
tn Slice
sl forall a. Default a => a
def forall a. Maybe a
Nothing forall a. Maybe a
Nothing Bool
True forall a. Default a => a
def forall a. Default a => a
def forall a. Maybe a
Nothing Bool
False


-- | Response to a 'Query' query.
data QueryResponse = QueryResponse {
      QueryResponse -> Vector Item
qrItems    :: V.Vector Item
    , QueryResponse -> Maybe [Attribute]
qrLastKey  :: Maybe [Attribute]
    , QueryResponse -> Int
qrCount    :: Int
    , QueryResponse -> Int
qrScanned  :: Int
    , QueryResponse -> Maybe ConsumedCapacity
qrConsumed :: Maybe ConsumedCapacity
    } deriving (QueryResponse -> QueryResponse -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: QueryResponse -> QueryResponse -> Bool
$c/= :: QueryResponse -> QueryResponse -> Bool
== :: QueryResponse -> QueryResponse -> Bool
$c== :: QueryResponse -> QueryResponse -> Bool
Eq,Int -> QueryResponse -> ShowS
[QueryResponse] -> ShowS
QueryResponse -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [QueryResponse] -> ShowS
$cshowList :: [QueryResponse] -> ShowS
show :: QueryResponse -> String
$cshow :: QueryResponse -> String
showsPrec :: Int -> QueryResponse -> ShowS
$cshowsPrec :: Int -> QueryResponse -> ShowS
Show,ReadPrec [QueryResponse]
ReadPrec QueryResponse
Int -> ReadS QueryResponse
ReadS [QueryResponse]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [QueryResponse]
$creadListPrec :: ReadPrec [QueryResponse]
readPrec :: ReadPrec QueryResponse
$creadPrec :: ReadPrec QueryResponse
readList :: ReadS [QueryResponse]
$creadList :: ReadS [QueryResponse]
readsPrec :: Int -> ReadS QueryResponse
$creadsPrec :: Int -> ReadS QueryResponse
Read,Eq QueryResponse
QueryResponse -> QueryResponse -> Bool
QueryResponse -> QueryResponse -> Ordering
QueryResponse -> QueryResponse -> QueryResponse
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: QueryResponse -> QueryResponse -> QueryResponse
$cmin :: QueryResponse -> QueryResponse -> QueryResponse
max :: QueryResponse -> QueryResponse -> QueryResponse
$cmax :: QueryResponse -> QueryResponse -> QueryResponse
>= :: QueryResponse -> QueryResponse -> Bool
$c>= :: QueryResponse -> QueryResponse -> Bool
> :: QueryResponse -> QueryResponse -> Bool
$c> :: QueryResponse -> QueryResponse -> Bool
<= :: QueryResponse -> QueryResponse -> Bool
$c<= :: QueryResponse -> QueryResponse -> Bool
< :: QueryResponse -> QueryResponse -> Bool
$c< :: QueryResponse -> QueryResponse -> Bool
compare :: QueryResponse -> QueryResponse -> Ordering
$ccompare :: QueryResponse -> QueryResponse -> Ordering
Ord)


instance FromJSON QueryResponse where
    parseJSON :: Value -> Parser QueryResponse
parseJSON (Object Object
v) = Vector Item
-> Maybe [Attribute]
-> Int
-> Int
-> Maybe ConsumedCapacity
-> QueryResponse
QueryResponse
        forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:?  Key
"Items" forall a. Parser (Maybe a) -> a -> Parser a
.!= forall a. Vector a
V.empty
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> ((do Value
o <- Object
v forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"LastEvaluatedKey"
                 forall a. a -> Maybe a
Just forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Value -> Parser [Attribute]
parseAttributeJson Value
o)
             forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing)
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v forall a. FromJSON a => Object -> Key -> Parser a
.:  Key
"Count"
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v forall a. FromJSON a => Object -> Key -> Parser a
.:  Key
"ScannedCount"
        forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"ConsumedCapacity"
    parseJSON Value
_ = forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"QueryResponse must be an object."


instance Transaction Query QueryResponse


instance SignQuery Query where
    type ServiceConfiguration Query = DdbConfiguration
    signQuery :: forall queryType.
Query
-> ServiceConfiguration Query queryType
-> SignatureData
-> SignedQuery
signQuery Query
gi = forall a qt.
ToJSON a =>
ByteString
-> a -> DdbConfiguration qt -> SignatureData -> SignedQuery
ddbSignQuery ByteString
"Query" Query
gi


instance ResponseConsumer r QueryResponse where
    type ResponseMetadata QueryResponse = DdbResponse
    responseConsumer :: Request
-> r
-> IORef (ResponseMetadata QueryResponse)
-> HTTPResponseConsumer QueryResponse
responseConsumer Request
_ r
_ IORef (ResponseMetadata QueryResponse)
ref Response (ConduitM () ByteString (ResourceT IO) ())
resp
        = forall a. FromJSON a => IORef DdbResponse -> HTTPResponseConsumer a
ddbResponseConsumer IORef (ResponseMetadata QueryResponse)
ref Response (ConduitM () ByteString (ResourceT IO) ())
resp


instance AsMemoryResponse QueryResponse where
    type MemoryResponse QueryResponse = QueryResponse
    loadToMemory :: QueryResponse -> ResourceT IO (MemoryResponse QueryResponse)
loadToMemory = forall (m :: * -> *) a. Monad m => a -> m a
return


instance ListResponse QueryResponse Item where
    listResponse :: QueryResponse -> [Item]
listResponse = forall a. Vector a -> [a]
V.toList forall b c a. (b -> c) -> (a -> b) -> a -> c
. QueryResponse -> Vector Item
qrItems


instance IteratedTransaction Query QueryResponse where
    nextIteratedRequest :: Query -> QueryResponse -> Maybe Query
nextIteratedRequest Query
request QueryResponse
response = case QueryResponse -> Maybe [Attribute]
qrLastKey QueryResponse
response of
        Maybe [Attribute]
Nothing -> forall a. Maybe a
Nothing
        Maybe [Attribute]
key -> forall a. a -> Maybe a
Just Query
request { qStartKey :: Maybe [Attribute]
qStartKey = Maybe [Attribute]
key }


sliceJson :: Slice -> Value
sliceJson :: Slice -> Value
sliceJson Slice{Maybe Condition
Attribute
sliceCond :: Maybe Condition
sliceHash :: Attribute
sliceCond :: Slice -> Maybe Condition
sliceHash :: Slice -> Attribute
..} = [Pair] -> Value
object (forall a b. (a -> b) -> [a] -> [b]
map Condition -> Pair
conditionJson [Condition]
cs)
    where
      cs :: [Condition]
cs = forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Condition
sliceCond forall a. [a] -> [a] -> [a]
++ [Condition
hashCond]
      hashCond :: Condition
hashCond = Text -> CondOp -> Condition
Condition (Attribute -> Text
attrName Attribute
sliceHash) (DValue -> CondOp
DEq (Attribute -> DValue
attrVal Attribute
sliceHash))