From bf11ba6f68bab085135e33251bc747fa57fef6d2 Mon Sep 17 00:00:00 2001 From: xuthus5 Date: Fri, 23 Jun 2023 23:09:36 +0800 Subject: [PATCH] feat: update and find action --- delete.go | 105 +++++++++++++++++++ find.go | 309 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 4 +- go.sum | 11 ++ insert.go | 4 +- scope.go | 36 ++++--- update.go | 104 ++++++++++++++++++ 7 files changed, 557 insertions(+), 16 deletions(-) create mode 100644 delete.go create mode 100644 find.go create mode 100644 update.go diff --git a/delete.go b/delete.go new file mode 100644 index 0000000..6479145 --- /dev/null +++ b/delete.go @@ -0,0 +1,105 @@ +package mdbc + +import ( + "context" + "gitter.top/common/goref" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type deleteScope struct { + *scope + *mdbc + ctx context.Context + opts *options.DeleteOptions + filter interface{} + err error +} + +func (ds *deleteScope) SetContext(ctx context.Context) *deleteScope { + ds.ctx = ctx + return ds +} + +func (ds *deleteScope) SetOptions(opts options.DeleteOptions) *deleteScope { + ds.opts = &opts + return ds +} + +// SetID delete documents based on ID, has the highest priority. +func (ds *deleteScope) SetID(id ...string) *deleteScope { + if len(id) == 0 { + return ds + } + if len(id) == 1 { + ds.filter = bson.M{ + "_id": id, + } + } + if len(id) > 1 { + ds.filter = bson.M{ + "_id": bson.M{"$in": id}, + } + } + return ds +} + +// SetFilter delete documents based on filter condition, filter type only support map or struct +func (ds *deleteScope) SetFilter(filter interface{}) *deleteScope { + if ds.filter != nil { + return ds + } + + if filter == nil { + ds.filter = bson.M{} + return ds + } + + if goref.IsBaseStruct(filter) { + ds.filter, ds.err = goref.StructToMap(filter, goref.WithOmitempty(true), goref.WithTag("bson")) + return ds + } + + if goref.IsBaseMap(filter) { + ds.filter = filter + return ds + } + + return ds +} + +func (ds *deleteScope) mergeOptins() { + if ds.ctx == nil { + ds.ctx = context.Background() + } + if ds.opts == nil { + ds.opts = new(options.DeleteOptions) + } + if ds.filter == nil { + ds.filter = bson.M{} + } +} + +func (ds *deleteScope) One() (int64, error) { + ds.mergeOptins() + if ds.err != nil { + return 0, ds.err + } + result, err := ds.database.Collection(ds.tableName).DeleteOne(ds.ctx, ds.filter, ds.opts) + if err != nil { + return 0, err + } + return result.DeletedCount, nil +} + +func (ds *deleteScope) Many() (int64, error) { + ds.mergeOptins() + if ds.err != nil { + return 0, ds.err + } + result, err := ds.database.Collection(ds.tableName).DeleteMany(ds.ctx, ds.filter, ds.opts) + if err != nil { + return 0, err + } + return result.DeletedCount, nil +} diff --git a/find.go b/find.go new file mode 100644 index 0000000..293351f --- /dev/null +++ b/find.go @@ -0,0 +1,309 @@ +package mdbc + +import ( + "context" + "fmt" + "gitter.top/common/goref" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type findScope struct { + *scope + *mdbc + ctx context.Context + opts any + limit *int64 + skip *int64 + upsert *bool + sort bson.D + filter any + selects any + doc any +} + +func (fs *findScope) SetContext(ctx context.Context) *findScope { + fs.ctx = ctx + return fs +} + +func (fs *findScope) SetFindOptions(opts options.FindOptions) *findScope { + fs.opts = &opts + return fs +} + +func (fs *findScope) SetFindOneOptions(opts options.FindOneOptions) *findScope { + fs.opts = &opts + return fs +} + +func (fs *findScope) SetFindOneAndDeleteOptions(opts options.FindOneAndDeleteOptions) *findScope { + fs.opts = &opts + return fs +} + +func (fs *findScope) SetFindOneAndUpdateOptions(opts options.FindOneAndUpdateOptions) *findScope { + fs.opts = &opts + return fs +} + +func (fs *findScope) SetFindOneAndReplaceOptions(opts options.FindOneAndReplaceOptions) *findScope { + fs.opts = &opts + return fs +} + +func (fs *findScope) SetLimit(limit int64) *findScope { + fs.limit = &limit + return fs +} + +func (fs *findScope) SetSkip(skip int64) *findScope { + fs.skip = &skip + return fs +} + +func (fs *findScope) SetUpsert(upsert bool) *findScope { + fs.upsert = &upsert + return fs +} + +// SetSelects select field, such as: {key: 1, key: 0} +// 1: display 0: hidden +func (fs *findScope) SetSelects(selects any) *findScope { + if !goref.IsBaseMap(selects) { + return fs + } + fs.selects = selects + return fs +} + +func (fs *findScope) SetSort(sort bson.D) *findScope { + fs.sort = sort + return fs +} + +func (fs *findScope) findMergeOptions() { + if fs.ctx == nil { + fs.ctx = context.Background() + } + if fs.filter == nil { + fs.filter = bson.M{} + } + if fs.opts == nil { + fs.opts = new(options.FindOptions) + } + opts := fs.opts.(*options.FindOptions) + if fs.limit != nil { + opts.Limit = fs.limit + } + if fs.skip == nil { + opts.Skip = fs.skip + } + if fs.sort != nil { + opts.Sort = fs.sort + } + if fs.selects != nil { + opts.Projection = fs.selects + } + fs.opts = opts +} + +// GetList get record list, list type is *[]*struct +func (fs *findScope) GetList(list any) error { + if !goref.IsBaseList(list) { + return fmt.Errorf("invalid list type, must be *[]*struct") + } + fs.findMergeOptions() + cursor, err := fs.database.Collection(fs.tableName).Find(fs.ctx, fs.filter, fs.opts.(*options.FindOptions)) + if err != nil { + return err + } + err = cursor.All(fs.ctx, list) + if err != nil { + return err + } + return nil +} + +// GetMap get record map, m type is *map[string]*struct, field is struct field +func (fs *findScope) GetMap(m any, field string) error { + if !goref.IsBaseMap(m) { + return fmt.Errorf("invalid m type, must be based on map") + } + fs.findMergeOptions() + cursor, err := fs.database.Collection(fs.tableName).Find(fs.ctx, fs.filter, fs.opts.(*options.FindOptions)) + if err != nil { + return err + } + list := goref.BuildSlice(fs.modelKind) + err = cursor.All(fs.ctx, list) + if err != nil { + return err + } + if err := goref.SliceStructToMap(list, m, field); err != nil { + return err + } + return nil +} + +func (fs *findScope) findOneMergeOptions() { + if fs.ctx == nil { + fs.ctx = context.Background() + } + if fs.filter == nil { + fs.filter = bson.M{} + } + if fs.opts == nil { + fs.opts = new(options.FindOneOptions) + } + opts := fs.opts.(*options.FindOneOptions) + if fs.skip == nil { + opts.Skip = fs.skip + } + if fs.sort != nil { + opts.Sort = fs.sort + } + if fs.selects != nil { + opts.Projection = fs.selects + } + fs.opts = opts +} + +func (fs *findScope) FindOne(doc any) error { + if !goref.IsBaseStruct(doc) { + return fmt.Errorf("invalid doc type, must be *struct") + } + fs.findOneMergeOptions() + result := fs.database.Collection(fs.tableName).FindOne(fs.ctx, fs.filter, fs.opts.(*options.FindOneOptions)) + if result.Err() != nil { + return result.Err() + } + if err := result.Decode(doc); err != nil { + return err + } + return nil +} + +func (fs *findScope) findOneAndDeleteMergeOptions() { + if fs.ctx == nil { + fs.ctx = context.Background() + } + if fs.filter == nil { + fs.filter = bson.M{} + } + if fs.opts == nil { + fs.opts = new(options.FindOneAndDeleteOptions) + } + opts := fs.opts.(*options.FindOneAndDeleteOptions) + if fs.sort != nil { + opts.Sort = fs.sort + } + if fs.selects != nil { + opts.Projection = fs.selects + } + fs.opts = opts +} + +func (fs *findScope) FindOneAndDelete() error { + fs.findOneAndDeleteMergeOptions() + result := fs.database.Collection(fs.tableName).FindOneAndDelete(fs.ctx, fs.filter, fs.opts.(*options.FindOneAndDeleteOptions)) + if result.Err() != nil { + return result.Err() + } + return nil +} + +func (fs *findScope) FindOneAndDeleteWithValue(doc any) error { + if !goref.IsBaseStruct(doc) { + return fmt.Errorf("invalid doc type, must be *struct") + } + fs.findOneAndDeleteMergeOptions() + result := fs.database.Collection(fs.tableName).FindOneAndDelete(fs.ctx, fs.filter, fs.opts.(*options.FindOneAndDeleteOptions)) + if result.Err() != nil { + return result.Err() + } + if err := result.Decode(doc); err != nil { + return err + } + return nil +} + +func (fs *findScope) findOneAndUpdateMergeOptions() { + if fs.ctx == nil { + fs.ctx = context.Background() + } + if fs.filter == nil { + fs.filter = bson.M{} + } + if fs.opts == nil { + fs.opts = new(options.FindOneAndUpdateOptions) + } + opts := fs.opts.(*options.FindOneAndUpdateOptions) + if fs.sort != nil { + opts.Sort = fs.sort + } + if fs.upsert != nil { + opts.Upsert = fs.upsert + } + if fs.selects != nil { + opts.Projection = fs.selects + } + fs.opts = opts + if goref.IsBaseStruct(fs.doc) { + fs.doc = bson.M{"$set": fs.doc} + } + if goref.IsBaseMap(fs.doc) && !goref.ExistMapKey(fs.doc, "$set") { + fs.doc = bson.M{"$set": fs.doc} + } +} + +// FindOneAndUpdate find one and update, doc type *struct or map +func (fs *findScope) FindOneAndUpdate(doc any) error { + fs.doc = doc + fs.findOneAndUpdateMergeOptions() + result := fs.database.Collection(fs.tableName).FindOneAndUpdate(fs.ctx, fs.filter, fs.doc, fs.opts.(*options.FindOneAndUpdateOptions)) + if result.Err() != nil { + return result.Err() + } + return nil +} + +func (fs *findScope) findOneAndReplaceMergeOptions() { + if fs.ctx == nil { + fs.ctx = context.Background() + } + if fs.filter == nil { + fs.filter = bson.M{} + } + if fs.opts == nil { + fs.opts = new(options.FindOneAndReplaceOptions) + } + opts := fs.opts.(*options.FindOneAndReplaceOptions) + if fs.sort != nil { + opts.Sort = fs.sort + } + if fs.upsert != nil { + opts.Upsert = fs.upsert + } + if fs.selects != nil { + opts.Projection = fs.selects + } + fs.opts = opts + if goref.IsBaseStruct(fs.doc) { + fs.doc = bson.M{"$set": fs.doc} + } + if goref.IsBaseMap(fs.doc) && !goref.ExistMapKey(fs.doc, "$set") { + fs.doc = bson.M{"$set": fs.doc} + } +} + +// FindOneAndReplace find one and replace, doc type *struct or map +func (fs *findScope) FindOneAndReplace(doc any) error { + fs.doc = doc + fs.findOneAndReplaceMergeOptions() + result := fs.database.Collection(fs.tableName).FindOneAndReplace(fs.ctx, fs.filter, fs.doc, fs.opts.(*options.FindOneAndReplaceOptions)) + if result.Err() != nil { + return result.Err() + } + return nil +} diff --git a/go.mod b/go.mod index 419e996..e2d46db 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,9 @@ go 1.20 require ( github.com/sirupsen/logrus v1.9.3 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.8.4 gitter.top/coco/lormatter v0.0.0-20230409145644-f9cb43f740dc - gitter.top/common/goref v0.0.0-20230622151024-9b220e13c7cd + gitter.top/common/goref v0.0.0-20230623144827-8cd99d7ed68f go.mongodb.org/mongo-driver v1.11.7 google.golang.org/protobuf v1.30.0 ) diff --git a/go.sum b/go.sum index 75f1fd4..6999f42 100644 --- a/go.sum +++ b/go.sum @@ -27,9 +27,12 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -44,6 +47,14 @@ gitter.top/coco/lormatter v0.0.0-20230409145644-f9cb43f740dc h1:B0bnfX8Y/dETV+zd gitter.top/coco/lormatter v0.0.0-20230409145644-f9cb43f740dc/go.mod h1:vyfU6MQ56tkoFKSzEzhYSwXjgZHMcu3AViHC3hIyO7I= gitter.top/common/goref v0.0.0-20230622151024-9b220e13c7cd h1:EeOtSwGexcx0u1AE91wnemiOGedENxaKcJUXxpzcgiE= gitter.top/common/goref v0.0.0-20230622151024-9b220e13c7cd/go.mod h1:oK6jZQ/ISS8gZ78rvww6p7FuLUzaJ+S5F5UXSqO7Lr0= +gitter.top/common/goref v0.0.0-20230623104541-a0005020066e h1:y56ZGlp2Pz+pia3gEeRAgrRoCOoiecxg0RtoaCEj7bY= +gitter.top/common/goref v0.0.0-20230623104541-a0005020066e/go.mod h1:oK6jZQ/ISS8gZ78rvww6p7FuLUzaJ+S5F5UXSqO7Lr0= +gitter.top/common/goref v0.0.0-20230623122346-fa52c8a5753e h1:er7LiuFgv7+vFGdJosiHLWgWdjpOQxoL+qi5roxYyTk= +gitter.top/common/goref v0.0.0-20230623122346-fa52c8a5753e/go.mod h1:oK6jZQ/ISS8gZ78rvww6p7FuLUzaJ+S5F5UXSqO7Lr0= +gitter.top/common/goref v0.0.0-20230623134616-9d375bad30e4 h1:yivG8g8Hty/rMiPLWhGDPmRJQXvcZwBtLvmminEEAd0= +gitter.top/common/goref v0.0.0-20230623134616-9d375bad30e4/go.mod h1:oK6jZQ/ISS8gZ78rvww6p7FuLUzaJ+S5F5UXSqO7Lr0= +gitter.top/common/goref v0.0.0-20230623144827-8cd99d7ed68f h1:+b+DwvEDKS6hgtJ49H+F0m310NO0zMVGn2kfXh2UWwA= +gitter.top/common/goref v0.0.0-20230623144827-8cd99d7ed68f/go.mod h1:9ZCvSyMgyJ6ODKdgvHgnNuRlBhvlzIOBcwhP3Buz5SA= go.mongodb.org/mongo-driver v1.11.7 h1:LIwYxASDLGUg/8wOhgOOZhX8tQa/9tgZPgzZoVqJvcs= go.mongodb.org/mongo-driver v1.11.7/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= diff --git a/insert.go b/insert.go index be67b00..9d1ea17 100644 --- a/insert.go +++ b/insert.go @@ -70,12 +70,12 @@ func (is *insertScope) SetContext(ctx context.Context) *insertScope { } func (is *insertScope) SetInsertOneOptions(opts options.InsertOneOptions) *insertScope { - is.opts = opts + is.opts = &opts return is } func (is *insertScope) SetInsertManyOptions(opts options.InsertManyOptions) *insertScope { - is.opts = opts + is.opts = &opts return is } diff --git a/scope.go b/scope.go index c78678a..64c6d18 100644 --- a/scope.go +++ b/scope.go @@ -24,12 +24,12 @@ type Collection interface { BulkWrite() Clone() Count() *countScope - Delete() - Find() + Delete() *deleteScope + Find() *findScope Drop() *dropScope Indexes() *indexesScope - Insert() - Update() + Insert() *insertScope + Update() *updateScope Watch() } @@ -53,12 +53,18 @@ func (s *scope) Count() *countScope { } } -func (s *scope) Delete() { - +func (s *scope) Delete() *deleteScope { + return &deleteScope{ + scope: s, + mdbc: s.mdbc, + } } -func (s *scope) Find() { - +func (s *scope) Find() *findScope { + return &findScope{ + scope: s, + mdbc: s.mdbc, + } } func (s *scope) Drop() *dropScope { @@ -75,12 +81,18 @@ func (s *scope) Indexes() *indexesScope { } } -func (s *scope) Insert() { - +func (s *scope) Insert() *insertScope { + return &insertScope{ + scope: s, + mdbc: s.mdbc, + } } -func (s *scope) Update() { - +func (s *scope) Update() *updateScope { + return &updateScope{ + scope: s, + mdbc: s.mdbc, + } } func (s *scope) Watch() { diff --git a/update.go b/update.go new file mode 100644 index 0000000..b0ab35e --- /dev/null +++ b/update.go @@ -0,0 +1,104 @@ +package mdbc + +import ( + "context" + "fmt" + "gitter.top/common/goref" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type updateScope struct { + *scope + *mdbc + ctx context.Context + filter any + docs any + opts any + err error +} + +func (us *updateScope) SetContext(ctx context.Context) *updateScope { + us.ctx = ctx + return us +} + +func (us *updateScope) SetUpdateOptions(opts options.UpdateOptions) *updateScope { + us.opts = &opts + return us +} + +func (us *updateScope) SetReplaceOptions(opts options.ReplaceOptions) *updateScope { + us.opts = &opts + return us +} + +func (us *updateScope) SetID(id ...string) *updateScope { + if len(id) == 0 { + return us + } + if len(id) == 1 { + us.filter = bson.M{ + "_id": id, + } + } + if len(id) > 1 { + us.filter = bson.M{ + "_id": bson.M{"$in": id}, + } + } + return us +} + +func (us *updateScope) SetFilter(filter interface{}) *updateScope { + if us.filter != nil { + return us + } + if filter == nil { + us.filter = bson.M{} + return us + } + if goref.IsBaseStruct(filter) || goref.IsBaseMap(filter) { + us.filter = filter + return us + } + us.err = fmt.Errorf("filter type must be struct or map") + return us +} + +func (us *updateScope) mergeOptions() { + if us.ctx == nil { + us.ctx = context.Background() + } + if us.filter == nil { + us.filter = bson.M{} + } + if us.opts == nil { + us.opts = new(options.UpdateOptions) + } + if goref.IsBaseStruct(us.docs) { + us.docs = bson.M{"$set": us.docs} + } + if goref.IsBaseMap(us.docs) && !goref.ExistMapKey(us.docs, "$set") { + us.docs = bson.M{"$set": us.docs} + } +} + +func (us *updateScope) One(docs interface{}) (*mongo.UpdateResult, error) { + us.docs = docs + us.mergeOptions() + return us.database.Collection(us.tableName).UpdateOne(us.ctx, us.filter, us.docs, us.opts.(*options.UpdateOptions)) +} + +func (us *updateScope) Many(docs interface{}) (*mongo.UpdateResult, error) { + us.docs = docs + us.mergeOptions() + return us.database.Collection(us.tableName).UpdateMany(us.ctx, us.filter, us.docs, us.opts.(*options.UpdateOptions)) +} + +func (us *updateScope) Replace(docs interface{}) (*mongo.UpdateResult, error) { + us.docs = docs + us.mergeOptions() + return us.database.Collection(us.tableName).ReplaceOne(us.ctx, us.filter, us.docs, us.opts.(*options.ReplaceOptions)) +}