# Indexable Usage Guide ## Generic Implementation for Any Indexable Struct The implementation is now **generic** and supports **any struct that embeds `model.Indexable`**! - **Interface**: `api/pkg/db/indexable.go` - defines the contract - **Implementation**: `api/pkg/db/internal/mongo/indexable/` - generic implementation - **Factory**: `api/pkg/db/project_indexable.go` - convenient factory for projects ## Usage ### 1. Using the Generic Implementation Directly ```go import "github.com/tech/sendico/pkg/db/internal/mongo/indexable" // For any type that embeds model.Indexable, define helper functions: createEmpty := func() *YourType { return &YourType{} } getIndexable := func(obj *YourType) *model.Indexable { return &obj.Indexable } // Create generic IndexableDB indexableDB := indexable.NewIndexableDB(repo, logger, createEmpty, getIndexable) // Use with single filter parameter err := indexableDB.Reorder(ctx, objectID, newIndex, filter) ``` ### 2. Using the Project Factory (Recommended for Projects) ```go import "github.com/tech/sendico/pkg/db" // Create project indexable DB (automatically applies org filter) projectDB := db.NewProjectIndexableDB(repo, logger, organizationRef) // Reorder project (org filter applied automatically) err := projectDB.Reorder(ctx, projectID, newIndex, repository.Query()) // Reorder with additional filters (combined with org filter) additionalFilter := repository.Query().Comparison(repository.Field("state"), builder.Eq, "active") err := projectDB.Reorder(ctx, projectID, newIndex, additionalFilter) ``` ## Examples for Different Types ### Project IndexableDB ```go createEmpty := func() *model.Project { return &model.Project{} } getIndexable := func(p *model.Project) *model.Indexable { return &p.Indexable } projectDB := indexable.NewIndexableDB(repo, logger, createEmpty, getIndexable) orgFilter := repository.OrgFilter(organizationRef) projectDB.Reorder(ctx, projectID, 2, orgFilter) ``` ### Status IndexableDB ```go createEmpty := func() *model.Status { return &model.Status{} } getIndexable := func(s *model.Status) *model.Indexable { return &s.Indexable } statusDB := indexable.NewIndexableDB(repo, logger, createEmpty, getIndexable) projectFilter := repository.Query().Comparison(repository.Field("projectRef"), builder.Eq, projectRef) statusDB.Reorder(ctx, statusID, 1, projectFilter) ``` ### Task IndexableDB ```go createEmpty := func() *model.Task { return &model.Task{} } getIndexable := func(t *model.Task) *model.Indexable { return &t.Indexable } taskDB := indexable.NewIndexableDB(repo, logger, createEmpty, getIndexable) statusFilter := repository.Query().Comparison(repository.Field("statusRef"), builder.Eq, statusRef) taskDB.Reorder(ctx, taskID, 3, statusFilter) ``` ### Priority IndexableDB ```go createEmpty := func() *model.Priority { return &model.Priority{} } getIndexable := func(p *model.Priority) *model.Indexable { return &p.Indexable } priorityDB := indexable.NewIndexableDB(repo, logger, createEmpty, getIndexable) orgFilter := repository.OrgFilter(organizationRef) priorityDB.Reorder(ctx, priorityID, 0, orgFilter) ``` ### Global Reordering (No Filter) ```go createEmpty := func() *model.Project { return &model.Project{} } getIndexable := func(p *model.Project) *model.Indexable { return &p.Indexable } globalDB := indexable.NewIndexableDB(repo, logger, createEmpty, getIndexable) // Reorders all items globally (empty filter) globalDB.Reorder(ctx, objectID, 5, repository.Query()) ``` ## Key Features ### ✅ **Generic Support** - Works with **any struct** that embeds `model.Indexable` - Type-safe with compile-time checking - No hardcoded types ### ✅ **Single Filter Parameter** - **Simple**: Single `builder.Query` parameter instead of variadic `interface{}` - **Flexible**: Can incorporate any combination of filters - **Type-safe**: No runtime type assertions needed ### ✅ **Clean Architecture** - Interface separated from implementation - Generic implementation in internal package - Easy-to-use factories for common types ## How It Works ### Generic Algorithm 1. **Get current index** using type-specific helper function 2. **If no change needed** → return early 3. **Apply filter** to scope affected items 4. **Shift affected items** using `PatchMany` with `$inc` 5. **Update target object** using `Patch` with `$set` ### Type-Safe Implementation ```go type IndexableDB[T storable.Storable] struct { repo repository.Repository logger mlogger.Logger createEmpty func() T getIndexable func(T) *model.Indexable } // Single filter parameter - clean and simple func (db *IndexableDB[T]) Reorder(ctx context.Context, objectRef primitive.ObjectID, newIndex int, filter builder.Query) error ``` ## Benefits ✅ **Generic** - Works with any Indexable struct ✅ **Type Safe** - Compile-time type checking ✅ **Simple** - Single filter parameter instead of variadic interface{} ✅ **Efficient** - Uses patches, not full updates ✅ **Clean** - Interface separated from implementation That's it! **Generic, type-safe, and simple** reordering for any Indexable struct with a single filter parameter.