Some checks failed
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline was successful
ci/woodpecker/push/fx_oracle Pipeline was successful
ci/woodpecker/push/frontend Pipeline was successful
ci/woodpecker/push/payments_orchestrator Pipeline was successful
ci/woodpecker/push/bump_version Pipeline failed
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/ledger Pipeline was successful
ci/woodpecker/push/notification Pipeline was successful
119 lines
3.6 KiB
Go
119 lines
3.6 KiB
Go
package tseriesimp
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/tech/sendico/pkg/db/repository"
|
|
"github.com/tech/sendico/pkg/db/repository/builder"
|
|
rdecoder "github.com/tech/sendico/pkg/db/repository/decoder"
|
|
tsoptions "github.com/tech/sendico/pkg/db/tseries/options"
|
|
tspoint "github.com/tech/sendico/pkg/db/tseries/point"
|
|
"github.com/tech/sendico/pkg/merrors"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
type TimeSeries struct {
|
|
options tsoptions.Options
|
|
collection *mongo.Collection
|
|
}
|
|
|
|
func NewMongoTimeSeriesCollection(ctx context.Context, db *mongo.Database, tsOpts *tsoptions.Options) (*TimeSeries, error) {
|
|
if tsOpts == nil {
|
|
return nil, merrors.InvalidArgument("nil time-series options provided", "options")
|
|
}
|
|
// Configure time-series options
|
|
granularity := tsOpts.Granularity.String()
|
|
ts := &options.TimeSeriesOptions{
|
|
TimeField: tsOpts.TimeField,
|
|
Granularity: &granularity,
|
|
}
|
|
if tsOpts.MetaField != "" {
|
|
ts.MetaField = &tsOpts.MetaField
|
|
}
|
|
|
|
// Collection options
|
|
collOpts := options.CreateCollection().SetTimeSeriesOptions(ts)
|
|
|
|
// Set TTL if requested
|
|
if tsOpts.ExpireAfter > 0 {
|
|
secs := int64(tsOpts.ExpireAfter / time.Second)
|
|
collOpts.SetExpireAfterSeconds(secs)
|
|
}
|
|
|
|
if err := db.CreateCollection(ctx, tsOpts.Collection, collOpts); err != nil {
|
|
if cmdErr, ok := err.(mongo.CommandError); !ok || cmdErr.Code != 48 {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return &TimeSeries{collection: db.Collection(tsOpts.Collection), options: *tsOpts}, nil
|
|
}
|
|
|
|
func (ts *TimeSeries) Aggregate(ctx context.Context, pipeline builder.Pipeline, decoder rdecoder.DecodingFunc) error {
|
|
queryFunc := func(ctx context.Context, collection *mongo.Collection) (*mongo.Cursor, error) {
|
|
return collection.Aggregate(ctx, pipeline.Build())
|
|
}
|
|
return ts.executeQuery(ctx, decoder, queryFunc)
|
|
}
|
|
|
|
func (ts *TimeSeries) Insert(ctx context.Context, timePoint tspoint.TimePoint) error {
|
|
_, err := ts.collection.InsertOne(ctx, timePoint)
|
|
return err
|
|
}
|
|
|
|
func (ts *TimeSeries) InsertMany(ctx context.Context, timePoints []tspoint.TimePoint) error {
|
|
docs := make([]any, len(timePoints))
|
|
for i, p := range timePoints {
|
|
docs[i] = p
|
|
}
|
|
|
|
// ignore the result if you like, or capture it
|
|
_, err := ts.collection.InsertMany(ctx, docs)
|
|
return err
|
|
}
|
|
|
|
type QueryFunc func(ctx context.Context, collection *mongo.Collection) (*mongo.Cursor, error)
|
|
|
|
func (ts *TimeSeries) executeQuery(ctx context.Context, decoder rdecoder.DecodingFunc, queryFunc QueryFunc) error {
|
|
cursor, err := queryFunc(ctx, ts.collection)
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
return merrors.NoData("no_items_in_array")
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cursor.Close(ctx)
|
|
|
|
for cursor.Next(ctx) {
|
|
if err := cursor.Err(); err != nil {
|
|
return err
|
|
}
|
|
if err = decoder(cursor); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ts *TimeSeries) Query(ctx context.Context, decoder rdecoder.DecodingFunc, query builder.Query, from, to *time.Time) error {
|
|
timeLimitedQuery := query
|
|
if from != nil {
|
|
timeLimitedQuery = timeLimitedQuery.And(repository.Query().Comparison(repository.Field(ts.options.TimeField), builder.Gte, *from))
|
|
}
|
|
if to != nil {
|
|
timeLimitedQuery = timeLimitedQuery.And(repository.Query().Comparison(repository.Field(ts.options.TimeField), builder.Lte, *to))
|
|
}
|
|
queryFunc := func(ctx context.Context, collection *mongo.Collection) (*mongo.Cursor, error) {
|
|
return collection.Find(ctx, timeLimitedQuery.BuildQuery(), timeLimitedQuery.BuildOptions())
|
|
}
|
|
return ts.executeQuery(ctx, decoder, queryFunc)
|
|
}
|
|
|
|
func (ts *TimeSeries) Name() string {
|
|
return ts.collection.Name()
|
|
}
|