2025-06-27 13:01:39 +07:00

194 lines
4.7 KiB
Go

package repository
import (
"github.com/pkg/errors"
"gorm.io/gorm"
)
type QueryBuilder[T any] struct {
db *gorm.DB
model T
}
func NewQueryBuilder[T any](db *gorm.DB) *QueryBuilder[T] {
var model T
return &QueryBuilder[T]{
db: db,
model: model,
}
}
type Filter struct {
Field string
Operator string // "=", "!=", ">", "<", ">=", "<=", "LIKE", "IN", "NOT IN", "IS NULL", "IS NOT NULL"
Value interface{}
}
type QueryOptions struct {
Filters []Filter
Limit int
Offset int
OrderBy []string
Preloads []string
GroupBy []string
Having []Filter
Distinct []string
CountOnly bool
}
func (qb *QueryBuilder[T]) BuildQuery(options QueryOptions) *gorm.DB {
query := qb.db.Model(&qb.model)
for _, filter := range options.Filters {
query = qb.applyFilter(query, filter)
}
if len(options.Distinct) > 0 {
for _, distinct := range options.Distinct {
query = query.Distinct(distinct)
}
}
if len(options.GroupBy) > 0 {
for _, groupBy := range options.GroupBy {
query = query.Group(groupBy)
}
}
for _, having := range options.Having {
query = qb.applyFilter(query, having)
}
return query
}
func (qb *QueryBuilder[T]) applyFilter(query *gorm.DB, filter Filter) *gorm.DB {
switch filter.Operator {
case "=", "":
return query.Where(filter.Field+" = ?", filter.Value)
case "!=":
return query.Where(filter.Field+" != ?", filter.Value)
case ">":
return query.Where(filter.Field+" > ?", filter.Value)
case "<":
return query.Where(filter.Field+" < ?", filter.Value)
case ">=":
return query.Where(filter.Field+" >= ?", filter.Value)
case "<=":
return query.Where(filter.Field+" <= ?", filter.Value)
case "LIKE":
return query.Where(filter.Field+" LIKE ?", filter.Value)
case "IN":
return query.Where(filter.Field+" IN ?", filter.Value)
case "NOT IN":
return query.Where(filter.Field+" NOT IN ?", filter.Value)
case "IS NULL":
return query.Where(filter.Field + " IS NULL")
case "IS NOT NULL":
return query.Where(filter.Field + " IS NOT NULL")
case "BETWEEN":
if values, ok := filter.Value.([]interface{}); ok && len(values) == 2 {
return query.Where(filter.Field+" BETWEEN ? AND ?", values[0], values[1])
}
return query
default:
return query.Where(filter.Field+" = ?", filter.Value)
}
}
func (qb *QueryBuilder[T]) ExecuteQuery(baseQuery *gorm.DB, options QueryOptions) *gorm.DB {
query := baseQuery.Session(&gorm.Session{})
if len(options.OrderBy) > 0 {
for _, orderBy := range options.OrderBy {
query = query.Order(orderBy)
}
}
for _, preload := range options.Preloads {
query = query.Preload(preload)
}
if options.Limit > 0 {
query = query.Limit(options.Limit)
}
if options.Offset > 0 {
query = query.Offset(options.Offset)
}
return query
}
func (qb *QueryBuilder[T]) Count(baseQuery *gorm.DB) (int64, error) {
var count int64
if err := baseQuery.Count(&count).Error; err != nil {
return 0, errors.Wrap(err, "failed to count records")
}
return count, nil
}
func (qb *QueryBuilder[T]) Find(query *gorm.DB) ([]T, error) {
var results []T
if err := query.Find(&results).Error; err != nil {
return nil, errors.Wrap(err, "failed to find records")
}
return results, nil
}
func (qb *QueryBuilder[T]) First(query *gorm.DB) (*T, error) {
var result T
if err := query.First(&result).Error; err != nil {
return nil, errors.Wrap(err, "failed to find record")
}
return &result, nil
}
func Equal(field string, value interface{}) Filter {
return Filter{Field: field, Operator: "=", Value: value}
}
func NotEqual(field string, value interface{}) Filter {
return Filter{Field: field, Operator: "!=", Value: value}
}
func GreaterThan(field string, value interface{}) Filter {
return Filter{Field: field, Operator: ">", Value: value}
}
func LessThan(field string, value interface{}) Filter {
return Filter{Field: field, Operator: "<", Value: value}
}
func GreaterEqual(field string, value interface{}) Filter {
return Filter{Field: field, Operator: ">=", Value: value}
}
func LessEqual(field string, value interface{}) Filter {
return Filter{Field: field, Operator: "<=", Value: value}
}
func Like(field string, value string) Filter {
return Filter{Field: field, Operator: "LIKE", Value: value}
}
func In(field string, values interface{}) Filter {
return Filter{Field: field, Operator: "IN", Value: values}
}
func NotIn(field string, values interface{}) Filter {
return Filter{Field: field, Operator: "NOT IN", Value: values}
}
func IsNull(field string) Filter {
return Filter{Field: field, Operator: "IS NULL"}
}
func IsNotNull(field string) Filter {
return Filter{Field: field, Operator: "IS NOT NULL"}
}
func Between(field string, start, end interface{}) Filter {
return Filter{Field: field, Operator: "BETWEEN", Value: []interface{}{start, end}}
}