diff --git a/mongox/database/updateone.go b/mongox/database/updateone.go new file mode 100644 index 0000000..2b2f9b9 --- /dev/null +++ b/mongox/database/updateone.go @@ -0,0 +1,62 @@ +package database + +import ( + "time" + + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo/options" + + "github.com/mainnika/mongox-go-driver/v2/mongox/base" + "github.com/mainnika/mongox-go-driver/v2/mongox/query" +) + +// UpdateOne updates a single document in the database and loads it into target +func (d *Database) UpdateOne(target interface{}, filters ...interface{}) (err error) { + + collection := d.GetCollectionOf(target) + opts := options.FindOneAndUpdate() + protected := base.GetProtection(target) + composed := query.Compose(filters...) + ctx := query.WithContext(d.Context(), composed) + updater := composed.Updater() + + opts.SetReturnDocument(options.After) + + if protected != nil { + if !protected.X.IsZero() { + query.Push(composed, protected) + } + updater = append(updater, primitive.M{ + "$set": primitive.M{ + "_x": primitive.NewObjectID(), + "_v": time.Now().Unix(), + }, + }) + } + + defer func() { + invokerr := composed.OnClose().Invoke(ctx, target) + if err == nil { + err = invokerr + } + + return + }() + + result := collection.FindOneAndUpdate(ctx, composed.M(), updater, opts) + if result.Err() != nil { + return result.Err() + } + + err = result.Decode(target) + if err != nil { + return + } + + err = composed.OnDecode().Invoke(ctx, target) + if err != nil { + return + } + + return +} diff --git a/mongox/query/compose.go b/mongox/query/compose.go index 2d7f88e..d5737f6 100644 --- a/mongox/query/compose.go +++ b/mongox/query/compose.go @@ -38,6 +38,7 @@ func Push(q *Query, f interface{}) (ok bool) { ok = ok || applySkip(q, f) ok = ok || applyProtection(q, f) ok = ok || applyPreloader(q, f) + ok = ok || applyUpdater(q, f) ok = ok || applyCallbacks(q, f) return ok @@ -125,6 +126,16 @@ func applyPreloader(q *Query, f interface{}) (ok bool) { return false } +func applyUpdater(q *Query, f interface{}) (ok bool) { + + if f, ok := f.(Updater); ok { + q.updater = f + return true + } + + return false +} + func applyCallbacks(q *Query, f interface{}) (ok bool) { switch cb := f.(type) { diff --git a/mongox/query/query.go b/mongox/query/query.go index c57af98..a0f68f4 100644 --- a/mongox/query/query.go +++ b/mongox/query/query.go @@ -11,6 +11,7 @@ type Query struct { sorter Sorter skipper Skipper preloader Preloader + updater Updater ondecode Callbacks onclose Callbacks } @@ -64,6 +65,16 @@ func (q *Query) Skipper() (skip *int64) { return q.skipper.Skip() } +// Updater is an update command for a query +func (q *Query) Updater() (update bson.A) { + + if q.updater == nil { + return + } + + return q.updater.Update() +} + // Preloader is a preloader list for a query func (q *Query) Preloader() (ok bool, preloads []string) { diff --git a/mongox/query/update.go b/mongox/query/update.go new file mode 100644 index 0000000..cfbc18e --- /dev/null +++ b/mongox/query/update.go @@ -0,0 +1,20 @@ +package query + +import ( + "go.mongodb.org/mongo-driver/bson" +) + +// Updater is a filter to update the data +type Updater interface { + Update() (update bson.A) +} + +// Update is a simple implementations of the Updater filter +type Update bson.M + +var _ Updater = &Update{} + +// Update returns an update command +func (u Update) Update() (update bson.A) { + return bson.A{bson.M(u)} +}