| package filter |
|
|
| import ( |
| "errors" |
| "fmt" |
| "math" |
| "reflect" |
| "slices" |
| "strings" |
| "time" |
|
|
| "entgo.io/ent/dialect/sql" |
| "github.com/huandu/go-sqlbuilder" |
| "github.com/oklog/ulid/v2" |
| "github.com/samber/lo" |
|
|
| "github.com/openmeterio/openmeter/pkg/models" |
| ) |
|
|
| |
| type Filter interface { |
| |
| Validate() error |
| |
| ValidateWithComplexity(maxDepth int) error |
| |
| Select(field string) func(*sql.Selector) |
| |
| SelectWhereExpr(field string, q *sqlbuilder.SelectBuilder) string |
| |
| IsEmpty() bool |
| } |
|
|
| var ( |
| ErrFilterMultipleOperators = errors.New("filter is invalid: multiple operators are set") |
| ErrFilterComplexityExceeded = errors.New("filter complexity exceeds maximum allowed depth") |
| ErrFilterFormatMismatch = errors.New("filter is invalid: format mismatch") |
| ErrOperationNotSupported = errors.New("filter is invalid: operation not supported") |
| ) |
|
|
| var ( |
| _ Filter = (*FilterString)(nil) |
| _ Filter = (*FilterInteger)(nil) |
| _ Filter = (*FilterFloat)(nil) |
| _ Filter = (*FilterTime)(nil) |
| _ Filter = (*FilterTimeUnix)(nil) |
| _ Filter = (*FilterBoolean)(nil) |
| _ Filter = (*FilterULID)(nil) |
| ) |
|
|
| |
| func EscapeLikePattern(value string) string { |
| likeEscapeCharacter := `\` |
| escaped := strings.ReplaceAll(value, likeEscapeCharacter, likeEscapeCharacter+likeEscapeCharacter) |
| escaped = strings.ReplaceAll(escaped, "%", likeEscapeCharacter+"%") |
| escaped = strings.ReplaceAll(escaped, "_", likeEscapeCharacter+"_") |
|
|
| return escaped |
| } |
|
|
| |
| func ContainsPattern(value string) string { |
| return fmt.Sprintf("%%%s%%", EscapeLikePattern(value)) |
| } |
|
|
| |
| |
| func ReverseContainsPattern(like *string) *string { |
| if like == nil { |
| return nil |
| } |
| v := *like |
| v = strings.TrimPrefix(v, "%") |
| v = strings.TrimSuffix(v, "%") |
| v = strings.ReplaceAll(v, `\_`, "_") |
| v = strings.ReplaceAll(v, `\%`, "%") |
| v = strings.ReplaceAll(v, `\\`, `\`) |
| return &v |
| } |
|
|
| |
| type FilterString struct { |
| Eq *string `json:"$eq,omitempty"` |
| Ne *string `json:"$ne,omitempty"` |
| Exists *bool `json:"$exists,omitempty"` |
| In *[]string `json:"$in,omitempty"` |
| Nin *[]string `json:"$nin,omitempty"` |
| Like *string `json:"$like,omitempty"` |
| Nlike *string `json:"$nlike,omitempty"` |
| Ilike *string `json:"$ilike,omitempty"` |
| Nilike *string `json:"$nilike,omitempty"` |
| Contains *string `json:"$contains,omitempty"` |
| Ncontains *string `json:"$ncontains,omitempty"` |
| Gt *string `json:"$gt,omitempty"` |
| Gte *string `json:"$gte,omitempty"` |
| Lt *string `json:"$lt,omitempty"` |
| Lte *string `json:"$lte,omitempty"` |
| And *[]FilterString `json:"$and,omitempty"` |
| Or *[]FilterString `json:"$or,omitempty"` |
| } |
|
|
| |
| func (f FilterString) Validate() error { |
| return models.NewNillableGenericValidationError(f.validateWithComplexity(math.MaxInt)) |
| } |
|
|
| |
| func (f FilterString) ValidateWithComplexity(maxDepth int) error { |
| return models.NewNillableGenericValidationError(f.validateWithComplexity(maxDepth)) |
| } |
|
|
| |
| |
| func (f FilterString) validateWithComplexity(maxDepth int) error { |
| if err := validateSingleOperator(f); err != nil { |
| return err |
| } |
|
|
| if slices.Contains(collectStringValues(f), "") { |
| return ErrFilterFormatMismatch |
| } |
|
|
| if f.And == nil && f.Or == nil { |
| return nil |
| } |
|
|
| if maxDepth <= 0 { |
| return ErrFilterComplexityExceeded |
| } |
|
|
| for _, child := range lo.FromPtr(f.And) { |
| if err := child.validateWithComplexity(maxDepth - 1); err != nil { |
| return err |
| } |
| } |
| for _, child := range lo.FromPtr(f.Or) { |
| if err := child.validateWithComplexity(maxDepth - 1); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
|
|
| |
| func (f FilterString) IsEmpty() bool { |
| return isEmptyFilter(f) |
| } |
|
|
| |
| func (f FilterString) SelectWhereExpr(field string, q *sqlbuilder.SelectBuilder) string { |
| switch { |
| case f.Eq != nil: |
| return q.EQ(field, *f.Eq) |
| case f.Ne != nil: |
| return q.NE(field, *f.Ne) |
| case f.Exists != nil: |
| if *f.Exists { |
| return q.IsNotNull(field) |
| } |
| return q.IsNull(field) |
| case f.In != nil: |
| return q.In(field, *f.In) |
| case f.Nin != nil: |
| return q.NotIn(field, *f.Nin) |
| case f.Like != nil: |
| return q.Like(field, *f.Like) |
| case f.Nlike != nil: |
| return q.NotLike(field, *f.Nlike) |
| case f.Ilike != nil: |
| return q.ILike(field, *f.Ilike) |
| case f.Nilike != nil: |
| return q.NotILike(field, *f.Nilike) |
| case f.Contains != nil: |
| return q.ILike(field, ContainsPattern(*f.Contains)) |
| case f.Ncontains != nil: |
| return q.NotILike(field, ContainsPattern(*f.Ncontains)) |
| case f.Gt != nil: |
| return q.GT(field, *f.Gt) |
| case f.Gte != nil: |
| return q.GTE(field, *f.Gte) |
| case f.Lt != nil: |
| return q.LT(field, *f.Lt) |
| case f.Lte != nil: |
| return q.LTE(field, *f.Lte) |
| case f.And != nil: |
| return q.And(lo.Map(*f.And, func(filter FilterString, _ int) string { |
| return filter.SelectWhereExpr(field, q) |
| })...) |
| case f.Or != nil: |
| return q.Or(lo.Map(*f.Or, func(filter FilterString, _ int) string { |
| return filter.SelectWhereExpr(field, q) |
| })...) |
| default: |
| return "" |
| } |
| } |
|
|
| |
| func (f FilterString) Select(field string) func(*sql.Selector) { |
| if f.IsEmpty() { |
| return nil |
| } |
|
|
| switch { |
| case f.Eq != nil: |
| return sql.FieldEQ(field, *f.Eq) |
| case f.Ne != nil: |
| return sql.FieldNEQ(field, *f.Ne) |
| case f.Exists != nil: |
| if *f.Exists { |
| return sql.FieldNotNull(field) |
| } |
| return sql.FieldIsNull(field) |
| case f.In != nil: |
| return sql.FieldIn(field, (*f.In)...) |
| case f.Nin != nil: |
| return sql.FieldNotIn(field, (*f.Nin)...) |
| case f.Like != nil: |
| return func(s *sql.Selector) { |
| s.Where(sql.Like(s.C(field), *f.Like)) |
| } |
| case f.Nlike != nil: |
| return func(s *sql.Selector) { |
| s.Where(sql.P(func(b *sql.Builder) { |
| b.Ident(s.C(field)).WriteString(" NOT LIKE ").Arg(*f.Nlike) |
| })) |
| } |
| case f.Ilike != nil: |
| return func(s *sql.Selector) { |
| s.Where(sql.P(func(b *sql.Builder) { |
| b.Ident(s.C(field)).WriteString(" ILIKE ").Arg(*f.Ilike) |
| })) |
| } |
| case f.Nilike != nil: |
| return func(s *sql.Selector) { |
| s.Where(sql.P(func(b *sql.Builder) { |
| b.Ident(s.C(field)).WriteString(" NOT ILIKE ").Arg(*f.Nilike) |
| })) |
| } |
| case f.Contains != nil: |
| return sql.FieldContainsFold(field, *f.Contains) |
| case f.Ncontains != nil: |
| pattern := ContainsPattern(*f.Ncontains) |
| return func(s *sql.Selector) { |
| s.Where(sql.P(func(b *sql.Builder) { |
| b.Ident(s.C(field)).WriteString(" NOT ILIKE ").Arg(pattern) |
| })) |
| } |
| case f.Gt != nil: |
| return sql.FieldGT(field, *f.Gt) |
| case f.Gte != nil: |
| return sql.FieldGTE(field, *f.Gte) |
| case f.Lt != nil: |
| return sql.FieldLT(field, *f.Lt) |
| case f.Lte != nil: |
| return sql.FieldLTE(field, *f.Lte) |
| case f.And != nil: |
| return sql.AndPredicates(lo.FilterMap(*f.And, func(filter FilterString, _ int) (func(*sql.Selector), bool) { |
| predicate := filter.Select(field) |
| return predicate, predicate != nil |
| })...) |
| case f.Or != nil: |
| return sql.OrPredicates(lo.FilterMap(*f.Or, func(filter FilterString, _ int) (func(*sql.Selector), bool) { |
| predicate := filter.Select(field) |
| return predicate, predicate != nil |
| })...) |
| default: |
| return nil |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| func (f *FilterString) Match(value string) (bool, error) { |
| if f == nil || f.IsEmpty() { |
| return true, nil |
| } |
| return f.matches(value) |
| } |
|
|
| |
| |
| func (f *FilterString) LoFilterPredicate() func(value string, _ int) (bool, error) { |
| return func(value string, _ int) (bool, error) { return f.Match(value) } |
| } |
|
|
| func (f FilterString) matches(value string) (bool, error) { |
| switch { |
| case f.Eq != nil: |
| return value == *f.Eq, nil |
| case f.Ne != nil: |
| return value != *f.Ne, nil |
| case f.Exists != nil: |
| return *f.Exists == (value != ""), nil |
| case f.In != nil: |
| return slices.Contains(*f.In, value), nil |
| case f.Nin != nil: |
| return !slices.Contains(*f.Nin, value), nil |
| case f.Contains != nil: |
| return strings.Contains(strings.ToLower(value), strings.ToLower(*f.Contains)), nil |
| case f.Ncontains != nil: |
| return !strings.Contains(strings.ToLower(value), strings.ToLower(*f.Ncontains)), nil |
| case f.Like != nil: |
| return false, ErrOperationNotSupported |
| case f.Nlike != nil: |
| return false, ErrOperationNotSupported |
| case f.Ilike != nil: |
| return false, ErrOperationNotSupported |
| case f.Nilike != nil: |
| return false, ErrOperationNotSupported |
| case f.Gt != nil: |
| return value > *f.Gt, nil |
| case f.Gte != nil: |
| return value >= *f.Gte, nil |
| case f.Lt != nil: |
| return value < *f.Lt, nil |
| case f.Lte != nil: |
| return value <= *f.Lte, nil |
| case f.And != nil: |
| for _, child := range *f.And { |
| if match, err := child.matches(value); err != nil { |
| return false, err |
| } else if !match { |
| return false, nil |
| } |
| } |
| return true, nil |
| case f.Or != nil: |
| var orErr error |
| for _, child := range *f.Or { |
| if match, err := child.matches(value); err != nil { |
| orErr = err |
| } else if match { |
| return true, nil |
| } |
| } |
| return false, orErr |
| default: |
| return true, nil |
| } |
| } |
|
|
| |
| type FilterInteger struct { |
| Eq *int `json:"$eq,omitempty"` |
| Ne *int `json:"$ne,omitempty"` |
| Gt *int `json:"$gt,omitempty"` |
| Gte *int `json:"$gte,omitempty"` |
| Lt *int `json:"$lt,omitempty"` |
| Lte *int `json:"$lte,omitempty"` |
| And *[]FilterInteger `json:"$and,omitempty"` |
| Or *[]FilterInteger `json:"$or,omitempty"` |
| } |
|
|
| |
| func (f FilterInteger) Validate() error { |
| return models.NewNillableGenericValidationError(f.validateWithComplexity(math.MaxInt)) |
| } |
|
|
| |
| func (f FilterInteger) ValidateWithComplexity(maxDepth int) error { |
| return models.NewNillableGenericValidationError(f.validateWithComplexity(maxDepth)) |
| } |
|
|
| |
| |
| func (f FilterInteger) validateWithComplexity(maxDepth int) error { |
| if err := validateSingleOperator(f); err != nil { |
| return err |
| } |
|
|
| if f.And == nil && f.Or == nil { |
| return nil |
| } |
|
|
| if maxDepth <= 0 { |
| return ErrFilterComplexityExceeded |
| } |
|
|
| for _, child := range lo.FromPtr(f.And) { |
| if err := child.validateWithComplexity(maxDepth - 1); err != nil { |
| return err |
| } |
| } |
| for _, child := range lo.FromPtr(f.Or) { |
| if err := child.validateWithComplexity(maxDepth - 1); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
|
|
| |
| func (f FilterInteger) IsEmpty() bool { |
| return isEmptyFilter(f) |
| } |
|
|
| |
| func (f FilterInteger) SelectWhereExpr(field string, q *sqlbuilder.SelectBuilder) string { |
| switch { |
| case f.Eq != nil: |
| return q.EQ(field, *f.Eq) |
| case f.Ne != nil: |
| return q.NE(field, *f.Ne) |
| case f.Gt != nil: |
| return q.GT(field, *f.Gt) |
| case f.Gte != nil: |
| return q.GTE(field, *f.Gte) |
| case f.Lt != nil: |
| return q.LT(field, *f.Lt) |
| case f.Lte != nil: |
| return q.LTE(field, *f.Lte) |
| case f.And != nil: |
| return q.And(lo.Map(*f.And, func(filter FilterInteger, _ int) string { |
| return filter.SelectWhereExpr(field, q) |
| })...) |
| case f.Or != nil: |
| return q.Or(lo.Map(*f.Or, func(filter FilterInteger, _ int) string { |
| return filter.SelectWhereExpr(field, q) |
| })...) |
| default: |
| return "" |
| } |
| } |
|
|
| |
| func (f FilterInteger) Select(field string) func(*sql.Selector) { |
| if f.IsEmpty() { |
| return nil |
| } |
|
|
| switch { |
| case f.Eq != nil: |
| return sql.FieldEQ(field, *f.Eq) |
| case f.Ne != nil: |
| return sql.FieldNEQ(field, *f.Ne) |
| case f.Gt != nil: |
| return sql.FieldGT(field, *f.Gt) |
| case f.Gte != nil: |
| return sql.FieldGTE(field, *f.Gte) |
| case f.Lt != nil: |
| return sql.FieldLT(field, *f.Lt) |
| case f.Lte != nil: |
| return sql.FieldLTE(field, *f.Lte) |
| case f.And != nil: |
| return sql.AndPredicates(lo.FilterMap(*f.And, func(filter FilterInteger, _ int) (func(*sql.Selector), bool) { |
| predicate := filter.Select(field) |
| return predicate, predicate != nil |
| })...) |
| case f.Or != nil: |
| return sql.OrPredicates(lo.FilterMap(*f.Or, func(filter FilterInteger, _ int) (func(*sql.Selector), bool) { |
| predicate := filter.Select(field) |
| return predicate, predicate != nil |
| })...) |
| default: |
| return nil |
| } |
| } |
|
|
| |
| type FilterFloat struct { |
| Eq *float64 `json:"$eq,omitempty"` |
| Ne *float64 `json:"$ne,omitempty"` |
| Gt *float64 `json:"$gt,omitempty"` |
| Gte *float64 `json:"$gte,omitempty"` |
| Lt *float64 `json:"$lt,omitempty"` |
| Lte *float64 `json:"$lte,omitempty"` |
| And *[]FilterFloat `json:"$and,omitempty"` |
| Or *[]FilterFloat `json:"$or,omitempty"` |
| } |
|
|
| func (f FilterFloat) Validate() error { |
| return models.NewNillableGenericValidationError(f.validateWithComplexity(math.MaxInt)) |
| } |
|
|
| |
| func (f FilterFloat) ValidateWithComplexity(maxDepth int) error { |
| return models.NewNillableGenericValidationError(f.validateWithComplexity(maxDepth)) |
| } |
|
|
| |
| |
| func (f FilterFloat) validateWithComplexity(maxDepth int) error { |
| if err := validateSingleOperator(f); err != nil { |
| return err |
| } |
|
|
| if f.And == nil && f.Or == nil { |
| return nil |
| } |
|
|
| if maxDepth <= 0 { |
| return ErrFilterComplexityExceeded |
| } |
|
|
| for _, child := range lo.FromPtr(f.And) { |
| if err := child.validateWithComplexity(maxDepth - 1); err != nil { |
| return err |
| } |
| } |
| for _, child := range lo.FromPtr(f.Or) { |
| if err := child.validateWithComplexity(maxDepth - 1); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
|
|
| |
| func (f FilterFloat) IsEmpty() bool { |
| return isEmptyFilter(f) |
| } |
|
|
| |
| func (f FilterFloat) SelectWhereExpr(field string, q *sqlbuilder.SelectBuilder) string { |
| switch { |
| case f.Eq != nil: |
| return q.EQ(field, *f.Eq) |
| case f.Ne != nil: |
| return q.NE(field, *f.Ne) |
| case f.Gt != nil: |
| return q.GT(field, *f.Gt) |
| case f.Gte != nil: |
| return q.GTE(field, *f.Gte) |
| case f.Lt != nil: |
| return q.LT(field, *f.Lt) |
| case f.Lte != nil: |
| return q.LTE(field, *f.Lte) |
| case f.And != nil: |
| return q.And(lo.Map(*f.And, func(filter FilterFloat, _ int) string { |
| return filter.SelectWhereExpr(field, q) |
| })...) |
| case f.Or != nil: |
| return q.Or(lo.Map(*f.Or, func(filter FilterFloat, _ int) string { |
| return filter.SelectWhereExpr(field, q) |
| })...) |
| default: |
| return "" |
| } |
| } |
|
|
| |
| func (f FilterFloat) Select(field string) func(*sql.Selector) { |
| if f.IsEmpty() { |
| return nil |
| } |
|
|
| switch { |
| case f.Eq != nil: |
| return sql.FieldEQ(field, *f.Eq) |
| case f.Ne != nil: |
| return sql.FieldNEQ(field, *f.Ne) |
| case f.Gt != nil: |
| return sql.FieldGT(field, *f.Gt) |
| case f.Gte != nil: |
| return sql.FieldGTE(field, *f.Gte) |
| case f.Lt != nil: |
| return sql.FieldLT(field, *f.Lt) |
| case f.Lte != nil: |
| return sql.FieldLTE(field, *f.Lte) |
| case f.And != nil: |
| return sql.AndPredicates(lo.FilterMap(*f.And, func(filter FilterFloat, _ int) (func(*sql.Selector), bool) { |
| predicate := filter.Select(field) |
| return predicate, predicate != nil |
| })...) |
| case f.Or != nil: |
| return sql.OrPredicates(lo.FilterMap(*f.Or, func(filter FilterFloat, _ int) (func(*sql.Selector), bool) { |
| predicate := filter.Select(field) |
| return predicate, predicate != nil |
| })...) |
| default: |
| return nil |
| } |
| } |
|
|
| |
| type FilterTime struct { |
| Eq *time.Time `json:"$eq,omitempty"` |
| Exists *bool `json:"$exists,omitempty"` |
| Gt *time.Time `json:"$gt,omitempty"` |
| Gte *time.Time `json:"$gte,omitempty"` |
| Lt *time.Time `json:"$lt,omitempty"` |
| Lte *time.Time `json:"$lte,omitempty"` |
| And *[]FilterTime `json:"$and,omitempty"` |
| Or *[]FilterTime `json:"$or,omitempty"` |
| } |
|
|
| |
| |
| |
| func NewFilterTime(after, before *time.Time) *FilterTime { |
| if after == nil && before == nil { |
| return nil |
| } |
| if after != nil && before != nil { |
| return &FilterTime{And: &[]FilterTime{{Gte: after}, {Lte: before}}} |
| } |
| if after != nil { |
| return &FilterTime{Gte: after} |
| } |
| return &FilterTime{Lte: before} |
| } |
|
|
| |
| func (f FilterTime) Validate() error { |
| return models.NewNillableGenericValidationError(f.validateWithComplexity(math.MaxInt)) |
| } |
|
|
| |
| func (f FilterTime) ValidateWithComplexity(maxDepth int) error { |
| return models.NewNillableGenericValidationError(f.validateWithComplexity(maxDepth)) |
| } |
|
|
| |
| |
| func (f FilterTime) validateWithComplexity(maxDepth int) error { |
| if err := validateSingleOperator(f); err != nil { |
| return err |
| } |
|
|
| if f.And == nil && f.Or == nil { |
| return nil |
| } |
|
|
| if maxDepth <= 0 { |
| return ErrFilterComplexityExceeded |
| } |
|
|
| for _, child := range lo.FromPtr(f.And) { |
| if err := child.validateWithComplexity(maxDepth - 1); err != nil { |
| return err |
| } |
| } |
| for _, child := range lo.FromPtr(f.Or) { |
| if err := child.validateWithComplexity(maxDepth - 1); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
|
|
| |
| func (f FilterTime) IsEmpty() bool { |
| return isEmptyFilter(f) |
| } |
|
|
| |
| func (f FilterTime) SelectWhereExpr(field string, q *sqlbuilder.SelectBuilder) string { |
| switch { |
| case f.Eq != nil: |
| return q.EQ(field, *f.Eq) |
| case f.Exists != nil: |
| if *f.Exists { |
| return q.IsNotNull(field) |
| } |
| return q.IsNull(field) |
| case f.Gt != nil: |
| return q.GT(field, *f.Gt) |
| case f.Gte != nil: |
| return q.GTE(field, *f.Gte) |
| case f.Lt != nil: |
| return q.LT(field, *f.Lt) |
| case f.Lte != nil: |
| return q.LTE(field, *f.Lte) |
| case f.And != nil: |
| return q.And(lo.Map(*f.And, func(filter FilterTime, _ int) string { |
| return filter.SelectWhereExpr(field, q) |
| })...) |
| case f.Or != nil: |
| return q.Or(lo.Map(*f.Or, func(filter FilterTime, _ int) string { |
| return filter.SelectWhereExpr(field, q) |
| })...) |
| default: |
| return "" |
| } |
| } |
|
|
| |
| func (f FilterTime) Select(field string) func(*sql.Selector) { |
| if f.IsEmpty() { |
| return nil |
| } |
|
|
| switch { |
| case f.Eq != nil: |
| return sql.FieldEQ(field, *f.Eq) |
| case f.Exists != nil: |
| if *f.Exists { |
| return sql.FieldNotNull(field) |
| } |
| return sql.FieldIsNull(field) |
| case f.Gt != nil: |
| return sql.FieldGT(field, *f.Gt) |
| case f.Gte != nil: |
| return sql.FieldGTE(field, *f.Gte) |
| case f.Lt != nil: |
| return sql.FieldLT(field, *f.Lt) |
| case f.Lte != nil: |
| return sql.FieldLTE(field, *f.Lte) |
| case f.And != nil: |
| return sql.AndPredicates(lo.FilterMap(*f.And, func(filter FilterTime, _ int) (func(*sql.Selector), bool) { |
| predicate := filter.Select(field) |
| return predicate, predicate != nil |
| })...) |
| case f.Or != nil: |
| return sql.OrPredicates(lo.FilterMap(*f.Or, func(filter FilterTime, _ int) (func(*sql.Selector), bool) { |
| predicate := filter.Select(field) |
| return predicate, predicate != nil |
| })...) |
| default: |
| return nil |
| } |
| } |
|
|
| |
| |
| type FilterTimeUnix struct { |
| FilterTime |
| } |
|
|
| |
| func (f FilterTimeUnix) SelectWhereExpr(field string, q *sqlbuilder.SelectBuilder) string { |
| switch { |
| case f.Eq != nil: |
| return q.EQ(field, f.Eq.Unix()) |
| case f.Exists != nil: |
| if *f.Exists { |
| return q.IsNotNull(field) |
| } |
| return q.IsNull(field) |
| case f.Gt != nil: |
| return q.GT(field, f.Gt.Unix()) |
| case f.Gte != nil: |
| return q.GTE(field, f.Gte.Unix()) |
| case f.Lt != nil: |
| return q.LT(field, f.Lt.Unix()) |
| case f.Lte != nil: |
| return q.LTE(field, f.Lte.Unix()) |
| case f.And != nil: |
| return q.And(lo.Map(*f.And, func(filter FilterTime, _ int) string { |
| return FilterTimeUnix{FilterTime: filter}.SelectWhereExpr(field, q) |
| })...) |
| case f.Or != nil: |
| return q.Or(lo.Map(*f.Or, func(filter FilterTime, _ int) string { |
| return FilterTimeUnix{FilterTime: filter}.SelectWhereExpr(field, q) |
| })...) |
| default: |
| return "" |
| } |
| } |
|
|
| |
| func (f FilterTimeUnix) Select(field string) func(*sql.Selector) { |
| if f.IsEmpty() { |
| return nil |
| } |
|
|
| switch { |
| case f.Eq != nil: |
| return sql.FieldEQ(field, f.Eq.Unix()) |
| case f.Exists != nil: |
| if *f.Exists { |
| return sql.FieldNotNull(field) |
| } |
| return sql.FieldIsNull(field) |
| case f.Gt != nil: |
| return sql.FieldGT(field, f.Gt.Unix()) |
| case f.Gte != nil: |
| return sql.FieldGTE(field, f.Gte.Unix()) |
| case f.Lt != nil: |
| return sql.FieldLT(field, f.Lt.Unix()) |
| case f.Lte != nil: |
| return sql.FieldLTE(field, f.Lte.Unix()) |
| case f.And != nil: |
| return sql.AndPredicates(lo.FilterMap(*f.And, func(filter FilterTime, _ int) (func(*sql.Selector), bool) { |
| predicate := (FilterTimeUnix{FilterTime: filter}).Select(field) |
| return predicate, predicate != nil |
| })...) |
| case f.Or != nil: |
| return sql.OrPredicates(lo.FilterMap(*f.Or, func(filter FilterTime, _ int) (func(*sql.Selector), bool) { |
| predicate := (FilterTimeUnix{FilterTime: filter}).Select(field) |
| return predicate, predicate != nil |
| })...) |
| default: |
| return nil |
| } |
| } |
|
|
| |
| type FilterBoolean struct { |
| Eq *bool `json:"$eq,omitempty"` |
| } |
|
|
| |
| func (f FilterBoolean) Validate() error { |
| return nil |
| } |
|
|
| |
| func (f FilterBoolean) ValidateWithComplexity(maxDepth int) error { |
| |
| return f.Validate() |
| } |
|
|
| |
| func (f FilterBoolean) IsEmpty() bool { |
| return isEmptyFilter(f) |
| } |
|
|
| |
| func (f FilterBoolean) SelectWhereExpr(field string, q *sqlbuilder.SelectBuilder) string { |
| switch { |
| case f.Eq != nil: |
| return q.EQ(field, *f.Eq) |
| default: |
| return "" |
| } |
| } |
|
|
| |
| func (f FilterBoolean) Select(field string) func(*sql.Selector) { |
| if f.IsEmpty() { |
| return nil |
| } |
|
|
| switch { |
| case f.Eq != nil: |
| return sql.FieldEQ(field, *f.Eq) |
| default: |
| return nil |
| } |
| } |
|
|
| |
| func SelectPredicate[P ~func(*sql.Selector)](f Filter, field string) *P { |
| if f == nil || f.IsEmpty() { |
| return nil |
| } |
|
|
| if s := f.Select(field); s != nil { |
| p := P(s) |
| return &p |
| } |
|
|
| return nil |
| } |
|
|
| |
| |
| type Predicate interface { |
| ~func(*sql.Selector) |
| } |
|
|
| |
| type EntQuery[Q any, P Predicate] interface { |
| Where(ps ...P) Q |
| } |
|
|
| |
| func ApplyToQuery[F Filter, Q EntQuery[Q, P], P Predicate](q Q, f *F, field string) Q { |
| if f == nil { |
| return q |
| } |
|
|
| if p := SelectPredicate[P](Filter(*f), field); p != nil { |
| return q.Where(*p) |
| } |
|
|
| return q |
| } |
|
|
| |
| |
| func validateSingleOperator(v Filter) error { |
| rv := reflect.ValueOf(v) |
| if rv.Kind() == reflect.Pointer { |
| rv = rv.Elem() |
| } |
|
|
| count := 0 |
| for i := 0; i < rv.NumField(); i++ { |
| f := rv.Field(i) |
| if f.Kind() == reflect.Pointer && !f.IsNil() { |
| count++ |
| } |
| } |
|
|
| if count > 1 { |
| return ErrFilterMultipleOperators |
| } |
|
|
| return nil |
| } |
|
|
| |
| func isEmptyFilter(v Filter) bool { |
| rv := reflect.ValueOf(v) |
| if rv.Kind() == reflect.Pointer { |
| rv = rv.Elem() |
| } |
|
|
| for i := 0; i < rv.NumField(); i++ { |
| f := rv.Field(i) |
| if f.Kind() == reflect.Pointer && !f.IsNil() { |
| return false |
| } |
| } |
|
|
| return true |
| } |
|
|
| |
| type FilterULID struct { |
| FilterString |
| And *[]FilterULID `json:"$and,omitempty"` |
| Or *[]FilterULID `json:"$or,omitempty"` |
| } |
|
|
| |
| func (f FilterULID) Validate() error { |
| return models.NewNillableGenericValidationError(f.validateWithComplexity(math.MaxInt)) |
| } |
|
|
| |
| func (f FilterULID) ValidateWithComplexity(maxDepth int) error { |
| return models.NewNillableGenericValidationError(f.validateWithComplexity(maxDepth)) |
| } |
|
|
| func (f FilterULID) validateWithComplexity(maxDepth int) error { |
| if err := validateSingleOperator(f.FilterString); err != nil { |
| return err |
| } |
|
|
| for _, value := range collectStringValues(f.FilterString) { |
| _, err := ulid.ParseStrict(value) |
| if err != nil { |
| return ErrFilterFormatMismatch |
| } |
| } |
|
|
| if f.And == nil && f.Or == nil { |
| return nil |
| } |
|
|
| if maxDepth <= 0 { |
| return ErrFilterComplexityExceeded |
| } |
|
|
| for _, child := range lo.FromPtr(f.And) { |
| if err := child.validateWithComplexity(maxDepth - 1); err != nil { |
| return err |
| } |
| } |
| for _, child := range lo.FromPtr(f.Or) { |
| if err := child.validateWithComplexity(maxDepth - 1); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
|
|
| |
| func (f FilterULID) IsEmpty() bool { |
| return f.FilterString.IsEmpty() && f.And == nil && f.Or == nil |
| } |
|
|
| |
| func (f FilterULID) SelectWhereExpr(field string, q *sqlbuilder.SelectBuilder) string { |
| switch { |
| case f.And != nil: |
| return q.And(lo.Map(*f.And, func(child FilterULID, _ int) string { |
| return child.SelectWhereExpr(field, q) |
| })...) |
| case f.Or != nil: |
| return q.Or(lo.Map(*f.Or, func(child FilterULID, _ int) string { |
| return child.SelectWhereExpr(field, q) |
| })...) |
| default: |
| return f.FilterString.SelectWhereExpr(field, q) |
| } |
| } |
|
|
| |
| func (f FilterULID) Select(field string) func(*sql.Selector) { |
| switch { |
| case f.And != nil: |
| return sql.AndPredicates(lo.FilterMap(*f.And, func(child FilterULID, _ int) (func(*sql.Selector), bool) { |
| p := child.Select(field) |
| return p, p != nil |
| })...) |
| case f.Or != nil: |
| return sql.OrPredicates(lo.FilterMap(*f.Or, func(child FilterULID, _ int) (func(*sql.Selector), bool) { |
| p := child.Select(field) |
| return p, p != nil |
| })...) |
| default: |
| return f.FilterString.Select(field) |
| } |
| } |
|
|
| |
| |
| func collectStringValues(f Filter) []string { |
| refFilter := reflect.ValueOf(f) |
| if refFilter.Kind() == reflect.Pointer { |
| refFilter = refFilter.Elem() |
| } |
|
|
| var values []string |
| for i := 0; i < refFilter.NumField(); i++ { |
| filterField := refFilter.Field(i) |
| if filterField.Kind() == reflect.Pointer && !filterField.IsNil() { |
| filterFieldElem := filterField.Elem() |
| switch filterFieldElem.Kind() { |
| case reflect.String: |
| values = append(values, filterFieldElem.String()) |
| case reflect.Slice, reflect.Array: |
| for i := range filterFieldElem.Len() { |
| arrayElem := filterFieldElem.Index(i) |
| if arrayElem.Kind() == reflect.String { |
| values = append(values, arrayElem.String()) |
| } |
| } |
| } |
| } |
| } |
| return values |
| } |
|
|