From 84ae518fe95394d9fa7c85ffefb71eb39ea8e72e Mon Sep 17 00:00:00 2001 From: Nikita Tokarchuk Date: Wed, 18 Nov 2020 20:09:57 +0100 Subject: [PATCH] Now all query filters can validate themselves using Valider interface --- mongox/database/count.go | 8 ++++++-- mongox/database/deletearray.go | 10 +++++++--- mongox/database/deleteone.go | 8 ++++++-- mongox/database/loadarray.go | 6 +++++- mongox/database/loadone.go | 6 +++++- mongox/database/loadstream.go | 8 ++++++-- mongox/database/saveone.go | 8 ++++++-- mongox/database/updateone.go | 8 ++++++-- mongox/query/compose.go | 17 ++++++++++++++--- mongox/query/validate.go | 6 ++++++ 10 files changed, 67 insertions(+), 18 deletions(-) create mode 100644 mongox/query/validate.go diff --git a/mongox/database/count.go b/mongox/database/count.go index c2ea792..d8711af 100644 --- a/mongox/database/count.go +++ b/mongox/database/count.go @@ -10,11 +10,15 @@ import ( // target is used only to get collection by tag so it'd be better to use nil ptr here func (d *Database) Count(target interface{}, filters ...interface{}) (result int64, err error) { + composed, err := query.Compose(filters...) + if err != nil { + return + } + collection := d.GetCollectionOf(target) - opts := options.Count() - composed := query.Compose(filters...) ctx := query.WithContext(d.Context(), composed) + opts := options.Count() opts.Limit = composed.Limiter() opts.Skip = composed.Skipper() diff --git a/mongox/database/deletearray.go b/mongox/database/deletearray.go index a591730..2eb1521 100644 --- a/mongox/database/deletearray.go +++ b/mongox/database/deletearray.go @@ -35,10 +35,14 @@ func (d *Database) DeleteArray(target interface{}, filters ...interface{}) (err zeroElem := reflect.Zero(targetSliceElemT) targetLen := targetSliceV.Len() + + composed, err := query.Compose(filters...) + if err != nil { + return + } + collection := d.GetCollectionOf(zeroElem.Interface()) - opts := options.Delete() ids := primitive.A{} - composed := query.Compose(filters...) ctx := query.WithContext(d.Context(), composed) for i := 0; i < targetLen; i++ { @@ -61,7 +65,7 @@ func (d *Database) DeleteArray(target interface{}, filters ...interface{}) (err composed.And(primitive.M{"_id": primitive.M{"$in": ids}}) - result, err := collection.DeleteMany(ctx, composed.M(), opts) + result, err := collection.DeleteMany(ctx, composed.M(), options.Delete()) if err != nil { return } diff --git a/mongox/database/deleteone.go b/mongox/database/deleteone.go index 6f33c4a..189495d 100644 --- a/mongox/database/deleteone.go +++ b/mongox/database/deleteone.go @@ -15,12 +15,16 @@ import ( // DeleteOne removes a document from a database and then returns it into target func (d *Database) DeleteOne(target interface{}, filters ...interface{}) (err error) { + composed, err := query.Compose(filters...) + if err != nil { + return + } + collection := d.GetCollectionOf(target) - opts := &options.FindOneAndDeleteOptions{} - composed := query.Compose(filters...) protected := base.GetProtection(target) ctx := query.WithContext(d.Context(), composed) + opts := options.FindOneAndDelete() opts.Sort = composed.Sorter() if !reflect2.IsNil(target) { diff --git a/mongox/database/loadarray.go b/mongox/database/loadarray.go index b618647..d22f46a 100644 --- a/mongox/database/loadarray.go +++ b/mongox/database/loadarray.go @@ -31,7 +31,11 @@ func (d *Database) LoadArray(target interface{}, filters ...interface{}) (err er panic(fmt.Errorf("target slice should contain ptrs")) } - composed := query.Compose(filters...) + composed, err := query.Compose(filters...) + if err != nil { + return + } + zeroElem := reflect.Zero(targetSliceElemT) hasPreloader, _ := composed.Preloader() ctx := query.WithContext(d.Context(), composed) diff --git a/mongox/database/loadone.go b/mongox/database/loadone.go index 6eb8d7f..95bb18a 100644 --- a/mongox/database/loadone.go +++ b/mongox/database/loadone.go @@ -11,7 +11,11 @@ import ( // LoadOne function loads a first single target document by a query func (d *Database) LoadOne(target interface{}, filters ...interface{}) (err error) { - composed := query.Compose(append(filters, query.Limit(1))...) + composed, err := query.Compose(append(filters, query.Limit(1))...) + if err != nil { + return + } + hasPreloader, _ := composed.Preloader() ctx := query.WithContext(d.Context(), composed) diff --git a/mongox/database/loadstream.go b/mongox/database/loadstream.go index 7a53145..d9644f4 100644 --- a/mongox/database/loadstream.go +++ b/mongox/database/loadstream.go @@ -10,12 +10,16 @@ import ( // LoadStream function loads documents one by one into a target channel func (d *Database) LoadStream(target interface{}, filters ...interface{}) (loader mongox.StreamLoader, err error) { - var cursor *mongox.Cursor + composed, err := query.Compose(filters...) + if err != nil { + return + } - composed := query.Compose(filters...) hasPreloader, _ := composed.Preloader() ctx := query.WithContext(d.Context(), composed) + var cursor *mongox.Cursor + if hasPreloader { cursor, err = d.createAggregateLoad(target, composed) } else { diff --git a/mongox/database/saveone.go b/mongox/database/saveone.go index 07f7b92..7de62d1 100644 --- a/mongox/database/saveone.go +++ b/mongox/database/saveone.go @@ -13,15 +13,19 @@ import ( // SaveOne saves a single source document to the database func (d *Database) SaveOne(source interface{}, filters ...interface{}) (err error) { + composed, err := query.Compose(filters...) + if err != nil { + return + } + collection := d.GetCollectionOf(source) - opts := options.FindOneAndReplace() id := base.GetID(source) protected := base.GetProtection(source) - composed := query.Compose(filters...) ctx := query.WithContext(d.Context(), composed) composed.And(primitive.M{"_id": id}) + opts := options.FindOneAndReplace() opts.SetUpsert(true) opts.SetReturnDocument(options.After) diff --git a/mongox/database/updateone.go b/mongox/database/updateone.go index 2b2f9b9..3a50fa7 100644 --- a/mongox/database/updateone.go +++ b/mongox/database/updateone.go @@ -13,13 +13,17 @@ import ( // UpdateOne updates a single document in the database and loads it into target func (d *Database) UpdateOne(target interface{}, filters ...interface{}) (err error) { + composed, err := query.Compose(filters...) + if err != nil { + return + } + 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 := options.FindOneAndUpdate() opts.SetReturnDocument(options.After) if protected != nil { diff --git a/mongox/query/compose.go b/mongox/query/compose.go index d3373e3..3e5cb93 100644 --- a/mongox/query/compose.go +++ b/mongox/query/compose.go @@ -12,12 +12,16 @@ import ( type applyFilterFunc = func(query *Query, filter interface{}) (ok bool) // Compose is a function to compose filters into a single query -func Compose(filters ...interface{}) (query *Query) { +func Compose(filters ...interface{}) (query *Query, err error) { query = &Query{} for _, filter := range filters { - if !Push(query, filter) { + ok, err := Push(query, filter) + if err != nil { + return nil, fmt.Errorf("invalid filter %v, %w", filter, err) + } + if !ok { panic(fmt.Errorf("unknown filter %v", filter)) } } @@ -26,12 +30,19 @@ func Compose(filters ...interface{}) (query *Query) { } // Push applies single filter to a query -func Push(query *Query, filter interface{}) (ok bool) { +func Push(query *Query, filter interface{}) (ok bool, err error) { ok = reflect2.IsNil(filter) if ok { return } + + valider, hasValider := filter.(Valider) + if hasValider { + err = valider.Valid() + } + if err != nil { + return } for _, applier := range []applyFilterFunc{ diff --git a/mongox/query/validate.go b/mongox/query/validate.go new file mode 100644 index 0000000..18a8fd3 --- /dev/null +++ b/mongox/query/validate.go @@ -0,0 +1,6 @@ +package query + +// Valider is a filter to validate the filter +type Valider interface { + Valid() (err error) +}