package prices import ( "context" "fmt" "time" "github.com/shopspring/decimal" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.uber.org/zap" ) type PriceDb struct { ID string `bson:"_id" json:"id"` CoingeckoID string `bson:"coingeckoId" json:"coingeckoId"` Price string `bson:"price" json:"price"` Datetime time.Time `bson:"dateTime" json:"dateTime"` UpdatedAt time.Time `bson:"updatedAt" json:"updatedAt"` } type PriceRepository struct { db *mongo.Database collection *mongo.Collection logger *zap.Logger } var ErrPriceNotFound = fmt.Errorf("price not found") // NewPriceRepository creates a new price repository. func NewPriceRepository(db *mongo.Database, logger *zap.Logger) *PriceRepository { return &PriceRepository{ db: db, collection: db.Collection("prices"), logger: logger, } } // Upsert upserts a price. func (p *PriceRepository) Upsert(ctx context.Context, coingeckoID string, price decimal.Decimal, dateTime time.Time) error { id := p.createID(coingeckoID, dateTime) model := &PriceDb{ ID: id, CoingeckoID: coingeckoID, Price: price.Truncate(8).String(), Datetime: dateTime, UpdatedAt: time.Now(), } update := bson.M{ "$set": model, "$setOnInsert": indexedAt(time.Now()), "$inc": bson.D{{Key: "revision", Value: 1}}, } opts := options.Update().SetUpsert(true) var err error _, err = p.collection.UpdateByID(ctx, model.ID, update, opts) return err } // Find finds a price. func (p *PriceRepository) Find(ctx context.Context, coingeckoID string, dateTime time.Time) (*PriceDb, error) { id := p.createID(coingeckoID, dateTime) var price PriceDb err := p.collection.FindOne(ctx, bson.M{"_id": id}).Decode(&price) if err != nil { if err == mongo.ErrNoDocuments { return nil, ErrPriceNotFound } return nil, err } return &price, err } func (p *PriceRepository) createID(coingeckoID string, dateTime time.Time) string { return fmt.Sprintf("%s-%s", coingeckoID, dateTime.Format(time.RFC3339)) } func indexedAt(t time.Time) IndexingTimestamps { return IndexingTimestamps{ IndexedAt: t, } } type IndexingTimestamps struct { IndexedAt time.Time `bson:"indexedAt"` }