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
}