package builderimp import ( "reflect" "github.com/tech/sendico/pkg/db/repository/builder" "github.com/tech/sendico/pkg/db/storable" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo/options" ) type QueryImp struct { filter bson.D sort bson.D limit *int64 offset *int64 } func (b *QueryImp) Filter(field builder.Field, value any) builder.Query { b.filter = append(b.filter, bson.E{Key: field.Build(), Value: value}) return b } func (b *QueryImp) And(filters ...builder.Query) builder.Query { andFilters := bson.A{} for _, f := range filters { andFilters = append(andFilters, f.BuildQuery()) } b.filter = append(b.filter, bson.E{Key: string(builder.And), Value: andFilters}) return b } func (b *QueryImp) Or(filters ...builder.Query) builder.Query { orFilters := bson.A{} for _, f := range filters { orFilters = append(orFilters, f.BuildQuery()) } b.filter = append(b.filter, bson.E{Key: string(builder.Or), Value: orFilters}) return b } func (b *QueryImp) Comparison(field builder.Field, operator builder.MongoOperation, value any) builder.Query { b.filter = append(b.filter, bson.E{Key: field.Build(), Value: bson.M{string(operator): value}}) return b } func (b *QueryImp) Expression(value builder.Expression) builder.Query { b.filter = append(b.filter, bson.E{Key: string(builder.Expr), Value: value.Build()}) return b } func (b *QueryImp) RegEx(field builder.Field, pattern, options string) builder.Query { b.filter = append(b.filter, bson.E{Key: field.Build(), Value: primitive.Regex{Pattern: pattern, Options: options}}) return b } func (b *QueryImp) opIn(field builder.Field, op builder.MongoOperation, values ...any) builder.Query { var flattenedValues []any for _, v := range values { switch reflect.TypeOf(v).Kind() { case reflect.Slice: slice := reflect.ValueOf(v) for i := range slice.Len() { flattenedValues = append(flattenedValues, slice.Index(i).Interface()) } default: flattenedValues = append(flattenedValues, v) } } b.filter = append(b.filter, bson.E{Key: field.Build(), Value: bson.M{string(op): flattenedValues}}) return b } func (b *QueryImp) NotIn(field builder.Field, values ...any) builder.Query { return b.opIn(field, builder.NotIn, values...) } func (b *QueryImp) In(field builder.Field, values ...any) builder.Query { return b.opIn(field, builder.In, values...) } func (b *QueryImp) Archived(isArchived *bool) builder.Query { if isArchived == nil { return b } return b.And(NewQueryImp().Filter(NewFieldImp(storable.IsArchivedField), *isArchived)) } func (b *QueryImp) Sort(field builder.Field, ascending bool) builder.Query { order := 1 if !ascending { order = -1 } b.sort = append(b.sort, bson.E{Key: field.Build(), Value: order}) return b } func (b *QueryImp) BuildPipeline() bson.D { query := bson.D{} if len(b.filter) > 0 { query = append(query, bson.E{Key: string(builder.Match), Value: b.filter}) } if len(b.sort) > 0 { query = append(query, bson.E{Key: string(builder.Sort), Value: b.sort}) } if b.limit != nil { query = append(query, bson.E{Key: string(builder.Limit), Value: *b.limit}) } if b.offset != nil { query = append(query, bson.E{Key: string(builder.Skip), Value: *b.offset}) } return query } func (b *QueryImp) BuildQuery() bson.D { return b.filter } func (b *QueryImp) Limit(limit *int64) builder.Query { b.limit = limit return b } func (b *QueryImp) Offset(offset *int64) builder.Query { b.offset = offset return b } func (b *QueryImp) BuildOptions() *options.FindOptions { opts := options.Find() if b.limit != nil { opts.SetLimit(*b.limit) } if b.offset != nil { opts.SetSkip(*b.offset) } if len(b.sort) > 0 { opts.SetSort(b.sort) } return opts } func NewQueryImp() builder.Query { return &QueryImp{ filter: bson.D{}, sort: bson.D{}, } }