Good operators

This commit is contained in:
Eden Kirin
2024-06-19 19:02:40 +02:00
parent 0fb5d637dd
commit 8f4ea5b648
4 changed files with 301 additions and 48 deletions

View File

@ -9,7 +9,6 @@ import (
"repo-pattern/app/repository" "repo-pattern/app/repository"
"repo-pattern/app/repository/smartfilter" "repo-pattern/app/repository/smartfilter"
"github.com/google/uuid"
"gorm.io/gorm" "gorm.io/gorm"
) )
@ -22,11 +21,17 @@ func doMagic(db *gorm.DB) {
var err error var err error
query := db query := db
id, _ := uuid.FromBytes([]byte("6dc096ab-5c03-427e-b808-c669f7446131")) // id, _ := uuid.FromBytes([]byte("6dc096ab-5c03-427e-b808-c669f7446131"))
// serialNumber := "222"
// serialNumberContains := "323"
issuer := "FINA"
f := smartfilter.SmartCertFilter[models.Cert]{ f := smartfilter.SmartCertFilter[models.Cert]{
Alive: &TRUE, Alive: &FALSE,
Id: &id, // Id: &id,
// SerialNumber: &serialNumber,
// SerialNumberContains: &serialNumberContains,
IssuerContains: &issuer,
} }
query, err = f.ToQuery(query) query, err = f.ToQuery(query)

View File

@ -3,45 +3,94 @@ package smartfilter
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"strconv"
"github.com/google/uuid" "github.com/google/uuid"
) )
type valueGetterFunc func(ff *FilterField, v reflect.Value) error
type FilterField struct { type FilterField struct {
Name string Name string
Operator Operator Operator Operator
boolValue bool valueKind reflect.Kind
intValue int64 boolValue *bool
uintValue uint64 intValue *int64
floatValue float64 uintValue *uint64
strValue string floatValue *float64
strValue *string
} }
func (ff *FilterField) getValue(v reflect.Value) string {
fn := typeGetter(v.Type())
fn(ff, v)
switch ff.valueKind {
case reflect.Bool:
if *ff.boolValue {
return "TRUE"
} else {
return "FALSE"
}
case reflect.Int:
return strconv.FormatInt(*ff.intValue, 10)
case reflect.Uint:
return strconv.FormatUint(*ff.uintValue, 10)
case reflect.Float32:
return "some float 32"
case reflect.Float64:
return "some float 64"
case reflect.String:
return *ff.strValue
}
return "???"
}
func (ff *FilterField) getValueWithOperator(v reflect.Value) string {
value := ff.getValue(v)
switch ff.valueKind {
case reflect.Bool:
return fmt.Sprintf("IS %s", value)
case reflect.Int, reflect.Uint, reflect.Float32, reflect.Float64, reflect.String:
return fmt.Sprintf("= %s", value)
}
return "???"
}
type valueGetterFunc func(ff *FilterField, v reflect.Value) error
func boolValueGetter(ff *FilterField, v reflect.Value) error { func boolValueGetter(ff *FilterField, v reflect.Value) error {
ff.boolValue = v.Bool() value := v.Bool()
ff.boolValue = &value
ff.valueKind = reflect.Bool
return nil return nil
} }
func intValueGetter(ff *FilterField, v reflect.Value) error { func intValueGetter(ff *FilterField, v reflect.Value) error {
ff.intValue = v.Int() value := v.Int()
ff.intValue = &value
ff.valueKind = reflect.Int
return nil return nil
} }
func uintValueGetter(ff *FilterField, v reflect.Value) error { func uintValueGetter(ff *FilterField, v reflect.Value) error {
ff.uintValue = v.Uint() value := v.Uint()
ff.uintValue = &value
ff.valueKind = reflect.Uint
return nil return nil
} }
func floatValueGetter(ff *FilterField, v reflect.Value) error { func floatValueGetter(ff *FilterField, v reflect.Value) error {
ff.floatValue = v.Float() value := v.Float()
ff.floatValue = &value
ff.valueKind = reflect.Float64
return nil return nil
} }
func strValueGetter(ff *FilterField, v reflect.Value) error { func strValueGetter(ff *FilterField, v reflect.Value) error {
ff.strValue = v.String() value := v.String()
ff.strValue = &value
ff.valueKind = reflect.String
return nil return nil
} }
@ -50,7 +99,9 @@ func uuidValueGetter(ff *FilterField, v reflect.Value) error {
if err != nil { if err != nil {
return err return err
} }
ff.strValue = uid.String() value := uid.String()
ff.strValue = &value
ff.valueKind = reflect.String
return nil return nil
} }
@ -58,6 +109,10 @@ func unsupportedValueGetter(ff *FilterField, v reflect.Value) error {
return fmt.Errorf("unsupported type: %v", v.Type()) return fmt.Errorf("unsupported type: %v", v.Type())
} }
func typeGetter(t reflect.Type) valueGetterFunc {
return newTypeGetter(t, true)
}
func newTypeGetter(t reflect.Type, allowAddr bool) valueGetterFunc { func newTypeGetter(t reflect.Type, allowAddr bool) valueGetterFunc {
// If we have a non-pointer value whose type implements // If we have a non-pointer value whose type implements
// Marshaler with a value receiver, then we're better off taking // Marshaler with a value receiver, then we're better off taking
@ -97,9 +152,23 @@ func newTypeGetter(t reflect.Type, allowAddr bool) valueGetterFunc {
// return newSliceEncoder(t) // return newSliceEncoder(t)
// case reflect.Array: // case reflect.Array:
// return newArrayEncoder(t) // return newArrayEncoder(t)
// case reflect.Pointer: case reflect.Pointer:
// return newPtrEncoder(t) return newPtrValueGetter(t)
default: default:
return unsupportedValueGetter return unsupportedValueGetter
} }
} }
type ptrValueGetter struct {
elemEnc valueGetterFunc
}
func (pvg ptrValueGetter) getValue(ff *FilterField, v reflect.Value) error {
pvg.elemEnc(ff, v.Elem())
return nil
}
func newPtrValueGetter(t reflect.Type) valueGetterFunc {
enc := ptrValueGetter{elemEnc: typeGetter(t.Elem())}
return enc.getValue
}

View File

@ -0,0 +1,22 @@
package smartfilter
type Operator string
const (
OperatorEQ Operator = "EQ"
OperatorNE Operator = "NE"
OperatorGT Operator = "GT"
OperatorGE Operator = "GE"
OperatorLT Operator = "LT"
OperatorLE Operator = "LE"
OperatorLIKE Operator = "LIKE"
OperatorILIKE Operator = "ILIKE"
OperatorIN Operator = "IN"
)
var OPERATORS = []Operator{
OperatorEQ, OperatorNE,
OperatorGT, OperatorGE, OperatorLT, OperatorLE,
OperatorLIKE, OperatorILIKE,
OperatorIN,
}

View File

@ -14,18 +14,25 @@ import (
const TAG_NAME = "filterfield" const TAG_NAME = "filterfield"
const TAG_VALUE_SEPARATOR = "," const TAG_VALUE_SEPARATOR = ","
type Operator string type handlerFunc func(query *gorm.DB, tableName string, filterField *FilterField) (*gorm.DB, error)
const ( var operatorHandlers = map[Operator]handlerFunc{
OperatorEQ Operator = "EQ" OperatorEQ: handleOperatorEQ,
OperatorIN Operator = "IN" OperatorNE: handleOperatorNE,
) OperatorGT: handleOperatorGT,
OperatorGE: handleOperatorGE,
var OPERATORS = []Operator{OperatorEQ, OperatorIN} OperatorLT: handleOperatorLT,
OperatorLE: handleOperatorLE,
OperatorLIKE: handleOperatorLIKE,
OperatorILIKE: handleOperatorILIKE,
}
type SmartCertFilter[T schema.Tabler] struct { type SmartCertFilter[T schema.Tabler] struct {
Model T Model T
Alive *bool `filterfield:"alive,EQ"` Alive *bool `filterfield:"alive,EQ"`
SerialNumber *string `filterfield:"serial_number,NE"`
SerialNumberContains *string `filterfield:"serial_number,LIKE"`
IssuerContains *string `filterfield:"issuer,ILIKE"`
Id *uuid.UUID `filterfield:"id,EQ"` Id *uuid.UUID `filterfield:"id,EQ"`
Ids *[]uuid.UUID `filterfield:"id,IN"` Ids *[]uuid.UUID `filterfield:"id,IN"`
CompanyId *uuid.UUID `filterfield:"company_id,EQ"` CompanyId *uuid.UUID `filterfield:"company_id,EQ"`
@ -60,36 +67,185 @@ func (f SmartCertFilter[T]) ToQuery(query *gorm.DB) (*gorm.DB, error) {
t := fieldReflect.Type() t := fieldReflect.Type()
fmt.Printf(">>> %+v --- %+v\n", field, t) fmt.Printf(">>> %+v --- %+v\n", field, t)
filterField, err := getFilterField(tagValue) filterField, err := newFilterField(tagValue)
if err != nil { if err != nil {
return nil, fmt.Errorf("%s.%s: %s", modelName, field.Name, err) return nil, fmt.Errorf("%s.%s: %s", modelName, field.Name, err)
} }
// fmt.Printf( strValue := filterField.getValue(fieldReflect)
// "tagValue: %s, Name: %s, Operator: %s\n", fmt.Printf(">>> filterField: %+v ==== %s\n", filterField, strValue)
// tagValue,
// filterField.Name,
// filterField.Operator,
// )
switch filterField.Operator { operatorHandler, ok := operatorHandlers[filterField.Operator]
case OperatorEQ: if !ok {
query = applyFilterEQ[string](query, tableName, filterField) return nil, fmt.Errorf("no handler for operator %s", filterField.Operator)
}
} }
// query = query.Where("certificates.alive=?", true) query, err = operatorHandler(query, tableName, filterField)
if err != nil {
return nil, err
}
}
return query, nil return query, nil
} }
func applyFilterEQ[T int | bool | string](query *gorm.DB, tableName string, filterField *FilterField) *gorm.DB { func handleOperatorEQ(query *gorm.DB, tableName string, filterField *FilterField) (*gorm.DB, error) {
// query = query.Where(fmt.Sprint("%s.%s = ?", tableName, filterField.Name), ) switch filterField.valueKind {
case reflect.Bool:
return query query = applyFilterEQ(query, tableName, filterField, *filterField.boolValue)
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
query = applyFilterEQ(query, tableName, filterField, *filterField.intValue)
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
query = applyFilterEQ(query, tableName, filterField, *filterField.uintValue)
case reflect.Float32, reflect.Float64:
query = applyFilterEQ(query, tableName, filterField, *filterField.floatValue)
case reflect.String:
query = applyFilterEQ(query, tableName, filterField, *filterField.strValue)
default:
return nil, fmt.Errorf("invalid field type for operator %s", filterField.Operator)
}
return query, nil
} }
func getFilterField(tagValue string) (*FilterField, error) { func applyFilterEQ[T bool | int64 | uint64 | float64 | string](
query *gorm.DB, tableName string, filterField *FilterField, value T,
) *gorm.DB {
return query.Where(fmt.Sprintf("%s.%s = ?", tableName, filterField.Name), value)
}
func handleOperatorNE(query *gorm.DB, tableName string, filterField *FilterField) (*gorm.DB, error) {
switch filterField.valueKind {
case reflect.Bool:
query = applyFilterNE(query, tableName, filterField, *filterField.boolValue)
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
query = applyFilterNE(query, tableName, filterField, *filterField.intValue)
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
query = applyFilterNE(query, tableName, filterField, *filterField.uintValue)
case reflect.Float32, reflect.Float64:
query = applyFilterNE(query, tableName, filterField, *filterField.floatValue)
case reflect.String:
query = applyFilterNE(query, tableName, filterField, *filterField.strValue)
default:
return nil, fmt.Errorf("invalid field type for operator %s", filterField.Operator)
}
return query, nil
}
func applyFilterNE[T bool | int64 | uint64 | float64 | string](
query *gorm.DB, tableName string, filterField *FilterField, value T,
) *gorm.DB {
return query.Where(fmt.Sprintf("%s.%s != ?", tableName, filterField.Name), value)
}
func handleOperatorLIKE(query *gorm.DB, tableName string, filterField *FilterField) (*gorm.DB, error) {
switch filterField.valueKind {
case reflect.String:
query = applyFilterLIKE(query, tableName, filterField, *filterField.strValue)
default:
return nil, fmt.Errorf("invalid field type for operator %s", filterField.Operator)
}
return query, nil
}
func applyFilterLIKE(query *gorm.DB, tableName string, filterField *FilterField, value string) *gorm.DB {
return query.Where(fmt.Sprintf("%s.%s LIKE ?", tableName, filterField.Name), fmt.Sprintf("%%%s%%", value))
}
func handleOperatorILIKE(query *gorm.DB, tableName string, filterField *FilterField) (*gorm.DB, error) {
switch filterField.valueKind {
case reflect.String:
query = applyFilterILIKE(query, tableName, filterField, *filterField.strValue)
default:
return nil, fmt.Errorf("invalid field type for operator %s", filterField.Operator)
}
return query, nil
}
func applyFilterILIKE(query *gorm.DB, tableName string, filterField *FilterField, value string) *gorm.DB {
return query.Where(fmt.Sprintf("%s.%s ILIKE ?", tableName, filterField.Name), fmt.Sprintf("%%%s%%", value))
}
func handleOperatorGT(query *gorm.DB, tableName string, filterField *FilterField) (*gorm.DB, error) {
switch filterField.valueKind {
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
query = applyFilterGT(query, tableName, filterField, *filterField.intValue)
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
query = applyFilterGT(query, tableName, filterField, *filterField.uintValue)
case reflect.Float32, reflect.Float64:
query = applyFilterGT(query, tableName, filterField, *filterField.floatValue)
default:
return nil, fmt.Errorf("invalid field type for operator %s", filterField.Operator)
}
return query, nil
}
func applyFilterGT[T bool | int64 | uint64 | float64 | string](
query *gorm.DB, tableName string, filterField *FilterField, value T,
) *gorm.DB {
return query.Where(fmt.Sprintf("%s.%s > ?", tableName, filterField.Name), value)
}
func handleOperatorGE(query *gorm.DB, tableName string, filterField *FilterField) (*gorm.DB, error) {
switch filterField.valueKind {
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
query = applyFilterGE(query, tableName, filterField, *filterField.intValue)
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
query = applyFilterGE(query, tableName, filterField, *filterField.uintValue)
case reflect.Float32, reflect.Float64:
query = applyFilterGE(query, tableName, filterField, *filterField.floatValue)
default:
return nil, fmt.Errorf("invalid field type for operator %s", filterField.Operator)
}
return query, nil
}
func applyFilterGE[T bool | int64 | uint64 | float64 | string](
query *gorm.DB, tableName string, filterField *FilterField, value T,
) *gorm.DB {
return query.Where(fmt.Sprintf("%s.%s >= ?", tableName, filterField.Name), value)
}
func handleOperatorLT(query *gorm.DB, tableName string, filterField *FilterField) (*gorm.DB, error) {
switch filterField.valueKind {
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
query = applyFilterLT(query, tableName, filterField, *filterField.intValue)
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
query = applyFilterLT(query, tableName, filterField, *filterField.uintValue)
case reflect.Float32, reflect.Float64:
query = applyFilterLT(query, tableName, filterField, *filterField.floatValue)
default:
return nil, fmt.Errorf("invalid field type for operator %s", filterField.Operator)
}
return query, nil
}
func applyFilterLT[T bool | int64 | uint64 | float64 | string](
query *gorm.DB, tableName string, filterField *FilterField, value T,
) *gorm.DB {
return query.Where(fmt.Sprintf("%s.%s < ?", tableName, filterField.Name), value)
}
func handleOperatorLE(query *gorm.DB, tableName string, filterField *FilterField) (*gorm.DB, error) {
switch filterField.valueKind {
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
query = applyFilterLE(query, tableName, filterField, *filterField.intValue)
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
query = applyFilterLE(query, tableName, filterField, *filterField.uintValue)
case reflect.Float32, reflect.Float64:
query = applyFilterLE(query, tableName, filterField, *filterField.floatValue)
default:
return nil, fmt.Errorf("invalid field type for operator %s", filterField.Operator)
}
return query, nil
}
func applyFilterLE[T bool | int64 | uint64 | float64 | string](
query *gorm.DB, tableName string, filterField *FilterField, value T,
) *gorm.DB {
return query.Where(fmt.Sprintf("%s.%s <= ?", tableName, filterField.Name), value)
}
func newFilterField(tagValue string) (*FilterField, error) {
values := strings.Split(tagValue, TAG_VALUE_SEPARATOR) values := strings.Split(tagValue, TAG_VALUE_SEPARATOR)
if len(values) != 2 { if len(values) != 2 {
return nil, fmt.Errorf("incorrect number of tag values: %s", tagValue) return nil, fmt.Errorf("incorrect number of tag values: %s", tagValue)
@ -101,6 +257,7 @@ func getFilterField(tagValue string) (*FilterField, error) {
} }
f := FilterField{ f := FilterField{
Name: values[0],
Operator: operator, Operator: operator,
} }
return &f, nil return &f, nil