package wmo import ( "context" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ct "gogs.mikescher.com/BlackForestBytes/goext/cursortoken" "gogs.mikescher.com/BlackForestBytes/goext/langext" "reflect" ) type fullTypeRef[TData any] struct { IsPointer bool Kind reflect.Kind Type reflect.Type UnderlyingType reflect.Type Name string Index []int } type Coll[TData any] struct { coll *mongo.Collection dataTypeMap map[string]fullTypeRef[TData] } func (c *Coll[TData]) Collection() *mongo.Collection { return c.coll } func (c *Coll[TData]) Name() string { return c.coll.Name() } func (c *Coll[TData]) Indexes() mongo.IndexView { return c.coll.Indexes() } func (c *Coll[TData]) Drop(ctx context.Context) error { return c.coll.Drop(ctx) } func (c *Coll[TData]) FindOne(ctx context.Context, filter any) (TData, error) { var res TData err := c.coll.FindOne(ctx, filter).Decode(&res) if err != nil { return *new(TData), err } return res, nil } func (c *Coll[TData]) FindOneOpt(ctx context.Context, filter any) (*TData, error) { var res TData err := c.coll.FindOne(ctx, filter).Decode(&res) if err == mongo.ErrNoDocuments { return nil, nil } if err != nil { return nil, err } return &res, nil } func (c *Coll[TData]) FindOneByID(ctx context.Context, id any) (TData, error) { var res TData err := c.coll.FindOne(ctx, bson.M{"_id": id}).Decode(&res) if err != nil { return *new(TData), err } return res, nil } func (c *Coll[TData]) FindOneOptByID(ctx context.Context, id any) (*TData, error) { var res TData err := c.coll.FindOne(ctx, bson.M{"_id": id}).Decode(&res) if err == mongo.ErrNoDocuments { return nil, nil } if err != nil { return nil, err } return &res, nil } func (c *Coll[TData]) Find(ctx context.Context, filter any, opts ...*options.FindOptions) ([]TData, error) { cursor, err := c.coll.Find(ctx, filter, opts...) if err != nil { return nil, err } res := make([]TData, 0, cursor.RemainingBatchLength()) err = cursor.All(ctx, &res) if err != nil { return nil, err } return res, nil } func (c *Coll[TData]) Aggregate(ctx context.Context, pipeline mongo.Pipeline, opts ...*options.AggregateOptions) ([]TData, error) { cursor, err := c.coll.Aggregate(ctx, pipeline, opts...) if err != nil { return nil, err } res := make([]TData, 0, cursor.RemainingBatchLength()) err = cursor.All(ctx, &res) if err != nil { return nil, err } return res, nil } func (c *Coll[TData]) ReplaceOne(ctx context.Context, id any, value TData) error { _, err := c.coll.UpdateOne(ctx, bson.M{"_id": id}, value) if err != nil { return err } return nil } func (c *Coll[TData]) UpdateOne(ctx context.Context, filterQuery any, updateQuery any) error { _, err := c.coll.UpdateOne(ctx, filterQuery, updateQuery) if err != nil { return err } return nil } func (c *Coll[TData]) UpdateOneByID(ctx context.Context, id any, updateQuery any) error { _, err := c.coll.UpdateOne(ctx, bson.M{"_id": id}, updateQuery) if err != nil { return err } return nil } func (c *Coll[TData]) UpdateOneAndReturn(ctx context.Context, id any, updateQuery any) (TData, error) { _, err := c.coll.UpdateOne(ctx, bson.M{"_id": id}, updateQuery) if err != nil { return *new(TData), err } var res TData err = c.coll.FindOne(ctx, bson.M{"_id": id}).Decode(&res) if err != nil { return *new(TData), err } return res, nil } func (c *Coll[TData]) DeleteOne(ctx context.Context, id any) error { _, err := c.coll.DeleteOne(ctx, bson.M{"_id": id}) if err != nil { return err } return nil } func (c *Coll[TData]) DeleteMany(ctx context.Context, filterQuery any) (*mongo.DeleteResult, error) { res, err := c.coll.DeleteMany(ctx, filterQuery) if err != nil { return nil, err } return res, nil } func (c *Coll[TData]) List(ctx context.Context, filter ct.Filter, pageSize *int, inTok ct.CursorToken) ([]TData, ct.CursorToken, error) { if inTok.Mode == ct.CTMEnd { return make([]TData, 0), ct.End(), nil } pipeline := filter.FilterQuery() sortPrimary, sortDirPrimary, sortSecondary, sortDirSecondary := filter.Pagination() paginationPipeline, err := CreatePagination(c, inTok, sortPrimary, sortDirPrimary, sortSecondary, sortDirSecondary, pageSize) if err != nil { return nil, ct.CursorToken{}, err } pipeline = append(pipeline, paginationPipeline...) cursor, err := c.coll.Aggregate(ctx, pipeline) if err != nil { return nil, ct.CursorToken{}, err } entities := make([]TData, 0, cursor.RemainingBatchLength()+1) for (pageSize == nil || len(entities) != *pageSize) && cursor.Next(ctx) { var entry TData err = cursor.Decode(&entry) if err != nil { return nil, ct.CursorToken{}, err } entities = append(entities, entry) } if pageSize == nil || len(entities) <= *pageSize || !cursor.TryNext(ctx) { return entities, ct.End(), nil } last := entities[len(entities)-1] nextToken, _ := c.createToken(sortPrimary, sortDirPrimary, sortSecondary, sortDirSecondary, last, pageSize) return entities, nextToken, nil } func (c *Coll[TData]) createToken(fieldPrimary string, dirPrimary ct.SortDirection, fieldSecondary *string, dirSecondary *ct.SortDirection, lastEntity TData, pageSize *int) (ct.CursorToken, error) { valuePrimary, err := c.getFieldValueAsTokenString(lastEntity, fieldPrimary) if err != nil { return ct.CursorToken{}, err } valueSeconary := "" if fieldSecondary != nil && dirSecondary != nil { valueSeconary, err = c.getFieldValueAsTokenString(lastEntity, *fieldSecondary) if err != nil { return ct.CursorToken{}, err } } return ct.CursorToken{ Mode: ct.CTMNormal, ValuePrimary: valuePrimary, ValueSecondary: valueSeconary, Direction: dirPrimary, PageSize: langext.Coalesce(pageSize, 0), Extra: ct.Extra{}, }, nil }