Files
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

123 lines
4.0 KiB
Go

package indexable
import (
"context"
"github.com/tech/sendico/pkg/db/repository"
"github.com/tech/sendico/pkg/db/repository/builder"
"github.com/tech/sendico/pkg/db/storable"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/model"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.uber.org/zap"
)
// IndexableDB implements db.IndexableDB interface with generic support
type IndexableDB[T storable.Storable] struct {
repo repository.Repository
logger mlogger.Logger
createEmpty func() T
getIndexable func(T) *model.Indexable
}
// NewIndexableDB creates a new IndexableDB instance
func NewIndexableDB[T storable.Storable](
repo repository.Repository,
logger mlogger.Logger,
createEmpty func() T,
getIndexable func(T) *model.Indexable,
) *IndexableDB[T] {
return &IndexableDB[T]{
repo: repo,
logger: logger,
createEmpty: createEmpty,
getIndexable: getIndexable,
}
}
// Reorder implements the db.IndexableDB interface with single filter parameter
func (db *IndexableDB[T]) Reorder(ctx context.Context, objectRef primitive.ObjectID, newIndex int, filter builder.Query) error {
// Get current object to find its index
obj := db.createEmpty()
err := db.repo.Get(ctx, objectRef, obj)
if err != nil {
db.logger.Error("Failed to get object for reordering",
zap.Error(err),
zap.String("object_ref", objectRef.Hex()),
zap.Int("new_index", newIndex))
return err
}
// Extract index from the object
indexable := db.getIndexable(obj)
currentIndex := indexable.Index
if currentIndex == newIndex {
db.logger.Debug("No reordering needed - same index",
zap.String("object_ref", objectRef.Hex()),
zap.Int("current_index", currentIndex),
zap.Int("new_index", newIndex))
return nil // No change needed
}
// Simple reordering logic
if currentIndex < newIndex {
// Moving down: shift items between currentIndex+1 and newIndex up by -1
patch := repository.Patch().Inc(repository.IndexField(), -1)
reorderFilter := filter.
And(repository.IndexOpFilter(currentIndex+1, builder.Gte)).
And(repository.IndexOpFilter(newIndex, builder.Lte))
updatedCount, err := db.repo.PatchMany(ctx, reorderFilter, patch)
if err != nil {
db.logger.Error("Failed to shift objects during reordering (moving down)",
zap.Error(err),
zap.String("object_ref", objectRef.Hex()),
zap.Int("current_index", currentIndex),
zap.Int("new_index", newIndex),
zap.Int("updated_count", updatedCount))
return err
}
db.logger.Debug("Successfully shifted objects (moving down)",
zap.String("object_ref", objectRef.Hex()),
zap.Int("updated_count", updatedCount))
} else {
// Moving up: shift items between newIndex and currentIndex-1 down by +1
patch := repository.Patch().Inc(repository.IndexField(), 1)
reorderFilter := filter.
And(repository.IndexOpFilter(newIndex, builder.Gte)).
And(repository.IndexOpFilter(currentIndex-1, builder.Lte))
updatedCount, err := db.repo.PatchMany(ctx, reorderFilter, patch)
if err != nil {
db.logger.Error("Failed to shift objects during reordering (moving up)",
zap.Error(err),
zap.String("object_ref", objectRef.Hex()),
zap.Int("current_index", currentIndex),
zap.Int("new_index", newIndex),
zap.Int("updated_count", updatedCount))
return err
}
db.logger.Debug("Successfully shifted objects (moving up)",
zap.String("object_ref", objectRef.Hex()),
zap.Int("updated_count", updatedCount))
}
// Update the target object to new index
patch := repository.Patch().Set(repository.IndexField(), newIndex)
err = db.repo.Patch(ctx, objectRef, patch)
if err != nil {
db.logger.Error("Failed to update target object index",
zap.Error(err),
zap.String("object_ref", objectRef.Hex()),
zap.Int("current_index", currentIndex),
zap.Int("new_index", newIndex))
return err
}
db.logger.Info("Successfully reordered object",
zap.String("object_ref", objectRef.Hex()),
zap.Int("old_index", currentIndex),
zap.Int("new_index", newIndex))
return nil
}