package query import ( "fmt" "github.com/modern-go/reflect2" "go.mongodb.org/mongo-driver/bson/primitive" "github.com/mainnika/mongox-go-driver/v2/mongox/base/protection" ) 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, err error) { query = &Query{} for _, filter := range filters { 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)) } } return } // Push applies single filter to a query func Push(query *Query, filter interface{}) (ok bool, err error) { ok = reflect2.IsNil(filter) if ok { return } validator, hasValidator := filter.(Validator) if hasValidator { err = validator.Validate() } if err != nil { return } for _, applier := range []applyFilterFunc{ applyBson, applyLimit, applySort, applySkip, applyProtection, applyPreloader, applyUpdater, applyCallbacks, } { ok = applier(query, filter) || ok } return } // applyBson is a fallback for a custom primitive.M func applyBson(query *Query, filter interface{}) (ok bool) { if filter, ok := filter.(primitive.M); ok { query.And(filter) return true } return false } // applyLimits extends query with a limiter func applyLimit(query *Query, filter interface{}) (ok bool) { if filter, ok := filter.(Limiter); ok { query.limiter = filter return true } return false } // applySort extends query with a sort rule func applySort(query *Query, filter interface{}) (ok bool) { if filter, ok := filter.(Sorter); ok { query.sorter = filter return true } return false } // applySkip extends query with a skip number func applySkip(query *Query, filter interface{}) (ok bool) { if filter, ok := filter.(Skipper); ok { query.skipper = filter return true } return false } func applyProtection(query *Query, filter interface{}) (ok bool) { var keyDoc = primitive.M{} switch filter := filter.(type) { case protection.Key: filter.PutToDocument(keyDoc) case *protection.Key: filter.PutToDocument(keyDoc) default: return false } query.And(keyDoc) return true } func applyPreloader(query *Query, filter interface{}) (ok bool) { if filter, ok := filter.(Preloader); ok { query.preloader = filter return true } return false } func applyUpdater(query *Query, filter interface{}) (ok bool) { if filter, ok := filter.(Updater); ok { query.updater = filter return true } return false } func applyCallbacks(query *Query, filter interface{}) (ok bool) { switch callback := filter.(type) { case OnDecode: query.ondecode = append(query.ondecode, Callback(callback)) case OnClose: query.onclose = append(query.onclose, Callback(callback)) default: return false } return true }