module/filter_op.go (1,559 lines of code) (raw):

package module import ( "fmt" "math" "strings" "github.com/alibaba/pairec/v2/recconf" "github.com/alibaba/pairec/v2/utils" ) const ( ITEM = "item" USER = "user" ) type FilterOp interface { Evaluate(map[string]interface{}) (bool, error) OpDomain() string } type FilterByDomainOp interface { FilterOp DomainEvaluate(map[string]any, map[string]any, map[string]any) (bool, error) } type EqualFilterOp struct { Name string Domain string Type string Value interface{} DomainValue string } func NewEqualFilterOp(config recconf.FilterParamConfig) *EqualFilterOp { equalFilterOp := &EqualFilterOp{ Name: config.Name, Type: config.Type, Value: config.Value, } if config.Domain == "" { equalFilterOp.Domain = ITEM } else { equalFilterOp.Domain = config.Domain } if v, ok := config.Value.(string); ok { equalFilterOp.DomainValue = v } return equalFilterOp } func (p *EqualFilterOp) Evaluate(properties map[string]interface{}) (bool, error) { left, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "string": v1 := utils.ToString(left, "v1") v2 := utils.ToString(p.Value, "v2") return v1 == v2, nil case "int": v1 := utils.ToInt(left, -1) v2 := utils.ToInt(p.Value, -2) return v1 == v2, nil case "int64": v1 := utils.ToInt64(left, -1) v2 := utils.ToInt64(p.Value, -2) return v1 == v2, nil default: return false, nil } } func (p *EqualFilterOp) DomainEvaluate(properties map[string]interface{}, userProperties map[string]interface{}, itemProperties map[string]interface{}) (bool, error) { left, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "string": v1 := utils.ToString(left, "v1") var right string if p.DomainValue == "" { right = utils.ToString(p.Value, "v2") } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToString(right1, "v2") } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToString(right1, "v2") } else { right = utils.ToString(p.Value, "v2") } return v1 == right, nil case "int": v1 := utils.ToInt(left, -1) var right int if p.DomainValue == "" { right = utils.ToInt(p.Value, -2) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToInt(right1, -2) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToInt(right1, -2) } else { right = utils.ToInt(p.Value, -2) } return v1 == right, nil case "int64": v1 := utils.ToInt64(left, -1) var right int64 if p.DomainValue == "" { right = utils.ToInt64(p.Value, -2) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToInt64(right1, -2) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToInt64(right1, -2) } else { right = utils.ToInt64(p.Value, -2) } return v1 == right, nil default: return false, nil } } func (p *EqualFilterOp) OpDomain() string { return p.Domain } type NotEqualFilterOp struct { Name string Domain string Type string Value interface{} DomainValue string } func NewNotEqualFilterOp(config recconf.FilterParamConfig) *NotEqualFilterOp { notEqualFilterOp := &NotEqualFilterOp{ Name: config.Name, Type: config.Type, Value: config.Value, } if config.Domain == "" { notEqualFilterOp.Domain = ITEM } else { notEqualFilterOp.Domain = config.Domain } if v, ok := config.Value.(string); ok { notEqualFilterOp.DomainValue = v } return notEqualFilterOp } func (p *NotEqualFilterOp) Evaluate(properties map[string]interface{}) (bool, error) { left, ok := properties[p.Name] if !ok { return true, nil } switch p.Type { case "string": v1 := utils.ToString(left, "") v2 := utils.ToString(p.Value, "") return v1 != v2, nil case "int": v1 := utils.ToInt(left, 0) v2 := utils.ToInt(p.Value, 0) return v1 != v2, nil case "int64": v1 := utils.ToInt64(left, 0) v2 := utils.ToInt64(p.Value, 0) return v1 != v2, nil default: return false, nil } } func (p *NotEqualFilterOp) DomainEvaluate(properties map[string]interface{}, userProperties map[string]interface{}, itemProperties map[string]interface{}) (bool, error) { left, ok := properties[p.Name] if !ok { return true, nil } switch p.Type { case "string": v1 := utils.ToString(left, "") var right string if p.DomainValue == "" { right = utils.ToString(p.Value, "") } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return true, nil } right = utils.ToString(right1, "") } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return true, nil } right = utils.ToString(right1, "") } else { right = utils.ToString(p.Value, "") } return v1 != right, nil case "int": v1 := utils.ToInt(left, 0) var right int if p.DomainValue == "" { right = utils.ToInt(p.Value, 0) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return true, nil } right = utils.ToInt(right1, 0) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return true, nil } right = utils.ToInt(right1, 0) } else { right = utils.ToInt(p.Value, 0) } return v1 != right, nil case "int64": v1 := utils.ToInt64(left, 0) var right int64 if p.DomainValue == "" { right = utils.ToInt64(p.Value, 0) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return true, nil } right = utils.ToInt64(right1, 0) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return true, nil } right = utils.ToInt64(right1, 0) } else { right = utils.ToInt64(p.Value, 0) } return v1 != right, nil default: return false, nil } } func (p *NotEqualFilterOp) OpDomain() string { return p.Domain } type InFilterOp struct { Name string Domain string Type string value string int_values []int string_values []string } func NewInFilterOp(config recconf.FilterParamConfig) *InFilterOp { op := &InFilterOp{ Name: config.Name, Type: config.Type, } if config.Domain == "" { op.Domain = ITEM } else { op.Domain = config.Domain } if val, ok := config.Value.(string); ok { op.value = val } switch op.Type { case "int": op.int_values = utils.ToIntArray(config.Value) case "string": op.string_values = utils.ToStringArray(config.Value) } return op } func (p *InFilterOp) DomainEvaluate(properties map[string]interface{}, userProperties map[string]interface{}, itemProperties map[string]interface{}) (bool, error) { left, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "string": v1 := utils.ToString(left, "") var right []string if p.value == "" { right = p.string_values } else { if strings.HasPrefix(p.value, "user.") { val := p.value[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToStringArray(right1) } else if strings.HasPrefix(p.value, "item.") { val := p.value[5:] right1, ok := itemProperties[val] if !ok { return true, nil } right = utils.ToStringArray(right1) } } for _, val := range right { if v1 == val { return true, nil } } case "int": v1 := utils.ToInt(left, math.MinInt32) var right []int if p.value == "" { right = p.int_values } else { if strings.HasPrefix(p.value, "user.") { val := p.value[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToIntArray(right1) } else if strings.HasPrefix(p.value, "item.") { val := p.value[5:] right1, ok := itemProperties[val] if !ok { return true, nil } right = utils.ToIntArray(right1) } } for _, val := range right { if v1 == val { return true, nil } } } return false, nil } func (p *InFilterOp) Evaluate(properties map[string]interface{}) (bool, error) { left, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "string": v1 := utils.ToString(left, "") for _, val := range p.string_values { if v1 == val { return true, nil } } case "int": v1 := utils.ToInt(left, math.MinInt32) for _, val := range p.int_values { if v1 == val { return true, nil } } } return false, nil } func (p *InFilterOp) OpDomain() string { return p.Domain } type FilterParam struct { operators []FilterOp } func NewFilterParamWithConfig(configs []recconf.FilterParamConfig) *FilterParam { p := FilterParam{ operators: make([]FilterOp, 0, 4), } for _, config := range configs { if config.Operator == "equal" { p.operators = append(p.operators, NewEqualFilterOp(config)) } else if config.Operator == "not_equal" { p.operators = append(p.operators, NewNotEqualFilterOp(config)) } else if config.Operator == "in" { p.operators = append(p.operators, NewInFilterOp(config)) } else if config.Operator == "not_in" { p.operators = append(p.operators, NewNotInFilterOp(config)) } else if config.Operator == "greater" { p.operators = append(p.operators, NewGreaterFilterOp(config)) } else if config.Operator == "greaterThan" { p.operators = append(p.operators, NewGreaterThanFilterOp(config)) } else if config.Operator == "less" { p.operators = append(p.operators, NewLessFilterOp(config)) } else if config.Operator == "lessThan" { p.operators = append(p.operators, NewLessThanFilterOp(config)) } else if config.Operator == "contains" { p.operators = append(p.operators, NewContainsFilterOp(config)) } else if config.Operator == "not_contains" { p.operators = append(p.operators, NewNotContainsFilterOp(config)) } else if config.Operator == "is_null" { p.operators = append(p.operators, NewIsNullFilterOp(config)) } else if config.Operator == "is_not_null" { p.operators = append(p.operators, NewIsNotNullFilterOp(config)) } else if config.Operator == "bool" { p.operators = append(p.operators, NewBoolFilterOp(config)) } } return &p } func (p *FilterParam) Evaluate(properties map[string]interface{}) (bool, error) { for _, op := range p.operators { ret, err := op.Evaluate(properties) if !ret || err != nil { return false, err } } return true, nil } func (p *FilterParam) EvaluateByDomain(userProperties, itemProperties map[string]interface{}) (bool, error) { for _, op := range p.operators { if domainFilterOp, ok := op.(FilterByDomainOp); ok { if domainFilterOp.OpDomain() == ITEM { ret, err := domainFilterOp.DomainEvaluate(itemProperties, userProperties, itemProperties) if !ret || err != nil { return false, err } } else if domainFilterOp.OpDomain() == USER { ret, err := domainFilterOp.DomainEvaluate(userProperties, userProperties, itemProperties) if !ret || err != nil { return false, err } } } else { if op.OpDomain() == ITEM { ret, err := op.Evaluate(itemProperties) if ret == false || err != nil { return false, err } } else if op.OpDomain() == USER { ret, err := op.Evaluate(userProperties) if ret == false || err != nil { return false, err } } else { return false, fmt.Errorf("not support this domain:%s", op.OpDomain()) } } } return true, nil } type GreaterFilterOp struct { Name string Domain string Type string Value interface{} DomainValue string } func NewGreaterFilterOp(config recconf.FilterParamConfig) *GreaterFilterOp { greaterFilterOp := &GreaterFilterOp{ Name: config.Name, Type: config.Type, Value: config.Value, } if config.Domain == "" { greaterFilterOp.Domain = ITEM } else { greaterFilterOp.Domain = config.Domain } if v, ok := config.Value.(string); ok { greaterFilterOp.DomainValue = v } return greaterFilterOp } func (p *GreaterFilterOp) Evaluate(properties map[string]interface{}) (bool, error) { left, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "float": v1 := utils.ToFloat(left, 0) v2 := utils.ToFloat(p.Value, 0) return v1 > v2, nil case "int": v1 := utils.ToInt(left, 0) v2 := utils.ToInt(p.Value, 0) return v1 > v2, nil case "int64": v1 := utils.ToInt64(left, 0) v2 := utils.ToInt64(p.Value, 0) return v1 > v2, nil default: return false, nil } } func (p *GreaterFilterOp) DomainEvaluate(properties map[string]interface{}, userProperties map[string]interface{}, itemProperties map[string]interface{}) (bool, error) { left, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "float": v1 := utils.ToFloat(left, 0) var right float64 if p.DomainValue == "" { right = utils.ToFloat(p.Value, 0) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToFloat(right1, 0) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return true, nil } right = utils.ToFloat(right1, 0) } else { right = utils.ToFloat(p.Value, 0) } return v1 > right, nil case "int": v1 := utils.ToInt(left, 0) var right int if p.DomainValue == "" { right = utils.ToInt(p.Value, 0) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToInt(right1, 0) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToInt(right1, 0) } else { right = utils.ToInt(p.Value, 0) } return v1 > right, nil case "int64": v1 := utils.ToInt64(left, 0) var right int64 if p.DomainValue == "" { right = utils.ToInt64(p.Value, 0) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToInt64(right1, 0) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToInt64(right1, 0) } else { right = utils.ToInt64(p.Value, 0) } return v1 > right, nil case "time": v1 := utils.ToString(left, "") var right string if p.DomainValue == "" { right = utils.ToString(p.Value, "") } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToString(right1, "") } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToString(right1, "") } else { right = utils.ToString(p.Value, "") } leftTime, leftOk := utils.TryParseTime(v1) if !leftOk { return false, nil } rightTime, rightOk := utils.TryParseTime(right) if !rightOk { return false, nil } return leftTime.UnixNano() > rightTime.UnixNano(), nil default: return false, nil } } func (p *GreaterFilterOp) OpDomain() string { return p.Domain } type GreaterThanFilterOp struct { Name string Domain string Type string Value interface{} DomainValue string } func NewGreaterThanFilterOp(config recconf.FilterParamConfig) *GreaterThanFilterOp { greaterThanFilterOp := &GreaterThanFilterOp{ Name: config.Name, Type: config.Type, Value: config.Value, } if config.Domain == "" { greaterThanFilterOp.Domain = ITEM } else { greaterThanFilterOp.Domain = config.Domain } if v, ok := config.Value.(string); ok { greaterThanFilterOp.DomainValue = v } return greaterThanFilterOp } func (p *GreaterThanFilterOp) Evaluate(properties map[string]interface{}) (bool, error) { left, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "float": v1 := utils.ToFloat(left, 0) v2 := utils.ToFloat(p.Value, 0) return v1 >= v2, nil case "int": v1 := utils.ToInt(left, 0) v2 := utils.ToInt(p.Value, 0) return v1 >= v2, nil case "int64": v1 := utils.ToInt64(left, 0) v2 := utils.ToInt64(p.Value, 0) return v1 >= v2, nil default: return false, nil } } func (p *GreaterThanFilterOp) DomainEvaluate(properties map[string]interface{}, userProperties map[string]interface{}, itemProperties map[string]interface{}) (bool, error) { left, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "float": v1 := utils.ToFloat(left, math.SmallestNonzeroFloat64) var right float64 if p.DomainValue == "" { right = utils.ToFloat(p.Value, math.MaxFloat64) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToFloat(right1, math.MaxFloat64) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return true, nil } right = utils.ToFloat(right1, math.MaxFloat64) } else { right = utils.ToFloat(p.Value, math.MaxFloat64) } return v1 >= right, nil case "int": v1 := utils.ToInt(left, math.MinInt) var right int if p.DomainValue == "" { right = utils.ToInt(p.Value, math.MaxInt) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToInt(right1, math.MaxInt) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToInt(right1, math.MaxInt) } else { right = utils.ToInt(p.Value, math.MaxInt) } return v1 >= right, nil case "int64": v1 := utils.ToInt64(left, 0) var right int64 if p.DomainValue == "" { right = utils.ToInt64(p.Value, 0) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToInt64(right1, 0) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToInt64(right1, 0) } else { right = utils.ToInt64(p.Value, 0) } return v1 >= right, nil case "time": v1 := utils.ToString(left, "") var right string if p.DomainValue == "" { right = utils.ToString(p.Value, "") } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToString(right1, "") } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToString(right1, "") } else { right = utils.ToString(p.Value, "") } leftTime, leftOk := utils.TryParseTime(v1) if !leftOk { return false, nil } rightTime, rightOk := utils.TryParseTime(right) if !rightOk { return false, nil } return leftTime.UnixNano() >= rightTime.UnixNano(), nil default: return false, nil } } func (p *GreaterThanFilterOp) OpDomain() string { return p.Domain } type LessFilterOp struct { Name string Domain string Type string Value interface{} DomainValue string } func NewLessFilterOp(config recconf.FilterParamConfig) *LessFilterOp { lessFilterOp := &LessFilterOp{ Name: config.Name, Type: config.Type, Value: config.Value, } if config.Domain == "" { lessFilterOp.Domain = ITEM } else { lessFilterOp.Domain = config.Domain } if v, ok := config.Value.(string); ok { lessFilterOp.DomainValue = v } return lessFilterOp } func (p *LessFilterOp) Evaluate(properties map[string]interface{}) (bool, error) { left, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "float": v1 := utils.ToFloat(left, 0) v2 := utils.ToFloat(p.Value, 0) return v1 < v2, nil case "int": v1 := utils.ToInt(left, 0) v2 := utils.ToInt(p.Value, 0) return v1 < v2, nil case "int64": v1 := utils.ToInt64(left, 0) v2 := utils.ToInt64(p.Value, 0) return v1 < v2, nil default: return false, nil } } func (p *LessFilterOp) DomainEvaluate(properties map[string]interface{}, userProperties map[string]interface{}, itemProperties map[string]interface{}) (bool, error) { left, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "float": v1 := utils.ToFloat(left, math.MaxFloat64) var right float64 if p.DomainValue == "" { right = utils.ToFloat(p.Value, math.SmallestNonzeroFloat64) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToFloat(right1, math.SmallestNonzeroFloat64) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return true, nil } right = utils.ToFloat(right1, math.SmallestNonzeroFloat64) } else { right = utils.ToFloat(p.Value, math.SmallestNonzeroFloat64) } return v1 < right, nil case "int": v1 := utils.ToInt(left, math.MaxInt) var right int if p.DomainValue == "" { right = utils.ToInt(p.Value, math.MinInt) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToInt(right1, math.MinInt) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToInt(right1, math.MinInt) } else { right = utils.ToInt(p.Value, math.MinInt) } return v1 < right, nil case "int64": v1 := utils.ToInt64(left, 0) var right int64 if p.DomainValue == "" { right = utils.ToInt64(p.Value, 0) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToInt64(right1, 0) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToInt64(right1, 0) } else { right = utils.ToInt64(p.Value, 0) } return v1 < right, nil case "time": v1 := utils.ToString(left, "") var right string if p.DomainValue == "" { right = utils.ToString(p.Value, "") } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToString(right1, "") } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToString(right1, "") } else { right = utils.ToString(p.Value, "") } leftTime, leftOk := utils.TryParseTime(v1) if !leftOk { return false, nil } rightTime, rightOk := utils.TryParseTime(right) if !rightOk { return false, nil } return leftTime.UnixNano() < rightTime.UnixNano(), nil default: return false, nil } } func (p *LessFilterOp) OpDomain() string { return p.Domain } type LessThanFilterOp struct { Name string Domain string Type string Value interface{} DomainValue string } func NewLessThanFilterOp(config recconf.FilterParamConfig) *LessThanFilterOp { lessThanFilterOp := &LessThanFilterOp{ Name: config.Name, Type: config.Type, Value: config.Value, } if config.Domain == "" { lessThanFilterOp.Domain = ITEM } else { lessThanFilterOp.Domain = config.Domain } if v, ok := config.Value.(string); ok { lessThanFilterOp.DomainValue = v } return lessThanFilterOp } func (p *LessThanFilterOp) Evaluate(properties map[string]interface{}) (bool, error) { left, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "float": v1 := utils.ToFloat(left, 0) v2 := utils.ToFloat(p.Value, 0) return v1 <= v2, nil case "int": v1 := utils.ToInt(left, 0) v2 := utils.ToInt(p.Value, 0) return v1 <= v2, nil case "int64": v1 := utils.ToInt64(left, 0) v2 := utils.ToInt64(p.Value, 0) return v1 <= v2, nil default: return false, nil } } func (p *LessThanFilterOp) DomainEvaluate(properties map[string]interface{}, userProperties map[string]interface{}, itemProperties map[string]interface{}) (bool, error) { left, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "float": v1 := utils.ToFloat(left, math.MaxFloat64) var right float64 if p.DomainValue == "" { right = utils.ToFloat(p.Value, math.SmallestNonzeroFloat64) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToFloat(right1, math.SmallestNonzeroFloat64) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return true, nil } right = utils.ToFloat(right1, math.SmallestNonzeroFloat64) } else { right = utils.ToFloat(p.Value, math.SmallestNonzeroFloat64) } return v1 <= right, nil case "int": v1 := utils.ToInt(left, math.MaxInt) var right int if p.DomainValue == "" { right = utils.ToInt(p.Value, math.MinInt) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToInt(right1, math.MinInt) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToInt(right1, math.MinInt) } else { right = utils.ToInt(p.Value, math.MinInt) } return v1 <= right, nil case "int64": v1 := utils.ToInt64(left, 0) var right int64 if p.DomainValue == "" { right = utils.ToInt64(p.Value, 0) } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToInt64(right1, 0) } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToInt64(right1, 0) } else { right = utils.ToInt64(p.Value, 0) } return v1 <= right, nil case "time": v1 := utils.ToString(left, "") var right string if p.DomainValue == "" { right = utils.ToString(p.Value, "") } else if strings.HasPrefix(p.DomainValue, "user.") { val := p.DomainValue[5:] right1, ok := userProperties[val] if !ok { return false, nil } right = utils.ToString(right1, "") } else if strings.HasPrefix(p.DomainValue, "item.") { val := p.DomainValue[5:] right1, ok := itemProperties[val] if !ok { return false, nil } right = utils.ToString(right1, "") } else { right = utils.ToString(p.Value, "") } leftTime, leftOk := utils.TryParseTime(v1) if !leftOk { return false, nil } rightTime, rightOk := utils.TryParseTime(right) if !rightOk { return false, nil } return leftTime.UnixNano() <= rightTime.UnixNano(), nil default: return false, nil } } func (p *LessThanFilterOp) OpDomain() string { return p.Domain } type ContainsFilterOp struct { Name string Domain string Type string Value interface{} } func NewContainsFilterOp(config recconf.FilterParamConfig) *ContainsFilterOp { containsFilterOp := &ContainsFilterOp{ Name: config.Name, Type: config.Type, Value: config.Value, } if config.Domain == "" { containsFilterOp.Domain = ITEM } else { containsFilterOp.Domain = config.Domain } return containsFilterOp } func (p *ContainsFilterOp) Evaluate(properties map[string]interface{}) (bool, error) { return false, nil } func (p *ContainsFilterOp) DomainEvaluate(properties map[string]interface{}, userProperties map[string]interface{}, itemProperties map[string]interface{}) (bool, error) { left1, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "[]string": var ( left []string right []string ) left = utils.ToStringArray(left1) if value, ok := p.Value.(string); ok { var right1 interface{} if strings.HasPrefix(value, "user.") { val := value[5:] right1, ok = userProperties[val] if !ok { return false, nil } } else if strings.HasPrefix(value, "item.") { val := value[5:] right1, ok = itemProperties[val] if !ok { return false, nil } } else { right1 = []string{value} } right = utils.ToStringArray(right1) } else { right = utils.ToStringArray(p.Value) } if len(left) == 0 || len(right) == 0 { return false, nil } return utils.StringContains(left, right), nil case "[]int": var ( left []int right []int ) left = utils.ToIntArray(left1) if value, ok := p.Value.(string); ok { var right1 interface{} if strings.HasPrefix(value, "user.") { val := value[5:] right1, ok = userProperties[val] if !ok { return false, nil } } else if strings.HasPrefix(value, "item.") { val := value[5:] right1, ok = itemProperties[val] if !ok { return false, nil } } right = utils.ToIntArray(right1) } else { right = utils.ToIntArray(p.Value) } if len(left) == 0 || len(right) == 0 { return false, nil } return utils.IntContains(left, right), nil } return false, nil } func (p *ContainsFilterOp) OpDomain() string { return p.Domain } type NotContainsFilterOp struct { Name string Domain string Type string Value interface{} } func NewNotContainsFilterOp(config recconf.FilterParamConfig) *NotContainsFilterOp { containsFilterOp := &NotContainsFilterOp{ Name: config.Name, Type: config.Type, Value: config.Value, } if config.Domain == "" { containsFilterOp.Domain = ITEM } else { containsFilterOp.Domain = config.Domain } return containsFilterOp } func (p *NotContainsFilterOp) Evaluate(properties map[string]interface{}) (bool, error) { return false, nil } func (p *NotContainsFilterOp) DomainEvaluate(properties map[string]interface{}, userProperties map[string]interface{}, itemProperties map[string]interface{}) (bool, error) { left1, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "[]string": var ( left []string right []string ) left = utils.ToStringArray(left1) if value, ok := p.Value.(string); ok { var right1 interface{} if strings.HasPrefix(value, "user.") { val := value[5:] right1, ok = userProperties[val] if !ok { return false, nil } } else if strings.HasPrefix(value, "item.") { val := value[5:] right1, ok = itemProperties[val] if !ok { return false, nil } } right = utils.ToStringArray(right1) } else { right = utils.ToStringArray(p.Value) } if len(left) == 0 || len(right) == 0 { return false, nil } return !utils.StringContains(left, right), nil case "[]int": var ( left []int right []int ) left = utils.ToIntArray(left1) if value, ok := p.Value.(string); ok { var right1 interface{} if strings.HasPrefix(value, "user.") { val := value[5:] right1, ok = userProperties[val] if !ok { return false, nil } } else if strings.HasPrefix(value, "item.") { val := value[5:] right1, ok = itemProperties[val] if !ok { return false, nil } } right = utils.ToIntArray(right1) } else { right = utils.ToIntArray(p.Value) } if len(left) == 0 || len(right) == 0 { return false, nil } return !utils.IntContains(left, right), nil } return false, nil } func (p *NotContainsFilterOp) OpDomain() string { return p.Domain } type NotInFilterOp struct { Name string Domain string Type string value string int_values []int string_values []string } func NewNotInFilterOp(config recconf.FilterParamConfig) *NotInFilterOp { op := &NotInFilterOp{ Name: config.Name, Type: config.Type, } if config.Domain == "" { op.Domain = ITEM } else { op.Domain = config.Domain } if value, ok := config.Value.(string); ok { op.value = value } else { switch op.Type { case "int": op.int_values = utils.ToIntArray(config.Value) case "string": op.string_values = utils.ToStringArray(config.Value) } } return op } func (p *NotInFilterOp) Evaluate(properties map[string]interface{}) (bool, error) { return false, nil } func (p *NotInFilterOp) DomainEvaluate(properties map[string]interface{}, userProperties map[string]interface{}, itemProperties map[string]interface{}) (bool, error) { left1, ok := properties[p.Name] if !ok { return false, nil } switch p.Type { case "string": left := utils.ToString(left1, "") var right []string if p.value == "" { right = p.string_values } else { if strings.HasPrefix(p.value, "user.") { val := p.value[5:] right1, ok := userProperties[val] if !ok { return true, nil } right = utils.ToStringArray(right1) } else if strings.HasPrefix(p.value, "item.") { val := p.value[5:] right1, ok := itemProperties[val] if !ok { return true, nil } right = utils.ToStringArray(right1) } } for _, val := range right { if left == val { return false, nil } } return true, nil case "int": left := utils.ToInt(left1, math.MinInt32) var ( right []int ) if p.value == "" { right = p.int_values } else { if strings.HasPrefix(p.value, "user.") { val := p.value[5:] right1, ok := userProperties[val] if !ok { return true, nil } right = utils.ToIntArray(right1) } else if strings.HasPrefix(p.value, "item.") { val := p.value[5:] right1, ok := itemProperties[val] if !ok { return true, nil } right = utils.ToIntArray(right1) } } for _, val := range right { if left == val { return false, nil } } return true, nil } return false, nil } func (p *NotInFilterOp) OpDomain() string { return p.Domain } type IsNullFilterOp struct { Name string Domain string } func NewIsNullFilterOp(config recconf.FilterParamConfig) *IsNullFilterOp { isNullFilterOp := &IsNullFilterOp{ Name: config.Name, } if config.Domain == "" { isNullFilterOp.Domain = ITEM } else { isNullFilterOp.Domain = config.Domain } return isNullFilterOp } func (p *IsNullFilterOp) Evaluate(properties map[string]interface{}) (bool, error) { _, ok := properties[p.Name] if !ok { return true, nil } return false, nil } func (p *IsNullFilterOp) OpDomain() string { return p.Domain } type IsNotNullFilterOp struct { Name string Domain string } func NewIsNotNullFilterOp(config recconf.FilterParamConfig) *IsNotNullFilterOp { isNotNullFilterOp := &IsNotNullFilterOp{ Name: config.Name, } if config.Domain == "" { isNotNullFilterOp.Domain = ITEM } else { isNotNullFilterOp.Domain = config.Domain } return isNotNullFilterOp } func (p *IsNotNullFilterOp) Evaluate(properties map[string]interface{}) (bool, error) { _, ok := properties[p.Name] if !ok { return false, nil } return true, nil } func (p *IsNotNullFilterOp) OpDomain() string { return p.Domain } type BoolFilterOp struct { filterParam *FilterParam isOrCondition bool } func NewBoolFilterOp(config recconf.FilterParamConfig) *BoolFilterOp { boolFilterOp := &BoolFilterOp{} v := utils.ToString(config.Type, "") if v == "" || strings.ToLower(v) == "or" { boolFilterOp.isOrCondition = true } fp := NewFilterParamWithConfig(config.Configs) boolFilterOp.filterParam = fp return boolFilterOp } func (p *BoolFilterOp) Evaluate(properties map[string]interface{}) (bool, error) { if p.filterParam == nil { return false, nil } for _, op := range p.filterParam.operators { ret, err := op.Evaluate(properties) if err != nil { return false, err } if p.isOrCondition { if ret { return true, nil } } else { if !ret { return false, nil } } } if p.isOrCondition { return false, nil } else { return true, nil } } func (p *BoolFilterOp) DomainEvaluate(properties map[string]interface{}, userProperties map[string]interface{}, itemProperties map[string]interface{}) (bool, error) { if p.filterParam == nil { return false, nil } for _, op := range p.filterParam.operators { if domainFilterOp, ok := op.(FilterByDomainOp); ok { if domainFilterOp.OpDomain() == ITEM { ret, err := domainFilterOp.DomainEvaluate(itemProperties, userProperties, itemProperties) if err != nil { return false, err } if p.isOrCondition { if ret { return true, nil } } else { if !ret { return false, nil } } } else if domainFilterOp.OpDomain() == USER { ret, err := domainFilterOp.DomainEvaluate(userProperties, userProperties, itemProperties) if err != nil { return false, err } if p.isOrCondition { if ret { return true, nil } } else { if !ret { return false, nil } } } } else { if op.OpDomain() == ITEM { ret, err := op.Evaluate(itemProperties) if err != nil { return false, err } if p.isOrCondition { if ret { return true, nil } } else { if !ret { return false, nil } } } else if op.OpDomain() == USER { ret, err := op.Evaluate(userProperties) if err != nil { return false, err } if p.isOrCondition { if ret { return true, nil } } else { if !ret { return false, nil } } } else { return false, fmt.Errorf("not support this domain:%s", op.OpDomain()) } } } if p.isOrCondition { return false, nil } else { return true, nil } } func (p *BoolFilterOp) OpDomain() string { return ITEM }