dynami

package module
v0.0.0-...-b5e4ba5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 18, 2017 License: MIT Imports: 19 Imported by: 0

README

Dynami

Dynami provides a simple wrapper over the official Go DynamoDB SDK.

In order to use this package effectively, an understanding of the underlying DynamoDB operations is recommended. For an introduction, click here.

Installation

go get -u github.com/robskie/dynami

Examples

Basic item operations:

type Item struct {
	Key   string `dbkey:"hash"`
	Value string
}

item := Item{"key", "somevalue"}
client := dynami.NewClient(dynami.USEast1, "id", "key")
client.Put("ItemTable", item)

// After some time...

fetched := Item{Key: "key"}
client.Get("ItemTable", &fetched)

// Do something with the fetched item

client.Delete("ItemTable", fetched)

Query example:

type Item struct {
	Hash  string `dbkey:"hash"`
	Range int    `dbkey:"range"`
	Value int
}

client := dynami.NewClient(dynami.USEast1, "id", "key")
it := client.Query("ItemTable").
	HashFilter("Hash", "somehashvalue").
	RangeFilter("Range BETWEEN :rval1 AND :rval2", 1, 10).
	Filter("Value = :fval", 42).
	Run()

for it.HasNext() {
	var item Item
	err := it.Next(&item)
	if err != nil {
		// Do something with item
	}
}

API Reference

Godoc documentation can be found here.

Tests

Before running the tests, install all the test dependencies first.

go get -t github.com/robskie/dynami

Tests can be run locally or online. To run the tests locally, DynamoDB Local must be installed in your home directory. You can download and install DynamoDB Local through these commands:

cd ~
curl -O -L http://dynamodb-local.s3-website-us-west-2.amazonaws.com/dynamodb_local_latest.tar.gz
mkdir DynamoDBLocal
tar -xzf dynamodb_local_latest.tar.gz -C DynamoDBLocal
rm dynamodb_local_latest.tar.gz

Now you can execute the tests locally via this command:

go test -v github.com/robskie/dynami

Or you can run them online by adding an online flag as shown in the following command. This will create test tables in the region set in DYNAMI_TEST_REGION environment variable. Note that these tests can take more than 30 minutes to finish.

DYNAMI_TEST_REGION=us-west-1 go test github.com/robskie/dynami -v -online -timeout 1h

Documentation

Overview

Package dynami provides a simple wrapper over the official Go DynamoDB SDK.

In order to use this package effectively, an understanding of the underlying DynamoDB operations is recommended. For an introduction, please visit https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html

Items

Items can be a map[string]interface{}, a struct, or a pointer to any of those. For an item to be valid, it must contain a nonempty key attribute. Key attributes are restricted to strings, numbers, and binary data. For non-key attributes, the following data types are allowed:

  • Strings
  • Numbers (int, uint, float32, etc)
  • Binary data ([]byte up to 400KB)
  • Maps (map[string]interface{})
  • Structs of any of the above
  • Slices of any of the above

Field Tags

This package uses field tags to specify an item's primary key and secondary index properties. To designate a struct field as the item's primary key use `dbkey:"keytype"`. "keytype" can be "hash" or "range" for hash and range attributes respectively. For secondary index properties, use the tag `dbindex:"type,IndexName"`. If the tagged property is a secondary index key, set "type" to "hash" or "range". If it is a projected attribute, change "type" to "project". In addition to the said tags, `json` and `dynamodbav` tags are also supported. This is useful for optional and ignored attributes.

Example code:

// A and B constitutes the primary key.
// C is the range attribute of LocalIndex.
// D is the hash attribute of GlobalIndex.
// A is the range attribute of GlobalIndex.
// E is projected to both LocalIndex and GlobalIndex.
// F is ignored and not stored.
// G is an optional attribute.
type TaggedStruct struct {
  A string `dbkey:"hash" dbindex:"range,GlobalIndex"`
  B string `dbkey:"range"`
  C string `dbindex:"range,LocalIndex"`
  D string `dbindex:"hash,GlobalIndex"`
  E string `dbindex:"project,LocalIndex,project,GlobalIndex"`
  F string `dynamodbav:"-"`
  G string `dynamodbav:",omitempty"`
}

Note that for local secondary indices, only the range attribute is tagged as shown in struct field C.

Item Operations

There are three basic item operations: PutItem, GetItem, and DeleteItem. Each of these operations accepts an item with nonempty primary key except GetItem which also accepts local and global secondary index keys.

Example code:

type Item struct {
  Key   string `dbkey:"hash"`
  Value string
}

item := Item{"key", "somevalue"}
client := dynami.NewClient(dynami.USEast1, "id", "key")
client.PutItem("ItemTable", item)

// After some time...

fetched := Item{Key: "key"}
client.GetItem("ItemTable", &fetched)

// Do something with the fetched item

client.DeleteItem("ItemTable", fetched)

Batch Operations

Each of the basic item operations also has a batch version: BatchPut, BatchGet and BatchDelete. Each of these operation returns a corresponding batch operation structure which allows method chaining so that multiple items from different tables can be processed at once. Unlike the official SDK, there are no limits on how many items each batch operation can process.

Example code:

type ItemA struct {
  Key   string `dbkey:"hash"`
  Value string
}

type ItemB struct {
  Key   string `dbkey:"hash"`
  Value string
}

// Create items
limitless := 200
itemsA := make([]ItemA, limitless)
itemsB := make([]ItemB, limitless)
for i := 0; i < limitless; i++ {
  itemsA[i] = ItemA{strconv.Itoa(i), "somevalue"}
  itemsB[i] = ItemB{strconv.Itoa(i), "anothervalue"}
}

// Add items
client := dynami.NewClient(dynami.USEast1, "id", "key")
client.BatchPut("ItemTableA", itemsA).
  Put("ItemTableB", itemsB).
  Run()

// Some time later...

fetchedA := make([]ItemA, limitless)
fetchedB := make([]ItemB, limitless)
for i := 0; i < limitless; i++ {
  fetchedA[i] = ItemA{Key: strconv.Itoa(i)}
  fetchedB[i] = ItemB{Key: strconv.Itoa(i)}
}
client.BatchGet("ItemTableA", fetchedA).
  Get("ItemTableB", fetchedB).
  Run()

// Process the items

client.BatchDelete("ItemTableA", fetchedA).
  Delete("ItemTableB", fetchedB).
  Run()

Queries

Queries are built by chaining filters and conditions. Running a query yields a result iterator. Unlike the official SDK, there is no size limit for each query operation.

Queries retrieve items that satisfies a given set of filters. There are three types of filter: a hash filter, a range filter, and a post filter. If a query has a hash filter, running it will perform a DynamoDB query operation, otherwise, a scan operation is performed. The range filter and the post filter accepts a filter expression and a set of values. A simple filter expression might look like this:

AttributeName > :value

A filter expression is composed of an attribute name, a condition, and a value placeholder. Value placeholders always start with a colon (:) and will be replaced with their corresponding filter values when the query is run. For all valid filter expressions see https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.SpecifyingConditions.html#ConditionExpressionReference Note that a range filter only accepts comparator, BETWEEN, and begins_with filter expressions.

Example code:

type Item struct {
  Hash  string `dbkey:"hash"`
  Range int    `dbkey:"range"`
  Value int
}

client := dynami.NewClient(dynami.USEast1, "id", "key")
it := client.Query("ItemTable").
  HashFilter("Hash", "somehashvalue").
  RangeFilter("Range BETWEEN :rval1 AND :rval2", 1, 10).
  Filter("Value = :fval", 42).
  Run()

for it.HasNext() {
  var item Item
  err := it.Next(&item)
  if err != nil {
    // Do something with item
  }
}

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	USEast1 = &Region{
		"us-east-1",
		"dynamodb.us-east-1.amazonaws.com",
		"streams.dynamodb.us-east-1.amazonaws.com",
	}

	USWest1 = &Region{
		"us-west-1",
		"dynamodb.us-west-1.amazonaws.com",
		"streams.dynamodb.us-west-1.amazonaws.com",
	}

	USWest2 = &Region{
		"us-west-2",
		"dynamodb.us-west-2.amazonaws.com",
		"streams.dynamodb.us-west-2.amazonaws.com",
	}

	EUWest1 = &Region{
		"eu-west-1",
		"dynamodb.eu-west-1.amazonaws.com",
		"streams.dynamodb.eu-west-1.amazonaws.com",
	}

	EUCentral1 = &Region{
		"eu-central-1",
		"dynamodb.eu-central-1.amazonaws.com",
		"streams.dynamodb.eu-central-1.amazonaws.com",
	}

	APNortheast1 = &Region{
		"ap-northeast-1",
		"dynamodb.ap-northeast-1.amazonaws.com",
		"streams.dynamodb.ap-northeast-1.amazonaws.com",
	}

	APNortheast2 = &Region{
		"ap-northeast-2",
		"dynamodb.ap-northeast-2.amazonaws.com",
		"streams.dynamodb.ap-northeast-2.amazonaws.com",
	}

	APSoutheast1 = &Region{
		"ap-southeast-1",
		"dynamodb.ap-southeast-1.amazonaws.com",
		"streams.dynamodb.ap-southeast-1.amazonaws.com",
	}

	APSoutheast2 = &Region{
		"ap-southeast-2",
		"dynamodb.ap-southeast-2.amazonaws.com",
		"streams.dynamodb.ap-southeast-2.amazonaws.com",
	}

	SAEast1 = &Region{
		"sa-east-1",
		"dynamodb.sa-east-1.amazonaws.com",
		"streams.dynamodb.sa-east-1.amazonaws.com",
	}
)

These are the list of all supported AWS regions.

View Source
var (
	// ErrNoSuchItem is returned when no item is found for the given key.
	ErrNoSuchItem = errors.New("dynami: no such item")
)

Functions

This section is empty.

Types

type BatchDelete

type BatchDelete struct {
	// contains filtered or unexported fields
}

BatchDelete can delete multiple items from one or more tables.

func (*BatchDelete) Delete

func (b *BatchDelete) Delete(tableName string, items interface{}) *BatchDelete

Delete chains another batch delete operation. This can be called multiple times as long as tableName is unique for each call.

func (*BatchDelete) Run

func (b *BatchDelete) Run() error

Run executes all delete operations in this batch. This may return a BatchError.

type BatchError

type BatchError map[string]map[int]error

BatchError represents a batch operation error. The outer map specifies the table where the error occurred and the inner map contains the index of the input element that caused the error.

func (BatchError) Error

func (e BatchError) Error() string

type BatchGet

type BatchGet struct {
	// contains filtered or unexported fields
}

BatchGet represents a batch get operation. It allows fetching of multiple items from multiple tables.

func (*BatchGet) Get

func (b *BatchGet) Get(
	tableName string,
	items interface{},
	consistent ...bool) *BatchGet

Get adds another batch get operation. This can be called multiple times with the constraint that a unique tableName is used for each call.

func (*BatchGet) Run

func (b *BatchGet) Run() error

Run fetches all the items in this batch. This may return a BatchError.

type BatchPut

type BatchPut struct {
	// contains filtered or unexported fields
}

BatchPut represents a batch put operation. It can put multiple items in multiple tables with one DynamoDB operation.

func (*BatchPut) Put

func (b *BatchPut) Put(tableName string, items interface{}) *BatchPut

Put chains another batch put operation. This can be called one or more times as long as tableName is unique for each call.

func (*BatchPut) Run

func (b *BatchPut) Run() error

Run executes every put operation in this batch. This may return a BatchError.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client represents a DynamoDB client.

func NewClient

func NewClient(region *Region, accessKeyID string, secretAccessKey string) *Client

NewClient creates a new client from the given credentials.

func (*Client) BatchDelete

func (c *Client) BatchDelete(tableName string, items interface{}) *BatchDelete

BatchDelete queues a batch delete operation. items must be a []T or []*T where T is a map[string]interface{} or a struct.

func (*Client) BatchGet

func (c *Client) BatchGet(
	tableName string,
	items interface{},
	consistent ...bool) *BatchGet

BatchGet queues a batch get operation. items must have the same conditions as that in BatchDelete.

func (*Client) BatchPut

func (c *Client) BatchPut(tableName string, items interface{}) *BatchPut

BatchPut queues a batch put operation. items must satisfy the same conditions as that in BatchDelete.

func (*Client) ClearTable

func (c *Client) ClearTable(tableName string) error

ClearTable removes all items from a table. This is achieved by deleting items by batch.

func (*Client) CreateTable

func (c *Client) CreateTable(table *schema.Table) error

CreateTable adds a new table to the current account. This waits for the table to become useable or active before returning.

Example
package main

import (
	"github.com/robskie/dynami"
	"github.com/robskie/dynami/schema"
)

var client = dynami.NewClient(dynami.USEast1, "TEST_ID", "TEST_KEY")

func main() {
	type TestItem struct {
		Hash       string `dbkey:"hash"`
		Range      string `dbkey:"range"`
		GlobalHash string `dbindex:"hash,GlobalIndex"`

		BigValue   []byte
		SmallValue string `dbindex:"project,GlobalIndex"`
	}

	// Create table schema from TestItem
	table := schema.NewTable(
		"TestTable",
		TestItem{},
		map[string]schema.Throughput{
			"TestTable": schema.Throughput{
				Read:  10,
				Write: 20,
			},
			"GlobalIndex": schema.Throughput{
				Read:  30,
				Write: 40,
			},
		},
	)

	// Perform table creation
	client.CreateTable(table)
}
Output:

func (*Client) DeleteItem

func (c *Client) DeleteItem(tableName string, item interface{}) error

DeleteItem removes an item from a table. item must be a map[string]interface{}, struct, or a pointer to any of the two with nonempty primary key.

func (*Client) DeleteTable

func (c *Client) DeleteTable(tableName string) (*schema.Table, error)

DeleteTable removes a table from the current account. This blocks until the table no longer exists.

func (*Client) DescribeTable

func (c *Client) DescribeTable(tableName string) (*schema.Table, error)

DescribeTable provides additional information about the given table. This includes the table's creation date, size in bytes, and the number of items it contains.

func (*Client) GetItem

func (c *Client) GetItem(
	tableName string,
	item interface{}, consistent ...bool) error

GetItem fetches an item from the database. item must be a pointer to a map[string]interface{} or pointer to a struct. In addition to retrieving items by its primary key, it can also get items using its local or global secondary index key, whichever is not empty. This only applies if item is a struct pointer.

Example
package main

import (
	"github.com/robskie/dynami"
)

var client = dynami.NewClient(dynami.USEast1, "TEST_ID", "TEST_KEY")

func main() {
	type TestItem struct {
		Hash       string `dbkey:"hash"`
		Range      string `dbkey:"range"`
		GlobalHash string `dbindex:"hash,GlobalIndex"`

		Value int `dbindex:"project,GlobalIndex"`
	}

	// Fetch using primary key
	itemA := TestItem{
		Hash:  "somehash",
		Range: "somerange",
	}
	client.GetItem("TestTable", &itemA)

	// Fetch using secondary index key
	itemB := TestItem{
		GlobalHash: "anotherhash",
	}
	client.GetItem("TestTable", &itemB)
}
Output:

func (*Client) GetStream

func (c *Client) GetStream(tableName string) (*RecordIterator, error)

GetStream returns the stream record iterator for the given table.

Example
package main

import (
	"github.com/robskie/dynami"
)

var client = dynami.NewClient(dynami.USEast1, "TEST_ID", "TEST_KEY")

func main() {
	type TestItem struct {
		Hash  string `dbkey:"hash"`
		Value int
	}

	it, _ := client.GetStream("TestTable")

	// Process each record as it arrives.
	// Note that this loop doesn't terminate
	// as long as the stream is enabled.
	var item TestItem
	for it.WaitNext() {
		recordType, _ := it.Next(&item)
		switch recordType {
		case dynami.AddedRecord:
			// Process added item
		case dynami.UpdatedRecord:
			// Process updated item
		case dynami.DeletedRecord:
			// Process deleted item
		}
	}
}
Output:

func (*Client) ListTables

func (c *Client) ListTables() ([]string, error)

ListTables returns all table names associated with the current account.

func (*Client) PutItem

func (c *Client) PutItem(tableName string, item interface{}) error

PutItem adds an item to the database. item must be a map[string]interface{}, struct, or a pointer to any of those with nonempty primary key.

func (*Client) Query

func (c *Client) Query(tableName string) *Query

Query returns a new query for the given table.

func (*Client) UpdateTable

func (c *Client) UpdateTable(table *schema.Table) error

UpdateTable modifies the table's throughput or global secondary indices. It can create and delete global secondary indices or update their throughputs. This method waits until all updates are finished.

Example
package main

import (
	"github.com/robskie/dynami"
	"github.com/robskie/dynami/schema"
)

var client = dynami.NewClient(dynami.USEast1, "TEST_ID", "TEST_KEY")

func main() {
	// Get table schema
	table, _ := client.DescribeTable("TestTable")

	// Update table throughput
	table.Throughput = schema.Throughput{
		Read:  10,
		Write: 20,
	}

	// Remove GlobalIndexA
	table.RemoveGlobalSecondaryIndex("GlobalIndexA")

	// Add GlobalIndexB
	table.AddGlobalSecondaryIndex(schema.SecondaryIndex{
		Name: "GlobalIndexB",
		Throughput: schema.Throughput{
			Read:  30,
			Write: 40,
		},
		Key: []schema.Key{
			{
				Name: "GlobalHashB",
				Type: schema.HashKey,
			},
		},
	})
	table.AddAttributes([]schema.Attribute{
		{
			Name: "GlobalHashB",
			Type: schema.StringType,
		},
	})

	// Update GlobalIndexC
	idx, _ := table.GetGlobalSecondaryIndex("GlobalIndexC")
	idx.Throughput = schema.Throughput{
		Read:  50,
		Write: 60,
	}
	table.AddGlobalSecondaryIndex(idx)

	// Perform update
	client.UpdateTable(table)
}
Output:

type ItemIterator

type ItemIterator struct {
	// contains filtered or unexported fields
}

ItemIterator iterates over the result of a query.

func (*ItemIterator) HasNext

func (it *ItemIterator) HasNext() bool

HasNext returns true if there are more query results to iterate over.

func (*ItemIterator) Next

func (it *ItemIterator) Next(item interface{}) error

Next loads the next result to item. item must be a pointer to map[string]interface{} or a pointer to struct.

type Query

type Query struct {
	// contains filtered or unexported fields
}

Query represents a client query. This may perform a DynamoDB query or scan operation depending on whether a hash filter is added or not.

func (*Query) Consistent

func (q *Query) Consistent() *Query

Consistent sets the query to use strongly consistent reads. This is ignored for queries on global secondary indices.

func (*Query) Desc

func (q *Query) Desc() *Query

Desc arranges the result by range key in descending order. The default order is ascending.

func (*Query) Filter

func (q *Query) Filter(expr string, values ...interface{}) *Query

Filter adds a post filter expression to the query. Multiple filters are AND'ed together.

func (*Query) HashFilter

func (q *Query) HashFilter(name string, value interface{}) *Query

HashFilter adds a hash filter to this query. Adding a hash filter to a query makes it perform a DynamoDB query operation instead of a scan operation.

func (*Query) Index

func (q *Query) Index(indexName string) *Query

Index specifies which secondary index to query.

func (*Query) Limit

func (q *Query) Limit(limit int) *Query

Limit limits the number of results returned.

func (*Query) RangeFilter

func (q *Query) RangeFilter(expr string, values ...interface{}) *Query

RangeFilter adds a range filter to this query. Valid expressions are comparison, BETWEEN, and begins_with filter expressions.

func (*Query) Run

func (q *Query) Run() *ItemIterator

Run executes the query and returns a result iterator.

type RecordIterator

type RecordIterator struct {
	// contains filtered or unexported fields
}

RecordIterator iterates through stream records.

func (*RecordIterator) HasNext

func (it *RecordIterator) HasNext() bool

HasNext returns true if there are still some records to iterate over. Use this method to get all currently available records.

func (*RecordIterator) Next

func (it *RecordIterator) Next(record interface{}) (RecordType, error)

Next loads the next record. record must be a pointer to struct or a pointer to a map[string]interface{}.

func (*RecordIterator) WaitNext

func (it *RecordIterator) WaitNext() bool

WaitNext waits for the next record in the stream. This returns false if the stream is closed. Use this to get current and future records.

type RecordType

type RecordType string

RecordType tells if an item is added, updated, or deleted from a table.

These are the valid record types. A record is created when an item is added, updated, or deleted from a table.

type Region

type Region struct {
	Name string

	// DynamoDB and DynamoDB Streams endpoint
	// URLs (hostname only or fully qualified URI)
	DynamoDBEndpoint        string
	DynamoDBStreamsEndpoint string
}

Region defines where DynamoDB services are located.

func GetRegion

func GetRegion(name string) *Region

GetRegion returns a new Region object given a valid AWS region, eg. "us-east-1".

Directories

Path Synopsis
Package schema contains the structs that describe the database table and its fields.
Package schema contains the structs that describe the database table and its fields.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL