Files
sendico/api/pkg/db/internal/mongo/indexable/USAGE.md
Stephan D 62a6631b9a
All checks were successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
service backend
2025-11-07 18:35:26 +01:00

174 lines
5.1 KiB
Markdown

# 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.