Updates¶
Dynorm provides a fluent Update Builder for atomic DynamoDB update operations.
Basic Updates¶
Update by ID¶
// Create update builder
updates := dynorm.Update().
Set("Status", "verified").
Set("VerifiedAt", time.Now())
// Apply to entity by ID
user, err := UserRepo.Update("01HQ3K...", updates)
Update via Query¶
// Update all matching entities
count, err := UserRepo.
Where("Status", "=", "pending").
Update(dynorm.Update().Set("Status", "active"))
fmt.Printf("Updated %d users\n", count)
Update Operations¶
Set¶
Set a field to a value:
updates := dynorm.Update().
Set("Status", "active").
Set("Email", "new@example.com").
Set("UpdatedAt", time.Now())
SetIfNotExists¶
Set a field only if it doesn't exist:
Remove¶
Remove attributes from the item:
Increment / Decrement¶
Atomic numeric operations:
// Increment
updates := dynorm.Update().
Increment("ViewCount", 1).
Increment("Score", 10)
// Decrement
updates := dynorm.Update().
Decrement("Stock", 1).
Decrement("Balance", 100)
Add¶
Add values to a number or set:
// Add to number
updates := dynorm.Update().
Add("Points", 50)
// Add to set
updates := dynorm.Update().
Add("Tags", []string{"featured", "new"})
Delete (Set Elements)¶
Remove elements from a set:
Append / Prepend (Lists)¶
Add elements to lists:
// Append to end
updates := dynorm.Update().
Append("Comments", comment1, comment2)
// Prepend to beginning
updates := dynorm.Update().
Prepend("RecentViews", viewRecord)
Conditional Updates¶
Add conditions that must be met for the update to succeed:
updates := dynorm.Update().
Set("Status", "published").
Where("Status", "=", "draft")
user, err := UserRepo.Update(id, updates)
if err != nil {
// Condition not met or other error
}
Available Conditions¶
// Field equals value
updates.Where("Status", "=", "draft")
// Field not equals
updates.Where("Status", "!=", "banned")
// Comparisons
updates.Where("Version", ">=", 1)
updates.Where("Age", "<", 18)
// Attribute exists
updates.WhereExists("Email")
// Attribute doesn't exist
updates.WhereNotExists("DeletedAt")
Multiple Conditions¶
updates := dynorm.Update().
Set("Status", "active").
Where("Status", "=", "pending").
Where("Age", ">=", 18).
WhereExists("Email")
Optimistic Locking¶
Use version fields for concurrent update safety:
type User struct {
dynorm.Entity
Version int `dynorm:"version"`
// ...
}
// Update with optimistic locking
updates := dynorm.Update().
Set("Status", "active").
WithOptimisticLock(currentVersion)
// Automatically checks version and increments it
user, err := UserRepo.Update(id, updates)
if err != nil {
// Version mismatch - someone else updated the entity
}
Manual Version Control¶
updates := dynorm.Update().
Set("Status", "active").
IncrementVersion() // Increment version field
// Or with explicit check
updates := dynorm.Update().
Set("Status", "active").
Where("Version", "=", 5).
Increment("Version", 1)
Chaining Operations¶
Combine multiple operations:
updates := dynorm.Update().
Set("Status", "active").
Set("ActivatedAt", time.Now()).
Increment("LoginCount", 1).
Remove("TempToken").
Append("ActivityLog", "Account activated").
Where("Status", "=", "pending")
user, err := UserRepo.Update(id, updates)
Examples¶
Activate User Account¶
func ActivateUser(userID string) (*User, error) {
updates := dynorm.Update().
Set("Status", "active").
Set("ActivatedAt", time.Now()).
Remove("ActivationToken").
Where("Status", "=", "pending")
return UserRepo.Update(userID, updates)
}
Increment Counter¶
func IncrementViewCount(postID string) (*Post, error) {
updates := dynorm.Update().
Increment("ViewCount", 1).
Set("LastViewedAt", time.Now())
return PostRepo.Update(postID, updates)
}
Add Tag to Post¶
func AddTag(postID string, tag string) (*Post, error) {
updates := dynorm.Update().
Append("Tags", tag)
return PostRepo.Update(postID, updates)
}
Safe Stock Decrement¶
func DecrementStock(productID string, quantity int) (*Product, error) {
updates := dynorm.Update().
Decrement("Stock", int64(quantity)).
Where("Stock", ">=", quantity) // Only if enough stock
return ProductRepo.Update(productID, updates)
}
Bulk Status Update¶
func DeactivateOldUsers(daysSinceLogin int) (int, error) {
cutoff := time.Now().AddDate(0, 0, -daysSinceLogin)
return UserRepo.
Where("LastLoginAt", "<", cutoff).
Where("Status", "=", "active").
Update(dynorm.Update().
Set("Status", "inactive").
Set("DeactivatedAt", time.Now()))
}
Error Handling¶
updates := dynorm.Update().
Set("Status", "active").
Where("Status", "=", "pending")
user, err := UserRepo.Update(id, updates)
if err != nil {
// Could be:
// - ConditionalCheckFailedException (condition not met)
// - ValidationException (invalid update)
// - ResourceNotFoundException (entity not found)
return err
}
Best Practices¶
Do
- Use atomic operations for counters
- Use optimistic locking for concurrent updates
- Use conditions to ensure safe updates
- Combine related updates in single operation
Don't
- Read-modify-write when atomic update works
- Ignore conditional check failures
- Update without version checks in concurrent scenarios
Update Flow¶
sequenceDiagram
participant App
participant Dynorm
participant DynamoDB
App->>Dynorm: Update(id, updates)
Dynorm->>Dynorm: Build UpdateExpression
Dynorm->>Dynorm: Build ConditionExpression
alt Has Conditions
Dynorm->>DynamoDB: UpdateItem + Condition
alt Condition Met
DynamoDB-->>Dynorm: Updated Item
Dynorm-->>App: Updated Entity
else Condition Failed
DynamoDB-->>Dynorm: ConditionalCheckFailed
Dynorm-->>App: Error
end
else No Conditions
Dynorm->>DynamoDB: UpdateItem
DynamoDB-->>Dynorm: Updated Item
Dynorm-->>App: Updated Entity
end Next Steps¶
- Batch Operations - Bulk updates
- Transactions - ACID operations
- Repository - Basic CRUD