174 lines
5.1 KiB
Markdown
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. |