License | BSD-style |
---|---|
Maintainer | palkovsky.ondrej@gmail.com |
Stability | experimental |
Portability | portable |
Safe Haskell | None |
Language | Haskell2010 |
Type-safe library for accessing DynamoDB database.
- data DynamoException = DynamoException Text
- data Consistency
- data Direction
- data Column typ coltype col
- (<.>) :: InCollection col2 (UnMaybe typ) NestedPath => Column typ TypColumn col1 -> Column typ2 TypColumn col2 -> Column typ2 TypColumn col1
- (<!>) :: Column [typ] TypColumn col -> Int -> Column typ TypColumn col
- (<!:>) :: IsText key => Column (HashMap key typ) TypColumn col -> key -> Column typ TypColumn col
- getItem :: forall m a r. (MonadAWS m, DynamoTable a r) => Consistency -> Proxy a -> PrimaryKey a r -> m (Maybe a)
- getItemBatch :: forall m a r. (MonadAWS m, DynamoTable a r) => Consistency -> [PrimaryKey a r] -> m [a]
- data QueryOpts a hash range
- queryOpts :: hash -> QueryOpts a hash range
- qConsistentRead :: forall a hash range. Lens' (QueryOpts a hash range) Consistency
- qStartKey :: forall a hash range. Lens' (QueryOpts a hash range) (Maybe (hash, range))
- qDirection :: forall a hash range. Lens' (QueryOpts a hash range) Direction
- qFilterCondition :: forall a hash range a. Lens (QueryOpts a hash range) (QueryOpts a hash range) (Maybe (FilterCondition a)) (Maybe (FilterCondition a))
- qHashKey :: forall a hash range. Lens' (QueryOpts a hash range) hash
- qRangeCondition :: forall a hash range. Lens' (QueryOpts a hash range) (Maybe (RangeOper range))
- qLimit :: forall a hash range. Lens' (QueryOpts a hash range) (Maybe Natural)
- query :: forall a t m range hash. (CanQuery a t hash range, MonadAWS m) => Proxy a -> QueryOpts a hash range -> Int -> m ([a], Maybe (PrimaryKey a WithRange))
- querySimple :: forall a t m hash range. (CanQuery a t hash range, MonadAWS m) => Proxy a -> hash -> Maybe (RangeOper range) -> Direction -> Int -> m [a]
- queryCond :: forall a t m hash range. (CanQuery a t hash range, MonadAWS m) => Proxy a -> hash -> Maybe (RangeOper range) -> FilterCondition a -> Direction -> Int -> m [a]
- querySource :: forall a t m hash range. (CanQuery a t hash range, MonadAWS m) => Proxy a -> QueryOpts a hash range -> Source m a
- querySourceChunks :: forall a t m hash range. (CanQuery a t hash range, MonadAWS m) => Proxy a -> QueryOpts a hash range -> Source m [a]
- querySourceByKey :: forall a parent hash rest v1 m r. (DynamoIndex a parent NoRange, Code a ~ '[hash ': rest], DynamoScalar v1 hash, MonadAWS m, DynamoTable parent r) => Proxy a -> hash -> Source m a
- queryOverIndex :: forall a t m v1 v2 hash r2 range rest parent. (CanQuery a t hash range, MonadAWS m, Code a ~ '[hash ': (range ': rest)], DynamoIndex a parent WithRange, ContainsTableKey a parent (PrimaryKey parent r2), DynamoTable parent r2, DynamoScalar v1 hash, DynamoScalar v2 range) => Proxy a -> QueryOpts a hash range -> Source m parent
- data ScanOpts a r
- scanOpts :: ScanOpts a r
- sFilterCondition :: forall a r. Lens' (ScanOpts a r) (Maybe (FilterCondition a))
- sConsistentRead :: forall a r. Lens' (ScanOpts a r) Consistency
- sLimit :: forall a r. Lens' (ScanOpts a r) (Maybe Natural)
- sParallel :: forall a r. Lens' (ScanOpts a r) (Maybe (Natural, Natural))
- sStartKey :: forall a r r. Lens (ScanOpts a r) (ScanOpts a r) (Maybe (PrimaryKey a r)) (Maybe (PrimaryKey a r))
- scan :: (MonadAWS m, TableScan a r t) => Proxy a -> ScanOpts a r -> Int -> m ([a], Maybe (PrimaryKey a r))
- scanSource :: (MonadAWS m, TableScan a r t) => Proxy a -> ScanOpts a r -> Source m a
- scanSourceChunks :: (MonadAWS m, TableScan a r t) => Proxy a -> ScanOpts a r -> Source m [a]
- scanCond :: forall a m r t. (MonadAWS m, TableScan a r t) => Proxy a -> FilterCondition a -> Int -> m [a]
- leftJoin :: forall a b m r. (MonadAWS m, DynamoTable a r, Ord (PrimaryKey a r), ContainsTableKey a a (PrimaryKey a r)) => Consistency -> Proxy a -> (b -> Maybe (PrimaryKey a r)) -> Conduit [b] m [(b, Maybe a)]
- innerJoin :: forall a b m r. (MonadAWS m, DynamoTable a r, Ord (PrimaryKey a r), ContainsTableKey a a (PrimaryKey a r)) => Consistency -> Proxy a -> (b -> Maybe (PrimaryKey a r)) -> Conduit [b] m [(b, a)]
- putItem :: (MonadAWS m, DynamoTable a r) => a -> m ()
- putItemBatch :: forall m a r. (MonadAWS m, DynamoTable a r) => [a] -> m ()
- insertItem :: forall a r m. (MonadAWS m, DynamoTable a r) => a -> m ()
- updateItemByKey :: forall a m r. (MonadAWS m, DynamoTable a r) => Proxy a -> PrimaryKey a r -> Action a -> m a
- updateItemByKey_ :: forall a m r. (MonadAWS m, DynamoTable a r) => Proxy a -> PrimaryKey a r -> Action a -> m ()
- updateItemCond_ :: forall a m r. (MonadAWS m, DynamoTable a r) => Proxy a -> PrimaryKey a r -> FilterCondition a -> Action a -> m ()
- deleteItemByKey :: forall m a r. (MonadAWS m, DynamoTable a r) => Proxy a -> PrimaryKey a r -> m ()
- deleteItemCondByKey :: forall m a r. (MonadAWS m, DynamoTable a r) => Proxy a -> PrimaryKey a r -> FilterCondition a -> m ()
- deleteItemBatchByKey :: forall m a r. (MonadAWS m, DynamoTable a r) => Proxy a -> [PrimaryKey a r] -> m ()
- deleteTable :: (MonadAWS m, DynamoTable a r) => Proxy a -> m ()
- tableKey :: forall a parent key. ContainsTableKey a parent key => a -> key
- class (DynamoCollection a r IsTable, HasPrimaryKey a r IsTable) => DynamoTable a r | a -> r
- class DynamoCollection a r IsIndex => DynamoIndex a parent r | a -> parent r
- type PrimaryKey a r = PrimaryKey' (Code a) r
- class ContainsTableKey a parent key | a -> parent key
- class (DynamoCollection a WithRange t, HasPrimaryKey a WithRange t) => CanQuery a t hash range | a -> hash range
- class (DynamoCollection a r t, HasPrimaryKey a r t) => TableScan a r t
Introduction
This library is operated in the following way:
- Create instances for your custom types using Database.DynamoDB.Types
- Create ordinary datatypes with records
- Use functions from Database.DynamoDB.TH to derive appropriate instances
- Optionally call generated migration function to automatically create tables and indices
- Call functions from this module to access the database
The library does its best to ensure that only correct DynamoDB operations are allowed. There are some limitations of DynamoDB regarding access to empty values, but the library takes care of this reasonably well.
Example of use
You may need to set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables.
data Test = Test { category :: T.Text , messageid :: T.Text , subject :: T.Text } deriving (Show) mkTableDefs "migrate" (tableConfig "" (''Test, WithRange) [] [])
This code creates appropriate instances for the table and the columns. It creates
global variables colCategory
, colMessageid
and colSubject
that can be used
in filtering conditions or update queries.
main = do lgr <- newLogger Info stdout env <- newEnv NorthVirginia Discover -- Override, use DynamoDD on localhost let dynamo = setEndpoint False "localhost" 8000 dynamoDB let newenv = env & configure dynamo & set envLogger lgr runResourceT $ runAWS newenv $ do -- Create tables and indexes migrate mempty Nothing -- Save data to database putItem (Test "news" "1-2-3-4" "New subject") -- Fetch data given primary key item <- getItem Eventually tTest ("news", "1-2-3-4") liftIO $ print item -- (item :: Maybe Test) -- Scan data using filter condition, return 10 results items <- scanCond tTest (subject' ==. "New subejct") 10 print items -- (items :: [Test])
See examples and test directories for more detail examples.
Proxies
In order to avoid ambiguity errors, most API calls need a Proxy
argument
to find out on which table or index to operate. These proxies are automatically
generated as a name of type prepended with "t" for tables and "i" for indexes.
A proxy for table Test will have name tTest, for index TestIndex the name will be iTestIndex.
Lens support
If the field names in the table record start with an underscore, the lens get automatically generated for accessing the fields. The lens are polymorphic, you can use them to access the fields of both main table and all the indexes.
data Test = Test { _messageid :: T.Text , _category :: T.Text , _subject :: T.Text } deriving (Show) data TestIndex = TestIndex { i_category :: T.Text , i_messageid :: T.Text } mkTableDefs "migrate" (tableConfig "" (''Test, WithRange) [(''TestIndex, NoRange)] []) doWithTest :: Test -> ... doWithTest item = (item ^. category) ... doWithItemIdx :: TestIndex -> .. getCategoryIdx item = (item ^. category) ...
Conversion support
Given a type Test
and an index type TestIndex
,
a function toTest
is created that converts from TestIndex
to Test
.
Such function is created only if TestIndex
projects
all fields from Test
.
Data types
data DynamoException Source #
Exceptions thrown by some dynamodb-simple actions.
data Consistency Source #
Parameter for queries involving read consistency settings.
Query direction
data Column typ coltype col Source #
Representation of a column for filter queries
- typ - datatype of column (Int, Text..)
- coltype - TypColumn or TypSize (result of size(column))
- col - instance of ColumnInfo, uniquely identify a column
Attribute path combinators
(<.>) :: InCollection col2 (UnMaybe typ) NestedPath => Column typ TypColumn col1 -> Column typ2 TypColumn col2 -> Column typ2 TypColumn col1 infixl 7 Source #
Combine attributes from nested structures.
address' <.> street'
(<!>) :: Column [typ] TypColumn col -> Int -> Column typ TypColumn col infixl 8 Source #
Access an index in a nested list.
users' <!> 0 <.> name'
(<!:>) :: IsText key => Column (HashMap key typ) TypColumn col -> key -> Column typ TypColumn col infixl 8 Source #
Access a key in a nested hashmap.
phones' <!:> "mobile" <.> number'
Fetching items
getItem :: forall m a r. (MonadAWS m, DynamoTable a r) => Consistency -> Proxy a -> PrimaryKey a r -> m (Maybe a) Source #
Read item from the database; primary key is either a hash key or (hash,range) tuple depending on the table.
getItemBatch :: forall m a r. (MonadAWS m, DynamoTable a r) => Consistency -> [PrimaryKey a r] -> m [a] Source #
Get batch of items.
Query options
qConsistentRead :: forall a hash range. Lens' (QueryOpts a hash range) Consistency Source #
qFilterCondition :: forall a hash range a. Lens (QueryOpts a hash range) (QueryOpts a hash range) (Maybe (FilterCondition a)) (Maybe (FilterCondition a)) Source #
qRangeCondition :: forall a hash range. Lens' (QueryOpts a hash range) (Maybe (RangeOper range)) Source #
Performing query
:: (CanQuery a t hash range, MonadAWS m) | |
=> Proxy a | |
-> QueryOpts a hash range | |
-> Int | Maximum number of items to fetch |
-> m ([a], Maybe (PrimaryKey a WithRange)) |
Fetch exactly the required count of items even when
it means more calls to dynamodb. Return last evaluted key if end of data
was not reached. Use qStartKey
to continue reading the query.
:: (CanQuery a t hash range, MonadAWS m) | |
=> Proxy a | Proxy type of a table to query |
-> hash | Hash key |
-> Maybe (RangeOper range) | Range condition |
-> Direction | Scan direction |
-> Int | Maximum number of items to fetch |
-> m [a] |
Perform a simple, eventually consistent, query.
Simple to use function to query limited amount of data from database.
:: (CanQuery a t hash range, MonadAWS m) | |
=> Proxy a | |
-> hash | Hash key |
-> Maybe (RangeOper range) | Range condition |
-> FilterCondition a | |
-> Direction | Scan direction |
-> Int | Maximum number of items to fetch |
-> m [a] |
Query with condition
querySource :: forall a t m hash range. (CanQuery a t hash range, MonadAWS m) => Proxy a -> QueryOpts a hash range -> Source m a Source #
Generic query function. You can query table or indexes that have a range key defined. The filter condition cannot access the hash and range keys.
querySourceChunks :: forall a t m hash range. (CanQuery a t hash range, MonadAWS m) => Proxy a -> QueryOpts a hash range -> Source m [a] Source #
Same as querySource
, but return data in original chunks.
querySourceByKey :: forall a parent hash rest v1 m r. (DynamoIndex a parent NoRange, Code a ~ '[hash ': rest], DynamoScalar v1 hash, MonadAWS m, DynamoTable parent r) => Proxy a -> hash -> Source m a Source #
Conduit to query global indexes with no range key; in case anyone needed it
queryOverIndex :: forall a t m v1 v2 hash r2 range rest parent. (CanQuery a t hash range, MonadAWS m, Code a ~ '[hash ': (range ': rest)], DynamoIndex a parent WithRange, ContainsTableKey a parent (PrimaryKey parent r2), DynamoTable parent r2, DynamoScalar v1 hash, DynamoScalar v2 range) => Proxy a -> QueryOpts a hash range -> Source m parent Source #
Query an index, fetch primary key from the result and immediately read full items from the main table.
You cannot perform strongly consistent reads on Global indexes; if you set
the qConsistentRead
to Strongly
, fetch from global indexes is still done
as eventually consistent. Queries on local indexes are performed according to the settings.
Scan options
Record for defining scan command. Use lenses to set the content.
sParallel: (Segment number, Total segments)
sFilterCondition :: forall a r. Lens' (ScanOpts a r) (Maybe (FilterCondition a)) Source #
sConsistentRead :: forall a r. Lens' (ScanOpts a r) Consistency Source #
sStartKey :: forall a r r. Lens (ScanOpts a r) (ScanOpts a r) (Maybe (PrimaryKey a r)) (Maybe (PrimaryKey a r)) Source #
Performing scan
:: (MonadAWS m, TableScan a r t) | |
=> Proxy a | |
-> ScanOpts a r | Scan settings |
-> Int | Required result count |
-> m ([a], Maybe (PrimaryKey a r)) | list of results, lastEvalutedKey or Nothing if end of data reached |
Function to call bounded scans. Tries to return exactly requested number of items.
Use sStartKey
to continue the scan.
scanSource :: (MonadAWS m, TableScan a r t) => Proxy a -> ScanOpts a r -> Source m a Source #
Conduit source for running a scan.
scanSourceChunks :: (MonadAWS m, TableScan a r t) => Proxy a -> ScanOpts a r -> Source m [a] Source #
Conduit source for running scan; the same as scanSource
, but return results in chunks as they come.
scanCond :: forall a m r t. (MonadAWS m, TableScan a r t) => Proxy a -> FilterCondition a -> Int -> m [a] Source #
Scan table using a given filter condition.
scanCond (colAddress <!:> "Home" <.> colCity ==. "London") 10
Helper conduits
:: (MonadAWS m, DynamoTable a r, Ord (PrimaryKey a r), ContainsTableKey a a (PrimaryKey a r)) | |
=> Consistency | |
-> Proxy a | Proxy type for the right table |
-> (b -> Maybe (PrimaryKey a r)) | |
-> Conduit [b] m [(b, Maybe a)] |
Conduit to do a left join on the items being sent; supposed to be used with querySourceChunks.
The 'foreign key' must have an Ord
to facilitate faster searching.
:: (MonadAWS m, DynamoTable a r, Ord (PrimaryKey a r), ContainsTableKey a a (PrimaryKey a r)) | |
=> Consistency | |
-> Proxy a | Proxy type for the right table |
-> (b -> Maybe (PrimaryKey a r)) | |
-> Conduit [b] m [(b, a)] |
The same as leftJoin
, but discard items that do not exist in the right table.
Data entry
putItem :: (MonadAWS m, DynamoTable a r) => a -> m () Source #
Write item into the database; overwrite any previously existing item with the same primary key.
putItemBatch :: forall m a r. (MonadAWS m, DynamoTable a r) => [a] -> m () Source #
Batch write into the database.
The batch is divided to 25-item chunks, each is sent and retried separately. If a batch fails on dynamodb exception, it is raised.
Note: On exception, the information about which items were saved is unavailable
insertItem :: forall a r m. (MonadAWS m, DynamoTable a r) => a -> m () Source #
Write item into the database only if it doesn't already exist.
Data modification
updateItemByKey :: forall a m r. (MonadAWS m, DynamoTable a r) => Proxy a -> PrimaryKey a r -> Action a -> m a Source #
Update item in a database, return an updated version of the item.
updateItemByKey_ :: forall a m r. (MonadAWS m, DynamoTable a r) => Proxy a -> PrimaryKey a r -> Action a -> m () Source #
Update item in a table.
updateItem (Proxy :: Proxy Test) (12, "2") (colCount +=. 100)
updateItemCond_ :: forall a m r. (MonadAWS m, DynamoTable a r) => Proxy a -> PrimaryKey a r -> FilterCondition a -> Action a -> m () Source #
Update item in a table while specifying a condition.
Deleting data
deleteItemByKey :: forall m a r. (MonadAWS m, DynamoTable a r) => Proxy a -> PrimaryKey a r -> m () Source #
Delete item from the database by specifying the primary key.
deleteItemCondByKey :: forall m a r. (MonadAWS m, DynamoTable a r) => Proxy a -> PrimaryKey a r -> FilterCondition a -> m () Source #
Delete item from the database by specifying the primary key and a condition. Throws AWS exception if the condition does not succeed.
deleteItemBatchByKey :: forall m a r. (MonadAWS m, DynamoTable a r) => Proxy a -> [PrimaryKey a r] -> m () Source #
Batch version of deleteItemByKey
.
Note: Because the requests are chunked, the information about which items were deleted in case of exception is unavailable.
Delete table
deleteTable :: (MonadAWS m, DynamoTable a r) => Proxy a -> m () Source #
Delete a table from DynamoDB.
Utility functions
tableKey :: forall a parent key. ContainsTableKey a parent key => a -> key Source #
Extract primary key from a record.
You can use this on both main table or on index tables if they contain the primary key from the main table. Table key is always projected to indexes anyway, so just define it in every index.
Typeclasses
class (DynamoCollection a r IsTable, HasPrimaryKey a r IsTable) => DynamoTable a r | a -> r Source #
Descritpion of dynamo table
tableName
class DynamoCollection a r IsIndex => DynamoIndex a parent r | a -> parent r Source #
Class representing a Global Secondary Index
indexName, indexIsLocal
type PrimaryKey a r = PrimaryKey' (Code a) r Source #
Type family that returns a primary key of a table/index depending on the RangeType
parameter.
class ContainsTableKey a parent key | a -> parent key Source #
Class for indexes that contain primary key of the parent table
dTableKey