package store import ( "context" "github.com/tech/sendico/ledger/storage" "github.com/tech/sendico/ledger/storage/model" "github.com/tech/sendico/pkg/db/repository" ri "github.com/tech/sendico/pkg/db/repository/index" "github.com/tech/sendico/pkg/db/storable" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/mlogger" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.uber.org/zap" ) type postingLinesStore struct { logger mlogger.Logger repo repository.Repository } func NewPostingLines(logger mlogger.Logger, db *mongo.Database) (storage.PostingLinesStore, error) { repo := repository.CreateMongoRepository(db, model.PostingLinesCollection) // Create index on journalEntryRef for fast lookup by entry entryIndex := &ri.Definition{ Keys: []ri.Key{ {Field: "journalEntryRef", Sort: ri.Asc}, }, } if err := repo.CreateIndex(entryIndex); err != nil { logger.Error("failed to ensure posting lines entry index", zap.Error(err)) return nil, err } // Create index on accountRef for account statement queries accountIndex := &ri.Definition{ Keys: []ri.Key{ {Field: "accountRef", Sort: ri.Asc}, {Field: "createdAt", Sort: ri.Desc}, }, } if err := repo.CreateIndex(accountIndex); err != nil { logger.Error("failed to ensure posting lines account index", zap.Error(err)) return nil, err } childLogger := logger.Named(model.PostingLinesCollection) childLogger.Debug("posting lines store initialised", zap.String("collection", model.PostingLinesCollection)) return &postingLinesStore{ logger: childLogger, repo: repo, }, nil } func (p *postingLinesStore) CreateMany(ctx context.Context, lines []*model.PostingLine) error { if len(lines) == 0 { p.logger.Warn("attempt to create empty posting lines array") return nil } storables := make([]storable.Storable, len(lines)) for i, line := range lines { if line == nil { p.logger.Warn("attempt to create nil posting line") return merrors.InvalidArgument("postingLinesStore: nil posting line") } storables[i] = line } if err := p.repo.InsertMany(ctx, storables); err != nil { p.logger.Warn("failed to create posting lines", zap.Error(err), zap.Int("count", len(lines))) return err } p.logger.Debug("posting lines created", zap.Int("count", len(lines))) return nil } func (p *postingLinesStore) ListByJournalEntry(ctx context.Context, entryRef primitive.ObjectID) ([]*model.PostingLine, error) { if entryRef.IsZero() { p.logger.Warn("attempt to list posting lines with zero entry ID") return nil, merrors.InvalidArgument("postingLinesStore: zero entry ID") } query := repository.Filter("journalEntryRef", entryRef) lines := make([]*model.PostingLine, 0) err := p.repo.FindManyByFilter(ctx, query, func(cur *mongo.Cursor) error { doc := &model.PostingLine{} if err := cur.Decode(doc); err != nil { return err } lines = append(lines, doc) return nil }) if err != nil { p.logger.Warn("failed to list posting lines by entry", zap.Error(err), zap.String("entryRef", entryRef.Hex())) return nil, err } p.logger.Debug("listed posting lines by entry", zap.Int("count", len(lines)), zap.String("entryRef", entryRef.Hex())) return lines, nil } func (p *postingLinesStore) ListByAccount(ctx context.Context, accountRef primitive.ObjectID, limit int, offset int) ([]*model.PostingLine, error) { if accountRef.IsZero() { p.logger.Warn("attempt to list posting lines with zero account ID") return nil, merrors.InvalidArgument("postingLinesStore: zero account ID") } limit64 := int64(limit) offset64 := int64(offset) query := repository.Query(). Filter(repository.Field("accountRef"), accountRef). Limit(&limit64). Offset(&offset64). Sort(repository.Field("createdAt"), false) // false = descending lines := make([]*model.PostingLine, 0) err := p.repo.FindManyByFilter(ctx, query, func(cur *mongo.Cursor) error { doc := &model.PostingLine{} if err := cur.Decode(doc); err != nil { return err } lines = append(lines, doc) return nil }) if err != nil { p.logger.Warn("failed to list posting lines by account", zap.Error(err), zap.String("accountRef", accountRef.Hex())) return nil, err } p.logger.Debug("listed posting lines by account", zap.Int("count", len(lines)), zap.String("accountRef", accountRef.Hex())) return lines, nil }