dynamorm

package module
v0.0.5 Latest Latest
Warning

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

Go to latest
Published: Aug 19, 2025 License: MIT Imports: 8 Imported by: 0

README

DynamORM

Go Reference Go Report Card

DynamORM is a lightweight, flexible Object-Relational Mapping (ORM) library for Amazon DynamoDB in Go. It provides a simple, type-safe way to interact with DynamoDB tables using Go structs.

Features

  • Simple, intuitive API for common DynamoDB operations
  • Type-safe mapping between Go structs and DynamoDB items
  • Support for primary keys and global secondary indexes (GSIs)
  • Composite primary key support and integrity
  • Flexible query and filtering capabilities
  • Lifecycle hooks for entities
  • Automatic pagination
  • Built on AWS SDK v2 for Go

Installation

go get github.com/vpriem/dynamorm

Quick Start

Entity

An entity must implement the Entity interface:

  • PkSk(): Returns the partition key and sort key (stored in the PK and SK columns respectively)
  • GSI1(): Returns the partition key and sort key for the first GSI (stored in GSI1PK and GSI1SK columns)
  • GSI2(): Returns the partition key and sort key for the second GSI (stored in GSI2PK and GSI2SK columns)
  • BeforeSave(): Lifecycle hook called before saving the entity, useful for setting timestamps, performing validation, etc.
// User entity example
type User struct {
    ID        uuid.UUID
    Email     string
    Name      string
    UpdatedAt time.Time
}

// PkSk returns the partition key and sort key
func (u *User) PkSk() (string, string) {
    return fmt.Sprintf("USER#%s", u.ID), "USER"
}

// GSI1 returns the partition key and sort key for GSI1
func (u *User) GSI1() (string, string) {
    return "USER#EMAIL", u.Email
}

// GSI2 returns the partition key and sort key for GSI2
func (u *User) GSI2() (string, string) {
    return "", ""
}

// BeforeSave is called before saving the entity
func (u *User) BeforeSave() error {
    u.UpdatedAt = time.Now()
    if !strings.Contains(u.Email, "@") {
        return fmt.Errorf("invalid email: %s", u.Email)
    }
    return nil
}

Storage

Creating a Storage
// Create DynamoDB client and storage
client := dynamodb.NewFromConfig(cfg)
storage := dynamorm.NewStorage("TableName", client)
Saving an Entity
// Create a new user
user := &User{
    ID:    uuid.MustParse("9be35b9b-e526-404f-8252-e14ce1cb9624"),
    Email: "john@doe.com",
    Name:  "John Doe",
}

// Save the user
err := storage.Save(ctx, user)

// Optionally, save with a condition (conditional write)
cond := expression.AttributeNotExists(expression.Name("PK"))
err = storage.Save(ctx, user, dynamorm.SaveCondition(cond))

The entity will be stored in DynamoDB with the following structure:

PK SK GSI1PK GSI1SK ID Email Name UpdatedAt
USER#9be35b9b-e526-404f-8252-e14ce1cb9624 USER USER#EMAIL john@doe.com 9be35b9b-e526-404f-8252-e14ce1cb9624 john@doe.com John Doe 2025-08-04T10:20:00Z
Batch Saving Entities
// Create users
user1 := &User{ID: uuid.New(), Email: "user1@example.com", Name: "User One"}
user2 := &User{ID: uuid.New(), Email: "user2@example.com", Name: "User Two"}
user3 := &User{ID: uuid.New(), Email: "user3@example.com", Name: "User Three"}

// Batch save all users
err = storage.BatchSave(ctx, user1, user2, user3)
Querying
Get One Entity
// Initialize user with ID
user := &User{ID: uuid.MustParse("9be35b9b-e526-404f-8252-e14ce1cb9624")}

// Get user by PK=USER#9be35b9b-e526-404f-8252-e14ce1cb9624 SK=USER
err := storage.Get(ctx, user)
if err != nil {
    if errors.Is(err, dynamorm.ErrEntityNotFound) {
        // Handle not found case
    }
}

fmt.Printf("Found user: %v\n", user)

You can customize the underlying GetItem request via options:

  • Consistent read: perform a strongly consistent read
  • Projection: only fetch specific attributes
// Strongly consistent read, and only fetch Name and Email attributes
if err := storage.Get(ctx, user,
    dynamorm.GetConsistent(true),
    dynamorm.GetAttribute("Name", "Email"),
); err != nil {
    // handle error
}
Get Multiple Entities
// Find a user using GSI1PK=USER#EMAIL, GSI1SK=john@doe.com
query, err := storage.QueryGSI1(ctx, "USER#EMAIL", dynamorm.SkEQ("john@doe.com"))

// Get the first result of the current page
user := &User{}
err = query.First(user)
if err != nil {
    if errors.Is(err, dynamorm.ErrIndexOutOfRange) {
        // No items on the current page
    }
}

// Get the last result of the current page
user = &User{}
err = query.Last(user)
if err != nil {
    if errors.Is(err, dynamorm.ErrIndexOutOfRange) {
        // No items on the current page
    }
}

// Query with filter conditions: GSI1PK=USER#EMAIL, begins_with(GSI1SK, "john@"), Name="John Doe"
query, err = storage.QueryGSI1(
    ctx,
    "USER#EMAIL",
    dynamorm.SkBeginsWith("john@"),
    dynamorm.QueryFilter(expression.Name("Name").Equal(expression.Value("John Doe"))),
)

// Query with filter conditions: GSI1PK=USER#EMAIL, begins_with(GSI1SK, "john@"), Name="John Doe" OR Name="Jane Doe"
query, err = storage.QueryGSI1(
    ctx,
    "USER#EMAIL",
    dynamorm.SkBeginsWith("john@"),
    dynamorm.QueryFilter(
        expression.Or(
            expression.Name("Name").Equal(expression.Value("John Doe")),
            expression.Name("Name").Equal(expression.Value("Jane Doe")),
        ),
    ),
)

// Example: limit 10 results per page and sort descending on SK
query, err = storage.Query(
    ctx,
    "USER#9be35b9b-e526-404f-8252-e14ce1cb9624",
    nil, // no SK condition
    dynamorm.QueryLimit(10),
    dynamorm.QueryForward(false),
)

Note: First() and Last() operate on the current page of results. Use NextPage(ctx) to fetch subsequent pages.

Query Iterator

DynamoDB paginates query results by design. The Next() method iterates only through the current page of results. Similarly, the Reset() method only resets the iterator for the current page.

// Iterate through all items of the current query result
for query.Next() {
    user := &User{}
    if err := query.Decode(user); err != nil {
        // Handle decode error
        break
    }
    // Process item
}

To retrieve additional results, call NextPage(ctx). Use it as the outer loop and check query.Error() after the loop for any pagination error.

// Iterate through all items across all pages
for query.NextPage(ctx) {
    // Process current page
    for query.Next() {
        // Process item
    }
}

if err := query.Error(); err != nil {
    // Handle pagination error
}
Scanning

You can scan the whole table or a Global Secondary Index (GSI) and customize the underlying ScanInput via ScanOption(s):

  • ScanFilter: apply a filter condition to reduce items returned
  • ScanAttribute: limit the attributes returned (projection)
  • ScanLimit: control the page size (number of items evaluated per request)

Example: scan table with a filter, limit 1 per page, and only fetch selected attributes:

filter := expression.Name("IsActive").Equal(expression.Value(true))

q, err := storage.Scan(ctx,
    dynamorm.ScanFilter(filter),
    dynamorm.ScanLimit(1),
    dynamorm.ScanAttribute("ID", "Email"),
)
if err != nil { /* handle error */ }

// Iterate across all pages
for q.NextPage(ctx) {
    for q.Next() {
        var user User
        if err := q.Decode(&user); err != nil { /* handle error */ }
        // handle user
    }
}
if err := q.Error(); err != nil { /* handle pagination error */ }

Scanning GSIs:

// Scan GSI1
q1, err := storage.ScanGSI1(ctx)

// Scan GSI2 with a filter
filter := expression.Name("Type").Equal(expression.Value("Customer"))
q2, err := storage.ScanGSI2(ctx, dynamorm.ScanFilter(filter))
Updating an Entity
user := &User{ID: uuid.MustParse("9be35b9b-e526-404f-8252-e14ce1cb9624")}

// Build an update expression using AWS SDK expression
upd := expression.Set(
    expression.Name("Name"),
    expression.Value("Jane Doe"),
)

// Build a condition expression
cond := expression.AttributeExists(expression.Name("Name"))

// Optionally add a condition and request returned attributes
err := storage.Update(ctx, user, upd,
    dynamorm.UpdateCondition(cond),
    dynamorm.UpdateReturnValues(dynamorm.ALL_NEW),
)
if err != nil {
    // Handle error
}

This updates specific attributes without overwriting the entire item. In this example, dynamorm.ALL_NEW returns the entire updated item; it will be decoded into the provided entity.

Removing an Entity
user := &User{ID: uuid.MustParse("9be35b9b-e526-404f-8252-e14ce1cb9624")}

// Optional: apply a condition to the delete (e.g., only delete if Name exists)
cond := expression.AttributeExists(expression.Name("Name"))

err := storage.Remove(ctx, user,
    dynamorm.RemoveCondition(cond),
)
if err != nil {
    // Handle error
}
Batch Removing Entities
// Remove multiple users by their keys
u1 := &User{ID: uuid.MustParse("9be35b9b-e526-404f-8252-e14ce1cb9624")}
u2 := &User{ID: uuid.MustParse("1aa0f2a6-3c68-4f7a-9a74-9a6e7d8c0f21")}

// Batch remove all users (uses PkSk() on each entity)
if err := storage.BatchRemove(ctx, u1, u2); err != nil {
    // Handle error
}

Note: DynamoDB's BatchWriteItem is limited to 25 items per request; larger inputs are automatically chunked.

Transactions
// Create a transaction from storage
tx := storage.Transaction()

// Queue multiple operations atomically
_ = tx.AddSave(user1)
_ = tx.AddConditionCheck(user2, expression.AttributeExists(
	expression.Name("Name"),
))
_ = tx.AddUpdate(user2, expression.Set(
    expression.Name("Name"),
    expression.Value("John Doe Jr."),
))
_ = tx.AddRemove(user3)

// Execute all operations
if err := tx.Execute(ctx); err != nil {
    // Handle error
}

Running Tests

  • Unit tests: make test
  • Integration tests: make integration (requires Docker)
  • All tests: make test-all
  • Coverage report: make coverage

License

DynamORM is released under the MIT License.

Documentation

Overview

Package dynamorm is a lightweight, flexible Object-Relational Mapping (ORM) library for Amazon DynamoDB in Go. It provides a simple, type-safe way to interact with DynamoDB tables using Go structs.

DynamORM offers features like:

  • Simple, intuitive API for common DynamoDB operations
  • Type-safe mapping between Go structs and DynamoDB items
  • Support for primary keys and global secondary indexes
  • Composite primary key support and integrity
  • Flexible query and filtering capabilities
  • Lifecycle hooks for entities

Index

Constants

View Source
const (
	NONE        types.ReturnValue = "NONE"
	ALL_OLD     types.ReturnValue = "ALL_OLD"
	UPDATED_OLD types.ReturnValue = "UPDATED_OLD"
	ALL_NEW     types.ReturnValue = "ALL_NEW"
	UPDATED_NEW types.ReturnValue = "UPDATED_NEW"
)

Common ReturnValue constants for UpdateItem.

Variables

View Source
var ErrBatch = errors.New("failed to process all items in batch")

ErrBatch is returned by batch write operations when DynamoDB responds with unprocessed items that could not be written (affecting BatchSave/BatchRemove).

View Source
var ErrClient = errors.New("client error")

ErrClient is used to wrap errors returned by the underlying DynamoDB client.

View Source
var ErrEntityBeforeSave = errors.New("failed to execute entity.BeforeSave")

ErrEntityBeforeSave is returned when Entity.BeforeSave returns an error during a save operation (e.g., Storage.Save).

View Source
var ErrEntityDecode = errors.New("failed to decode entity")

ErrEntityDecode is returned when the Decoder fails to convert a DynamoDB item into the target entity (e.g., during Storage.Get).

View Source
var ErrEntityEncode = errors.New("failed to encode entity")

ErrEntityEncode is returned when the Encoder fails to convert an entity into a DynamoDB attribute map (e.g., during Storage.Save).

View Source
var ErrEntityNotFound = errors.New("entity not found")

ErrEntityNotFound is returned by Storage.Get when an entity with the given PK/SK does not exist in DynamoDB.

View Source
var ErrEntityPkNotSet = errors.New("entity PK not set")

ErrEntityPkNotSet is returned when an entity's partition key (PK) is empty while an operation requires it (e.g., Storage.Save/Get/Update/Remove).

View Source
var ErrEntitySkNotSet = errors.New("entity SK not set")

ErrEntitySkNotSet is returned when an entity's sort key (SK) is empty while an operation requires it (e.g., Storage.Save/Get/Update/Remove).

View Source
var ErrIndexOutOfRange = errors.New("index out of range")

ErrIndexOutOfRange is returned by Query.Decode, Query.First, and Query.Last when the requested item is outside the bounds of the current result set, including when there are no items.

Functions

func WithBaseEndpoint

func WithBaseEndpoint(endpoint string) func(*dynamodb.Options)

WithBaseEndpoint returns a function that configures the DynamoDB client with a custom base endpoint. This is particularly useful for local test integration, allowing you to point the client to a local DynamoDB instance or alternative endpoint.

Types

type Builder added in v0.0.5

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

func (*Builder) Build added in v0.0.5

func (b *Builder) Build() (Expression, error)

func (*Builder) WithCondition added in v0.0.5

func (b *Builder) WithCondition(condition expression.ConditionBuilder) BuilderInterface

func (*Builder) WithFilter added in v0.0.5

func (b *Builder) WithFilter(filter expression.ConditionBuilder) BuilderInterface

func (*Builder) WithKeyCondition added in v0.0.5

func (b *Builder) WithKeyCondition(keyCond expression.KeyConditionBuilder) BuilderInterface

func (*Builder) WithProjection added in v0.0.5

func (b *Builder) WithProjection(projection expression.ProjectionBuilder) BuilderInterface

func (*Builder) WithUpdate added in v0.0.5

func (b *Builder) WithUpdate(update expression.UpdateBuilder) BuilderInterface

type BuilderInterface added in v0.0.5

func NewBuilder added in v0.0.5

func NewBuilder() BuilderInterface

type ClientError added in v0.0.5

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

ClientError represents errors originating from the DynamoDB client. It wraps the original client error (accessible via errors.Unwrap / errors.As).

func NewClientError added in v0.0.5

func NewClientError(err error) *ClientError

NewClientError wraps an error returned by the underlying DynamoDB client in a ClientError.

func (*ClientError) Error added in v0.0.5

func (e *ClientError) Error() string

Error returns a human-readable message.

func (*ClientError) Is added in v0.0.5

func (e *ClientError) Is(target error) bool

Is makes ClientError match ErrClient when used with errors.Is, allowing callers to detect client-originated errors without losing the underlying type.

func (*ClientError) Unwrap added in v0.0.5

func (e *ClientError) Unwrap() error

Unwrap exposes the wrapped client error so callers can use errors.As oto inspect the underlying AWS error type.

type CreateBuilder added in v0.0.5

type CreateBuilder func() BuilderInterface

type Decoder

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

func DefaultDecoder

func DefaultDecoder() *Decoder

DefaultDecoder returns a new Decoder with pre-configured options.

func NewDecoder

func NewDecoder(optFns ...func(*attributevalue.DecoderOptions)) *Decoder

NewDecoder creates a new Decoder with the provided decoder options.

func (*Decoder) Decode

func (e *Decoder) Decode(m map[string]types.AttributeValue, out interface{}) error

type DecoderInterface

type DecoderInterface interface {
	// Decode unmarshals DynamoDB attribute values into the provided out parameter.
	Decode(map[string]types.AttributeValue, interface{}) error
}

DecoderInterface provides a way to decode DynamoDB attribute values into Go types.

type DynamoDB

DynamoDB defines the interface for interacting with AWS DynamoDB service. This interface abstracts the AWS SDK's DynamoDB client, allowing for easier testing and flexibility in implementation.

type Encoder

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

func DefaultEncoder

func DefaultEncoder() *Encoder

DefaultEncoder returns a new Encoder with pre-configured options.

func NewEncoder

func NewEncoder(optFns ...func(*attributevalue.EncoderOptions)) *Encoder

NewEncoder creates a new Encoder with the provided encoder options.

func (*Encoder) Encode

func (e *Encoder) Encode(in interface{}) (map[string]types.AttributeValue, error)

type EncoderInterface

type EncoderInterface interface {
	// Encode marshals a Go value into DynamoDB attribute values.
	Encode(interface{}) (map[string]types.AttributeValue, error)
}

EncoderInterface provides a way to encode Go types into DynamoDB attribute values.

type Entity

type Entity interface {
	// PkSk returns the partition key and sort key for the entity.
	// During a save operation (Save or BatchSave) it is called and saves the values respectively in PK/SK columns.
	// During a Get() operation it is called to know how to retrieve the entity.
	// Both values must be non-empty for operations to succeed.
	PkSk() (string, string)

	// GSI1 returns the partition key and sort key for the first Global Secondary Index.
	// During a save operation (Save or BatchSave) it is called and saves the values respectively in GSI1PK/GSI1SK columns.
	// Return empty strings if not using this GSI.
	GSI1() (string, string)

	// GSI2 returns the partition key and sort key for the second Global Secondary Index.
	// During a save operation (Save or BatchSave) it is called and saves the values respectively in GSI2PK/GSI2SK columns.
	// Return empty strings if not using this GSI.
	GSI2() (string, string)

	// BeforeSave is called before the entity is saved to DynamoDB.
	// This can be used to set timestamps, perform validation, etc.
	// Return an error to abort the save operation.
	BeforeSave() error
}

Entity is the core interface that must be implemented by any struct that will be stored in DynamoDB using this package.

Implementing this interface allows the struct to define how it maps to DynamoDB's key schema and provides lifecycle hooks.

type Expression added in v0.0.5

type Expression interface {
	Condition() *string
	KeyCondition() *string
	Filter() *string
	Projection() *string
	Names() map[string]string
	Values() map[string]types.AttributeValue
	Update() *string
}

type GetOption added in v0.0.5

GetOption customizes the DynamoDB GetItemInput request built by Storage.Get.

func GetAttribute added in v0.0.5

func GetAttribute(attrs ...string) GetOption

GetAttribute limits the attributes returned by the GetItemInput request using a ProjectionExpression built from the provided attribute names

func GetConsistent added in v0.0.5

func GetConsistent(consistent bool) GetOption

GetConsistent sets the ConsistentRead flag on the GetItemInput request. When true, the read is strongly consistent; when false (default), it is eventually consistent.

type Option

type Option func(*Options)

Option is a function type that modifies Options for use with NewStorage().

func WithBuilder added in v0.0.5

func WithBuilder(nb CreateBuilder) Option

WithBuilder allows providing a custom builder factory used by Storage to create expression builders. Useful for testing or extending behavior.

func WithDecoder

func WithDecoder(d DecoderInterface) Option

WithDecoder provides your own custom decoder when creating a Storage with NewStorage().

func WithEncoder

func WithEncoder(e EncoderInterface) Option

WithEncoder provides your own custom encoder when creating a Storage with NewStorage().

type Options

type Options struct {
	Encoder    EncoderInterface
	Decoder    DecoderInterface
	NewBuilder CreateBuilder
}

Options contains configuration options for Storage.

func DefaultOptions

func DefaultOptions() *Options

DefaultOptions creates default options for the storage, providing default encoder and decoder.

type Output added in v0.0.5

type Output struct {
	Count            int32
	ScannedCount     int32
	Items            []map[string]types.AttributeValue
	LastEvaluatedKey map[string]types.AttributeValue
}

Output represents the result of a DynamoDB query or scan operation.

func NewOutputFromQueryOutput added in v0.0.5

func NewOutputFromQueryOutput(q *dynamodb.QueryOutput) *Output

NewOutputFromQueryOutput converts an AWS SDK DynamoDB QueryOutput to the library's Output type.

func NewOutputFromScanOutput added in v0.0.5

func NewOutputFromScanOutput(s *dynamodb.ScanOutput) *Output

NewOutputFromScanOutput converts an AWS SDK DynamoDB ScanOutput to the library's Output type.

type Query

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

Query implements the QueryInterface for handling DynamoDB query results. It provides methods for iterating through query results, automatic pagination, and decoding items into Go structs.

func NewQuery

func NewQuery(client DynamoDB, query *dynamodb.QueryInput, scan *dynamodb.ScanInput, output *Output, decoder DecoderInterface) *Query

NewQuery creates a new Query instance from the query input and output.

func (*Query) Count

func (q *Query) Count() int32

func (*Query) Decode

func (q *Query) Decode(e Entity) error

func (*Query) Error

func (q *Query) Error() error

func (*Query) First

func (q *Query) First(e Entity) error

func (*Query) Last added in v0.0.5

func (q *Query) Last(e Entity) error

func (*Query) Next

func (q *Query) Next() bool

func (*Query) NextPage added in v0.0.4

func (q *Query) NextPage(ctx context.Context) bool

func (*Query) Reset

func (q *Query) Reset()

func (*Query) ScannedCount

func (q *Query) ScannedCount() int32

type QueryInterface

type QueryInterface interface {
	// Count returns the number of items of the current the query result
	Count() int32
	// ScannedCount returns the number of items evaluated of the current the query result
	ScannedCount() int32
	// First decodes the first item in the current result set into the provided entity.
	// Returns ErrIndexOutOfRange if there are no items.
	First(Entity) error
	// Last decodes the last item in the current result set into the provided entity.
	// Returns ErrIndexOutOfRange if there are no items.
	Last(Entity) error
	// Next advances the iterator to the next item.
	// Returns true if there is a next item available, false otherwise.
	Next() bool
	// NextPage fetches the next page of results using the LastEvaluatedKey.
	// Returns true if there are more results to be fetched, false otherwise.
	// Any error encountered can be retrieved via Error().
	NextPage(context.Context) bool
	// Error returns the last error encountered during pagination.
	Error() error
	// Reset resets the iterator to the beginning
	Reset()
	// Decode decodes the current item into the provided interface
	Decode(Entity) error
}

QueryInterface provides an interface to handle DynamoDB query results. It supports automatic pagination when iterating through results using Next().

type QueryOption added in v0.0.5

QueryOption customizes the DynamoDB QueryInput built by Storage.Query, Storage.QueryGSI1, and Storage.QueryGSI2.

func QueryAttribute added in v0.0.5

func QueryAttribute(attrs ...string) QueryOption

QueryAttribute limits the attributes returned by the Query operation by applying a projection on the expression builder.

func QueryConsistent added in v0.0.5

func QueryConsistent(consistent bool) QueryOption

QueryConsistent controls whether the Query operation uses strongly consistent reads. When true, strongly consistent reads are used; when false, eventually consistent reads are used.

func QueryFilter added in v0.0.5

func QueryFilter(filter expression.ConditionBuilder) QueryOption

QueryFilter applies a filter expression to the Query operation. Use AWS SDK's expression builders to compose complex filters.

func QueryForward added in v0.0.5

func QueryForward(forward bool) QueryOption

QueryForward controls the sort order of the results for a Query operation. When true, results are returned in ascending order by the sort key; when false, results are returned in descending order.

func QueryLimit added in v0.0.5

func QueryLimit(count int32) QueryOption

QueryLimit sets the maximum number of items DynamoDB should evaluate per Query request by assigning the Limit field on the QueryInput. This controls page size.

type RemoveOption added in v0.0.5

RemoveOption represents a function that can mutate the DynamoDB DeleteItemInput and/or the expression.Builder used to build the delete statement. It returns the (possibly new) BuilderInterface to allow chaining when the builder is immutable.

func RemoveCondition added in v0.0.5

func RemoveCondition(condition expression.ConditionBuilder) RemoveOption

RemoveCondition returns a RemoveOption that applies a condition to the removal via the expression builder. This results in a ConditionExpression being added to the DeleteItemInput request when the builder is built.

type ReturnValue added in v0.0.5

type ReturnValue = types.ReturnValue

ReturnValue is an alias of types.ReturnValue for convenience when specifying the expected return values of an UpdateItem operation.

type SaveOption added in v0.0.5

SaveOption represents a function that can mutate the DynamoDB PutItemInput and/or the expression.Builder used to build the put statement. It returns the (possibly new) BuilderInterface to allow chaining when the builder is immutable.

func SaveCondition added in v0.0.5

func SaveCondition(condition expression.ConditionBuilder) SaveOption

SaveCondition returns a SaveOption that applies a condition to the save via the expression builder. This results in a ConditionExpression being added to the PutItem request when the builder is built.

type ScanOption added in v0.0.5

ScanOption customizes the DynamoDB ScanInput request built by Storage.Scan.

func ScanAttribute added in v0.0.5

func ScanAttribute(attrs ...string) ScanOption

ScanAttribute limits the attributes returned by the Scan operation.

func ScanFilter added in v0.0.5

func ScanFilter(filter expression.ConditionBuilder) ScanOption

ScanFilter applies a filter expression to the Scan operation.

func ScanLimit added in v0.0.5

func ScanLimit(count int32) ScanOption

ScanLimit sets the maximum number of items to evaluate per Scan request by assigning the Limit field on the ScanInput.

type SkCondition added in v0.0.5

type SkCondition func(key string) expression.KeyConditionBuilder

SkCondition represents a function that, given a sort key name, returns a KeyConditionBuilder to be used in a DynamoDB Query key condition. It is used by Storage.Query, Storage.QueryGSI1, and Storage.QueryGSI2 to build the SK part of the key condition expression.

func SkBeginsWith

func SkBeginsWith(prefix string) SkCondition

SkBeginsWith creates a sort key prefix condition: begins_with(SK, prefix).

func SkBetween added in v0.0.5

func SkBetween(lower, upper interface{}) SkCondition

SkBetween creates a sort key range condition: SK BETWEEN lower AND upper.

func SkEQ

func SkEQ(v interface{}) SkCondition

SkEQ creates a sort key equality condition: SK = value.

func SkGT

func SkGT(v interface{}) SkCondition

SkGT creates a sort key "greater than" condition: SK > value.

func SkGTE

func SkGTE(v interface{}) SkCondition

SkGTE creates a sort key "greater than or equal" condition: SK >= value.

func SkLT

func SkLT(v interface{}) SkCondition

SkLT creates a sort key "less than" condition: SK < value.

func SkLTE

func SkLTE(v interface{}) SkCondition

SkLTE creates a sort key "less than or equal" condition: SK <= value.

type Storage

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

Storage implements the StorageInterface for DynamoDB operations.

func NewStorage

func NewStorage(table string, client DynamoDB, optFns ...Option) *Storage

NewStorage creates a new Storage instance with the specified table name and DynamoDB client. Optional configuration can be provided through Option functions.

Example:

storage := dynamorm.NewStorage("MyTable", dynamoDBClient)

With custom options:

storage := dynamorm.NewStorage("MyTable", dynamoDBClient,
    dynamorm.WithEncoder(customEncoder),
    dynamorm.WithDecoder(customDecoder),
)

func (*Storage) BatchRemove added in v0.0.5

func (s *Storage) BatchRemove(ctx context.Context, entities ...Entity) error

func (*Storage) BatchSave added in v0.0.5

func (s *Storage) BatchSave(ctx context.Context, entities ...Entity) error

func (*Storage) Get

func (s *Storage) Get(ctx context.Context, e Entity, opts ...GetOption) error

func (*Storage) Query

func (s *Storage) Query(ctx context.Context, pk string, cond SkCondition, opts ...QueryOption) (QueryInterface, error)

func (*Storage) QueryGSI1

func (s *Storage) QueryGSI1(ctx context.Context, pk string, cond SkCondition, opts ...QueryOption) (QueryInterface, error)

func (*Storage) QueryGSI2

func (s *Storage) QueryGSI2(ctx context.Context, pk string, cond SkCondition, opts ...QueryOption) (QueryInterface, error)

func (*Storage) Remove

func (s *Storage) Remove(ctx context.Context, e Entity, opts ...RemoveOption) error

func (*Storage) Save

func (s *Storage) Save(ctx context.Context, e Entity, opts ...SaveOption) error

func (*Storage) Scan added in v0.0.5

func (s *Storage) Scan(ctx context.Context, opts ...ScanOption) (QueryInterface, error)

func (*Storage) ScanGSI1 added in v0.0.5

func (s *Storage) ScanGSI1(ctx context.Context, opts ...ScanOption) (QueryInterface, error)

func (*Storage) ScanGSI2 added in v0.0.5

func (s *Storage) ScanGSI2(ctx context.Context, opts ...ScanOption) (QueryInterface, error)

func (*Storage) Transaction added in v0.0.5

func (s *Storage) Transaction() TransactionInterface

func (*Storage) Update added in v0.0.5

func (s *Storage) Update(ctx context.Context, e Entity, update expression.UpdateBuilder, opts ...UpdateOption) error

type StorageInterface

type StorageInterface interface {
	// Get retrieves an entity from DynamoDB by its partition key (PK) and sort key (SK).
	// It calls entity.PkSk() to determine how to query the entity.
	// Optional Get options can customize the underlying GetItemInput request.
	// Returns ErrEntityNotFound if the item doesn't exist in the table.
	Get(context.Context, Entity, ...GetOption) error

	// Query performs a query operation on the table using the partition key (PK).
	// An optional SK condition can be provided to refine the query, as well as additional filters.
	// It returns a QueryInterface for iterating through the results.
	Query(context.Context, string, SkCondition, ...QueryOption) (QueryInterface, error)

	// QueryGSI1 performs a query operation on the Global Secondary Index 1.
	// An optional SK condition can be provided to refine the query, as well as additional filters.
	// It returns a QueryInterface for iterating through the results.
	QueryGSI1(context.Context, string, SkCondition, ...QueryOption) (QueryInterface, error)

	// QueryGSI2 performs a query operation on the Global Secondary Index 2.
	// An optional SK condition can be provided to refine the query, as well as additional filters.
	// It returns a QueryInterface for iterating through the results.
	QueryGSI2(context.Context, string, SkCondition, ...QueryOption) (QueryInterface, error)

	// Scan performs a scan operation on the table.
	// Optional ScanOption(s) can customize the underlying ScanInput (e.g., limit, filter, projection).
	// It returns a QueryInterface for iterating through the results.
	Scan(context.Context, ...ScanOption) (QueryInterface, error)

	// ScanGSI1 performs a scan operation on the Global Secondary Index 1.
	// Optional ScanOption(s) can customize the underlying ScanInput (e.g., limit, filter, projection).
	// It returns a QueryInterface for iterating through the results.
	ScanGSI1(context.Context, ...ScanOption) (QueryInterface, error)

	// ScanGSI2 performs a scan operation on the Global Secondary Index 2.
	// Optional ScanOption(s) can customize the underlying ScanInput (e.g., limit, filter, projection).
	// It returns a QueryInterface for iterating through the results.
	ScanGSI2(context.Context, ...ScanOption) (QueryInterface, error)

	// Save persists a single entity to DynamoDB.
	// It calls entity.PkSk() to populate PK and SK, and entity.GSI1() and entity.GSI2()
	// to populate GSI PK and SK. The BeforeSave() hook is called on the entity before saving.
	// Optional SaveOption(s) can be provided, such as SaveCondition to apply a conditional write.
	Save(context.Context, Entity, ...SaveOption) error

	// BatchSave persists one or more entities to DynamoDB using BatchWriteItem.
	// It calls BeforeSave() and uses PkSk(), GSI1(), and GSI2() for each entity.
	// Note: DynamoDB limits BatchWriteItem to 25 items per request; larger inputs are chunked.
	BatchSave(context.Context, ...Entity) error

	// BatchRemove deletes one or more entities from DynamoDB using BatchWriteItem with DeleteRequests.
	// It uses PkSk() for each entity to compute the key.
	// Note: DynamoDB limits BatchWriteItem to 25 items per request; larger inputs are chunked.
	BatchRemove(context.Context, ...Entity) error

	// Update applies one or more update operations to an existing item identified by its PK/SK.
	// If the operation returns attributes, they are decoded into the provided entity; otherwise, the entity is left unchanged.
	Update(context.Context, Entity, expression.UpdateBuilder, ...UpdateOption) error

	// Remove deletes an entity from DynamoDB by its PK/SK.
	// It calls entity.PkSk() to determine how to delete the entity.
	// Returns an error if the operation fails.
	Remove(context.Context, Entity, ...RemoveOption) error

	// Transaction creates a new Transaction to batch multiple write operations
	// (put, update, delete) and execute them atomically.
	// The returned transaction uses the same table as the storage instance.
	Transaction() TransactionInterface
}

StorageInterface defines the operations for interacting with DynamoDB. It provides methods for retrieving, querying, saving, and removing entities.

type Transaction added in v0.0.5

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

func NewTransaction added in v0.0.5

func NewTransaction(table string, client DynamoDB, encoder EncoderInterface, newBuilder CreateBuilder) *Transaction

NewTransaction creates a new Transaction with the provided DynamoDB client.

func (*Transaction) AddConditionCheck added in v0.0.5

func (tx *Transaction) AddConditionCheck(e Entity, cond expression.ConditionBuilder) error

func (*Transaction) AddRemove added in v0.0.5

func (tx *Transaction) AddRemove(e Entity, opts ...RemoveOption) error

func (*Transaction) AddSave added in v0.0.5

func (tx *Transaction) AddSave(e Entity, opts ...SaveOption) error

func (*Transaction) AddUpdate added in v0.0.5

func (tx *Transaction) AddUpdate(e Entity, update expression.UpdateBuilder, opts ...UpdateOption) error

func (*Transaction) Execute added in v0.0.5

func (tx *Transaction) Execute(ctx context.Context) error

type TransactionInterface added in v0.0.5

type TransactionInterface interface {
	// AddSave adds a Put operation for the given entity to the transaction.
	AddSave(Entity, ...SaveOption) error
	// AddUpdate adds an Update operation for the given entity to the transaction.
	AddUpdate(Entity, expression.UpdateBuilder, ...UpdateOption) error
	// AddRemove adds a Delete operation for the given entity to the transaction.
	AddRemove(Entity, ...RemoveOption) error
	// AddConditionCheck adds a ConditionCheck operation for the given entity with the provided condition.
	AddConditionCheck(Entity, expression.ConditionBuilder) error
	// Execute executes the transaction.
	Execute(ctx context.Context) error
}

TransactionInterface describes a lightweight builder for DynamoDB transactional writes. It allows queuing multiple write operations (Put, Update, Delete) against a single table and executing them atomically via TransactWriteItems.

Implementations should accumulate operations until Execute is called. If no operations were added, Execute should be a no-op and return nil.

type UpdateOption added in v0.0.5

UpdateOption represents a function that can mutate the DynamoDB UpdateItemInput and/or the expression.Builder used to build the update statement. It returns the (possibly new) BuilderInterface to allow chaining when the builder is immutable.

func UpdateCondition added in v0.0.5

func UpdateCondition(condition expression.ConditionBuilder) UpdateOption

UpdateCondition returns an UpdateOption that applies a condition to the update via the expression builder. This results in a ConditionExpression being added to the UpdateItem request when the builder is built.

func UpdateReturnValues added in v0.0.5

func UpdateReturnValues(v ReturnValue) UpdateOption

UpdateReturnValues returns an UpdateOption that sets the ReturnValues field on the UpdateItemInput.

Jump to

Keyboard shortcuts

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