Skip to content

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