Skip to content

Commit 0d0f63d

Browse files
authored
Merge pull request #12 from skyhackvip/feature/6
eval compare
2 parents b63d492 + db0c94d commit 0d0f63d

File tree

9 files changed

+272
-57
lines changed

9 files changed

+272
-57
lines changed

configs/const.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ const (
1616
AFTER = "AFTER"
1717
KEYEXIST = "KEYEXIST"
1818
VALUEEXIST = "VALUEEXIST"
19+
AND = "and"
20+
OR = "or"
1921
)
2022

2123
var OperatorMap = map[string]string{
@@ -33,6 +35,8 @@ var OperatorMap = map[string]string{
3335
AFTER: "after",
3436
KEYEXIST: "keyexist",
3537
VALUEEXIST: "valueexist",
38+
AND: "&&",
39+
OR: "||",
3640
}
3741

3842
var NumSupportOperator = map[string]struct{}{
@@ -81,6 +85,20 @@ var DefaultSupportOperator = map[string]struct{}{
8185
NEQ: struct{}{},
8286
}
8387

88+
var CompareOperators = map[string]struct{}{
89+
EQ: struct{}{},
90+
NEQ: struct{}{},
91+
GT: struct{}{},
92+
LT: struct{}{},
93+
GE: struct{}{},
94+
LE: struct{}{},
95+
}
96+
97+
var BooleanOperators = map[string]string{
98+
AND: OperatorMap[AND],
99+
OR: OperatorMap[OR],
100+
}
101+
84102
//all support node
85103
const (
86104
START = "start"

core/feature.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ func (feature *TypeNumFeature) Compare(op string, target interface{}) (bool, err
168168
case "NEQ":
169169
rs, err := operator.Compare(op, value, target)
170170
return rs, err
171-
case "BETWEEN":
171+
case "BETWEEN": //todo: [ ( ) ]
172172
if t, ok := target.([]interface{}); !ok {
173173
return false, errcode.ParseErrorTargetMustBeArray
174174
} else {
@@ -189,7 +189,6 @@ func (feature *TypeNumFeature) Compare(op string, target interface{}) (bool, err
189189
return false, err
190190
}
191191
return rs1 && rs2, nil
192-
193192
}
194193
case "IN":
195194
if t, ok := target.([]interface{}); !ok {
@@ -358,7 +357,7 @@ func (feature *TypeDateFeature) Compare(op string, target interface{}) (bool, er
358357
var err error
359358
switch target.(type) {
360359
case string:
361-
targetTime, err = util.StringToDate(target.(string))
360+
targetTime, err = util.ToDate(target.(string))
362361
if err != nil {
363362
return false, err
364363
}
@@ -368,11 +367,11 @@ func (feature *TypeDateFeature) Compare(op string, target interface{}) (bool, er
368367
if targetArr := target.([]string); len(targetArr) != 2 {
369368
return false, errcode.ParseErrorTargetNotSupport
370369
} else {
371-
targetTimeLeft, err = util.StringToDate(targetArr[0])
370+
targetTimeLeft, err = util.ToDate(targetArr[0])
372371
if err != nil {
373372
return false, err
374373
}
375-
targetTimeRight, err = util.StringToDate(targetArr[1])
374+
targetTimeRight, err = util.ToDate(targetArr[1])
376375
if err != nil {
377376
return false, err
378377
}
@@ -466,9 +465,9 @@ func (feature *TypeArrayFeature) Compare(op string, target interface{}) (bool, e
466465
}
467466
switch op {
468467
case configs.EQ:
469-
return operator.CompareArray(valueArr, targetArr), nil
468+
return operator.Compare(op, valueArr, targetArr)
470469
case configs.NEQ:
471-
return !operator.CompareArray(valueArr, targetArr), nil
470+
return operator.Compare(op, valueArr, targetArr)
472471
case configs.IN:
473472
return operator.AInB(valueArr, targetArr), nil
474473
case configs.CONTAIN:

internal/errcode/common_error.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package errcode
22

33
var (
4-
ErrorFeatureTypeUnknow = NewError(2000001, "feature type support int,float,bool,string,date,array,map")
5-
ErrorTypeConvert = NewError(2000002, "type convert error")
4+
ErrorFeatureTypeUnknow = NewError(2000001, "feature type support int,float,bool,string,date,array,map")
5+
ErrorTypeConvert = NewError(2000002, "type convert error")
6+
ErrorNotSupportOperator = NewError(2000003, "not support operator")
7+
ErrorNotANumber = NewError(2000004, "not a number")
8+
ErrorBooleanValEmpty = NewError(2000005, "boolean operator value is empty")
9+
ErrorBooleanValLack = NewError(2000006, "boolean operator value lack")
610
)

internal/operator/base.go renamed to internal/operator/array_op.go

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,19 @@
11
package operator
22

3-
import (
4-
"errors"
5-
"github.com/Knetic/govaluate"
6-
"github.com/skyhackvip/risk_engine/internal/log"
7-
)
8-
9-
func Evaluate(exprStr string, params map[string]interface{}) (bool, error) {
10-
expr, err := govaluate.NewEvaluableExpression(exprStr)
11-
log.Infof("base evaluate: %v", expr, params)
12-
if err != nil {
13-
return false, err
14-
}
15-
eval, err := expr.Evaluate(params)
16-
if err != nil {
17-
return false, err
18-
}
19-
if result, ok := eval.(bool); ok {
20-
return result, nil
21-
}
22-
return false, errors.New("convert error")
23-
}
24-
3+
//jundge val in arr
254
func InArray(arr []interface{}, val interface{}) bool {
265
if len(arr) == 0 {
276
return false
287
}
298
for _, v := range arr {
30-
if v == val {
9+
if ok, err := Compare("EQ", v, val); err != nil && ok {
3110
return true
3211
}
3312
}
3413
return false
3514
}
3615

16+
//jundge array A in Array B
3717
func AInB(a []interface{}, b []interface{}) bool {
3818
if len(b) == 0 {
3919
return false

internal/operator/compare.go

Lines changed: 122 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,108 @@
11
package operator
22

33
import (
4-
"errors"
5-
"fmt"
64
"github.com/skyhackvip/risk_engine/configs"
5+
"github.com/skyhackvip/risk_engine/internal/errcode"
6+
"github.com/skyhackvip/risk_engine/internal/log"
7+
"github.com/skyhackvip/risk_engine/internal/util"
78
)
89

9-
//compare expression:left [><=] right
10+
//compare operator expression
11+
//left [operator] right
12+
//operator: [eq,neq,gt,lt,ge,le]
1013
func Compare(operator string, left interface{}, right interface{}) (bool, error) {
11-
var params = make(map[string]interface{})
12-
params["left"] = left
13-
params["right"] = right
14+
log.Infof("compare operator: %v", operator, left, right)
15+
if _, ok := configs.CompareOperators[operator]; !ok {
16+
log.Errorf("not compare operator: %v", operator)
17+
return false, errcode.ErrorNotSupportOperator
18+
}
19+
20+
switch operator {
21+
case configs.EQ:
22+
return equal(left, right)
23+
case configs.NEQ:
24+
rs, err := equal(left, right)
25+
return !rs, err
1426

15-
if _, ok := configs.OperatorMap[operator]; !ok {
16-
return false, errors.New("not support operator")
27+
//only number can compare(gt,lt,ge,le)
28+
case configs.GT:
29+
return numCompare(left, right, operator)
30+
case configs.LT:
31+
return numCompare(left, right, operator)
32+
case configs.GE:
33+
return numCompare(left, right, operator)
34+
case configs.LE:
35+
return numCompare(left, right, operator)
1736
}
18-
expr := fmt.Sprintf("left %s right", configs.OperatorMap[operator])
37+
return false, errcode.ErrorNotSupportOperator
38+
}
1939

20-
return Evaluate(expr, params)
40+
//jundge left == right
41+
func equal(left, right interface{}) (bool, error) {
42+
leftType, err := util.GetType(left)
43+
if err != nil {
44+
log.Errorf("left type unknow: %v", left, err)
45+
return false, err //unknow type
46+
}
47+
rightType, err := util.GetType(right)
48+
if err != nil {
49+
log.Errorf("right type unknow: %v", right, err)
50+
return false, err
51+
}
52+
if !util.MatchType(leftType, rightType) {
53+
return false, nil
54+
}
55+
if leftType == configs.ARRAY {
56+
return arrayEqual(left.([]interface{}), right.([]interface{})), nil
57+
}
58+
if leftType == configs.MAP {
59+
return false, errcode.ErrorNotSupportOperator
60+
}
61+
if leftType == configs.STRING {
62+
return left.(string) == right.(string), nil
63+
}
64+
if leftType == configs.BOOL {
65+
leftBool, err := util.ToBool(left)
66+
if err != nil {
67+
return false, err
68+
}
69+
rightBool, err := util.ToBool(right)
70+
if err != nil {
71+
return false, err
72+
}
73+
return leftBool == rightBool, nil
74+
}
75+
if leftType == configs.INT || leftType == configs.FLOAT {
76+
leftNum, err := util.ToFloat64(left)
77+
if err != nil {
78+
return false, err
79+
}
80+
rightNum, err := util.ToFloat64(right)
81+
if err != nil {
82+
return false, err
83+
}
84+
return numCompare(leftNum, rightNum, configs.EQ)
85+
}
86+
if leftType == configs.DATE {
87+
leftDate, err := util.ToDate(left)
88+
if err != nil {
89+
return false, err
90+
}
91+
rightDate, err := util.ToDate(right)
92+
if err != nil {
93+
return false, err
94+
}
95+
return leftDate.Equal(rightDate), nil
96+
}
97+
if leftType == configs.DEFAULT {
98+
return left == right, nil
99+
}
100+
return false, errcode.ErrorNotSupportOperator
21101
}
22102

23-
func CompareArray(a, b []interface{}) bool {
103+
//a == b true
104+
//a != b false
105+
func arrayEqual(a, b []interface{}) bool {
24106
if len(a) != len(b) {
25107
return false
26108
}
@@ -39,3 +121,32 @@ func CompareArray(a, b []interface{}) bool {
39121
}
40122
return true
41123
}
124+
125+
//compare number (lt, gt, le, ge, eq, neq)
126+
//only number can compare
127+
func numCompare(left, right interface{}, op string) (bool, error) {
128+
leftNum, err := util.ToFloat64(left)
129+
if err != nil {
130+
return false, errcode.ErrorNotANumber
131+
}
132+
rightNum, err := util.ToFloat64(right)
133+
if err != nil {
134+
return false, errcode.ErrorNotANumber
135+
}
136+
switch op {
137+
case configs.EQ:
138+
return leftNum == rightNum, nil
139+
case configs.NEQ:
140+
return leftNum != rightNum, nil
141+
case configs.GT:
142+
return leftNum > rightNum, nil
143+
case configs.LT:
144+
return leftNum < rightNum, nil
145+
case configs.GE:
146+
return leftNum >= rightNum, nil
147+
case configs.LE:
148+
return leftNum <= rightNum, nil
149+
default:
150+
return false, errcode.ErrorNotSupportOperator
151+
}
152+
}

internal/operator/eval.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package operator
2+
3+
import (
4+
"errors"
5+
"github.com/Knetic/govaluate"
6+
"github.com/skyhackvip/risk_engine/internal/log"
7+
)
8+
9+
//using govalute to execute expression
10+
func Evaluate(exprStr string, params map[string]interface{}) (bool, error) {
11+
expr, err := govaluate.NewEvaluableExpression(exprStr)
12+
log.Infof("base evaluate: %v", expr, params)
13+
if err != nil {
14+
return false, err
15+
}
16+
eval, err := expr.Evaluate(params)
17+
if err != nil {
18+
return false, err
19+
}
20+
if result, ok := eval.(bool); ok {
21+
return result, nil
22+
}
23+
return false, errors.New("convert error")
24+
}

internal/operator/operator_test.go

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,46 @@
11
package operator
22

33
import (
4+
"github.com/skyhackvip/risk_engine/internal/log"
45
"testing"
6+
"time"
57
)
68

7-
func TestCompareArray(t *testing.T) {
8-
t.Log(CompareArray([]interface{}{3, 8, 7, 6, 9}, []interface{}{9, 6, 7, 3, 8}))
9-
t.Log(CompareArray([]interface{}{"a", "b", "d", "c", "e"}, []interface{}{"a", "b", "c", "d", "e"}))
10-
}
11-
129
func TestAInB(t *testing.T) {
1310
t.Log(AInB([]interface{}{1, 3, 5}, []interface{}{1, 2, 3, 4, 5}))
1411
t.Log(AInB([]interface{}{1, 3, 5}, []interface{}{1, 4, 5, 6}))
1512
t.Log(AInB([]interface{}{1, 3, 5}, []interface{}{1, 3}))
1613
t.Log(AInB([]interface{}{1, 3, 5}, []interface{}{5, 8, 7, 3, 1}))
1714
}
15+
16+
func TestCompare(t *testing.T) {
17+
log.InitLogger("console", "")
18+
t.Log("------eq-------")
19+
t.Log(Compare("EQ", 3, 3.0))
20+
t.Log(Compare("EQ", 3, "3.000"))
21+
t.Log(Compare("EQ", "3", "3.000"))
22+
t.Log(Compare("EQ", "3.0", "3.000"))
23+
t.Log(Compare("EQ", "3.0", "3.0r00"))
24+
t.Log(Compare("EQ", "aa", "aa"))
25+
t.Log(Compare("EQ", "true", true))
26+
t.Log(Compare("EQ", "true", "true"))
27+
t.Log(Compare("EQ", true, true))
28+
29+
t.Log("------ge-------")
30+
t.Log(Compare("GE", "3.0", "2.99999999"))
31+
t.Log(Compare("GE", "3", "2.99999999"))
32+
t.Log(Compare("GE", "3", "2"))
33+
t.Log(Compare("GE", 3, 2))
34+
t.Log(Compare("GE", 3, 2.99999999))
35+
t.Log(Compare("GE", 3.0, 2.99999999))
36+
t.Log(Compare("GE", 3.0, 3.000000000))
37+
38+
t.Log("------date-----")
39+
t.Log(Compare("EQ", "2022-10-10", "2022-10-10 00:00:00"))
40+
now := time.Now()
41+
t.Log(Compare("EQ", now, now))
42+
43+
t.Log("------array-----")
44+
t.Log(Compare("EQ", []interface{}{3, 8, 7, 6, 9}, []interface{}{9, 6, 7, 3, 8}))
45+
t.Log(Compare("EQ", []interface{}{"a", "b", "d", "c", "e"}, []interface{}{"a", "b", "c", "d", "e"}))
46+
}

0 commit comments

Comments
 (0)