Collection¶
Collections wrap query results with utility methods for filtering, sorting, grouping, and bulk operations.
Overview¶
When you call GetAll() on a query, you receive a Collection[T]:
users, err := UserRepo.
Where("Status", "=", "active").
GetAll()
// users is a *Collection[User]
fmt.Printf("Found %d users\n", users.Count())
Access Methods¶
Basic Access¶
// Get count
count := users.Count()
// Check if empty
if users.IsEmpty() {
fmt.Println("No users found")
}
// Get underlying slice
items := users.Items()
// Alias for Items()
slice := users.ToSlice()
// Get by index
first := users.First()
last := users.Last()
third := users.Get(2) // nil if out of bounds
Pagination Info¶
// Check if more results available
if users.HasMore() {
cursor := users.NextCursor()
// Use cursor for next page
}
// Get full metadata
meta := users.Metadata()
fmt.Printf("Scanned: %d, HasMore: %v\n", meta.ScannedCount, meta.HasMore)
Filtering¶
// Filter with predicate
activeUsers := users.Filter(func(u *User) bool {
return u.Status == "active"
})
// Reject (inverse of filter)
nonAdmins := users.Reject(func(u *User) bool {
return u.Role == "admin"
})
// Where - filter by field value
admins := users.Where("Role", "admin")
// WhereNot - exclude by field value
regularUsers := users.WhereNot("Role", "admin")
// Find first matching
admin := users.Find(func(u *User) bool {
return u.Role == "admin"
})
// Check if any match
hasAdmins := users.Any(func(u *User) bool {
return u.Role == "admin"
})
// Check if all match
allActive := users.All(func(u *User) bool {
return u.Status == "active"
})
// Check if contains specific item
containsUser := users.Contains(specificUser)
Sorting¶
// Sort with custom comparator
sorted := users.Sort(func(a, b *User) bool {
return a.CreatedAt.Before(b.CreatedAt)
})
// Sort by field (ascending)
byName := users.SortBy("FirstName")
// Sort by field (descending)
byNewest := users.SortByDesc("CreatedAt")
// Reverse order
reversed := users.Reverse()
Iteration¶
// Iterate over each item
users.Each(func(u *User) {
fmt.Printf("User: %s\n", u.Email)
})
// Iterate with index
users.EachWithIndex(func(i int, u *User) {
fmt.Printf("%d: %s\n", i, u.Email)
})
// Map/transform items
updated := users.Map(func(u *User) *User {
u.Status = "processed"
return u
})
Aggregation¶
// Pluck field values (any type)
emails := users.Pluck("Email") // []any
// Pluck string field values
emailStrings := users.PluckStrings("Email") // []string
// Get all IDs
ids := users.IDs() // []string
// Group by field
byStatus := users.GroupBy("Status")
// map[any]*Collection[User]
// {"active": Collection, "inactive": Collection}
// Get unique by field
uniqueByEmail := users.Unique("Email")
Chunking and Slicing¶
// Take first n items
firstTen := users.Take(10)
// Skip first n items
afterTen := users.Skip(10)
// Split into chunks
chunks := users.Chunk(25)
for _, chunk := range chunks {
// Process each chunk
err := chunk.SaveAll()
}
Bulk Operations¶
Collections with a repository reference support bulk operations:
// Save all entities
err := users.SaveAll()
// Delete all entities
err := users.DeleteAll()
// Update field on all entities
err := users.UpdateField("Status", "processed")
// Update multiple fields
err := users.UpdateAll(map[string]any{
"Status": "processed",
"UpdatedAt": time.Now(),
})
Dirty Tracking¶
// Get entities with changes
dirty := users.Dirty()
// Get entities without changes
clean := users.Clean()
// Check if any entity has changes
if users.HasDirty() {
// Save only changed entities
err := users.SaveDirty()
}
Creating Collections¶
Collections are typically returned from repository queries:
// From repository queries
users, err := UserRepo.Where("Status", "=", "active").GetAll()
// users is already a *Collection[User]
// Collections support chaining
activeAdmins := users.Filter(func(u *User) bool {
return u.Role == "admin"
})
Chaining Operations¶
All methods that return collections can be chained:
result := users.
Filter(func(u *User) bool {
return u.Status == "active"
}).
SortByDesc("CreatedAt").
Take(10)
fmt.Printf("Top 10 active users: %d\n", result.Count())
Example: Processing Users¶
func ProcessActiveUsers() error {
// Get all active users
users, err := UserRepo.
Where("Status", "=", "active").
GetAll()
if err != nil {
return err
}
// Filter to verified users only
verified := users.Filter(func(u *User) bool {
return u.Verified
})
// Group by role
byRole := verified.GroupBy("Role")
// Process admins
if admins, ok := byRole["admin"]; ok {
admins.Each(func(u *User) {
fmt.Printf("Admin: %s\n", u.Email)
})
}
// Update status for all verified users
verified.Each(func(u *User) {
u.Status = "processed"
})
// Save only changed entities
return verified.SaveDirty()
}
Example: Batch Processing¶
func ProcessInBatches() error {
users, err := UserRepo.All()
if err != nil {
return err
}
// Process in chunks of 25 (DynamoDB batch limit)
chunks := users.Chunk(25)
for i, chunk := range chunks {
fmt.Printf("Processing batch %d of %d\n", i+1, len(chunks))
chunk.Each(func(u *User) {
u.LastProcessed = time.Now()
})
if err := chunk.SaveAll(); err != nil {
return fmt.Errorf("batch %d failed: %w", i+1, err)
}
}
return nil
}
Error Handling¶
// Collections from queries have repository references
users, _ := UserRepo.GetAll()
err := users.SaveAll() // Works - has repository reference
// If you need to create a collection from a slice,
// use the internal package:
import impl "github.com/go-gamma/dynorm/pkg/dynorm"
users := impl.NewCollectionWithRepo(userSlice, UserRepo.Repository)
err := users.SaveAll() // Works
Next Steps¶
- Query Builder - Build queries that return collections
- Pagination - Paginate through large result sets
- Batch Operations - Efficient bulk operations