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

5.1 KiB

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

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)
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

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

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

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

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)

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

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.