diff --git a/exerr/data.go b/exerr/data.go index 6dcbc9f..a5c2002 100644 --- a/exerr/data.go +++ b/exerr/data.go @@ -46,6 +46,7 @@ var ( TypeCursorTokenDecode = NewType("CURSOR_TOKEN_DECODE", langext.Ptr(500)) TypeMongoFilter = NewType("MONGO_FILTER", langext.Ptr(500)) TypeMongoReflection = NewType("MONGO_REFLECTION", langext.Ptr(500)) + TypeMongoInvalidOpt = NewType("MONGO_INVALIDOPT", langext.Ptr(500)) TypeWrap = NewType("Wrap", nil) diff --git a/goextVersion.go b/goextVersion.go index d604dbe..694f938 100644 --- a/goextVersion.go +++ b/goextVersion.go @@ -1,5 +1,5 @@ package goext -const GoextVersion = "0.0.308" +const GoextVersion = "0.0.309" -const GoextVersionTimestamp = "2023-11-09T10:02:31+0100" +const GoextVersionTimestamp = "2023-11-09T10:17:29+0100" diff --git a/wmo/queryFind.go b/wmo/queryFind.go index 9f58b74..6cb12e5 100644 --- a/wmo/queryFind.go +++ b/wmo/queryFind.go @@ -4,18 +4,52 @@ import ( "context" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" "gogs.mikescher.com/BlackForestBytes/goext/exerr" "gogs.mikescher.com/BlackForestBytes/goext/langext" ) -func (c *Coll[TData]) Find(ctx context.Context, filter bson.M) ([]TData, error) { +func (c *Coll[TData]) Find(ctx context.Context, filter bson.M, opts ...*options.FindOptions) ([]TData, error) { pipeline := mongo.Pipeline{} pipeline = append(pipeline, bson.D{{Key: "$match", Value: filter}}) + for _, opt := range opts { + if opt != nil && opt.Sort != nil { + pipeline = append(pipeline, bson.D{{Key: "$sort", Value: opt.Sort}}) + } + } + + for _, opt := range opts { + if opt != nil && opt.Skip != nil { + pipeline = append(pipeline, bson.D{{Key: "$skip", Value: *opt.Skip}}) + } + } + + for _, opt := range opts { + if opt != nil && opt.Limit != nil { + pipeline = append(pipeline, bson.D{{Key: "$limit", Value: *opt.Limit}}) + } + } + pipeline = langext.ArrConcat(pipeline, c.extraModPipeline) - cursor, err := c.coll.Aggregate(ctx, pipeline) + for _, opt := range opts { + if opt != nil && opt.Projection != nil { + pipeline = append(pipeline, bson.D{{Key: "$project", Value: opt.Projection}}) + } + } + + convOpts := make([]*options.AggregateOptions, 0, len(opts)) + for _, v := range opts { + vConv, err := convertFindOpt(v) + if err != nil { + return nil, exerr.Wrap(err, "mongo-aggregation failed").Any("pipeline", pipeline).Str("collection", c.Name()).Build() + } + convOpts = append(convOpts, vConv) + } + + cursor, err := c.coll.Aggregate(ctx, pipeline, convOpts...) if err != nil { return nil, exerr.Wrap(err, "mongo-aggregation failed").Any("pipeline", pipeline).Str("collection", c.Name()).Build() } @@ -27,3 +61,66 @@ func (c *Coll[TData]) Find(ctx context.Context, filter bson.M) ([]TData, error) return res, nil } + +// converts FindOptions to AggregateOptions +func convertFindOpt(v *options.FindOptions) (*options.AggregateOptions, error) { + if v == nil { + return nil, nil + } + + r := options.Aggregate() + + if v.AllowDiskUse != nil { + r.SetAllowDiskUse(*v.AllowDiskUse) + } + if v.AllowPartialResults != nil { + return nil, exerr.New(exerr.TypeMongoInvalidOpt, "Invalid option 'AllowPartialResults' (cannot convert to AggregateOptions)").Build() + } + if v.BatchSize != nil { + r.SetBatchSize(*v.BatchSize) + } + if v.Collation != nil { + r.SetCollation(v.Collation) + } + if v.Comment != nil { + r.SetComment(*v.Comment) + } + if v.CursorType != nil { + return nil, exerr.New(exerr.TypeMongoInvalidOpt, "Invalid option 'CursorType' (cannot convert to AggregateOptions)").Build() + } + if v.Hint != nil { + r.SetHint(v.Hint) + } + if v.Max != nil { + return nil, exerr.New(exerr.TypeMongoInvalidOpt, "Invalid option 'Max' (cannot convert to AggregateOptions)").Build() + } + if v.MaxAwaitTime != nil { + r.SetMaxAwaitTime(*v.MaxAwaitTime) + } + if v.MaxTime != nil { + r.SetMaxTime(*v.MaxTime) + } + if v.Min != nil { + return nil, exerr.New(exerr.TypeMongoInvalidOpt, "Invalid option 'Min' (cannot convert to AggregateOptions)").Build() + } + if v.NoCursorTimeout != nil { + return nil, exerr.New(exerr.TypeMongoInvalidOpt, "Invalid option 'NoCursorTimeout' (cannot convert to AggregateOptions)").Build() + } + if v.OplogReplay != nil { + return nil, exerr.New(exerr.TypeMongoInvalidOpt, "Invalid option 'OplogReplay' (cannot convert to AggregateOptions)").Build() + } + if v.ReturnKey != nil { + return nil, exerr.New(exerr.TypeMongoInvalidOpt, "Invalid option 'ReturnKey' (cannot convert to AggregateOptions)").Build() + } + if v.ShowRecordID != nil { + return nil, exerr.New(exerr.TypeMongoInvalidOpt, "Invalid option 'ShowRecordID' (cannot convert to AggregateOptions)").Build() + } + if v.Snapshot != nil { + return nil, exerr.New(exerr.TypeMongoInvalidOpt, "Invalid option 'Snapshot' (cannot convert to AggregateOptions)").Build() + } + if v.Let != nil { + r.SetLet(v.Let) + } + + return r, nil +}