execute rules on list of data objects
Hello, I read the example:
...
type User struct { Name string Age int64 Male bool }
func (u *User)GetNum(i int64) int64 { return i }
func (u *User)Print(s string){ fmt.Println(s) }
func (u *User)Say(){ fmt.Println("hello world") }
const (
base_rule = ...
func exe(user *User){
dataContext := context.NewDataContext()
//inject struct
dataContext.Add("User", user)
...
//init rule engine
knowledgeContext := base.NewKnowledgeContext()
ruleBuilder := builder.NewRuleBuilder(knowledgeContext, dataContext)
//读取规则
start1 := time.Now().UnixNano()
err := ruleBuilder.BuildRuleFromString(base_rule)
end1 := time.Now().UnixNano()
logrus.Infof("rules num:%d, load rules cost time:%d ns", len(knowledgeContext.RuleEntities), end1-start1 )
if err != nil{
logrus.Errorf("err:%s ", err)
}else{
eng := engine.NewGengine()
start := time.Now().UnixNano()
// true: means when there are many rules, if one rule execute error,continue to execute rules after the occur error rule
err := eng.Execute(ruleBuilder, true)
... } }
func Test_Base(t *testing.T){ user := &User{ Name: "Calo", Age: 0, Male: true, } exe(user) // HERE: If I have a list of user, for example I have 10 user object, how to run exe(user) efficiently? }
My questions is: if I have a list of user, with 10 user objects. How to run exe(user) 10 times but more efficiently. Because in the exe it will execute the functions in sequence as below: NewDataContext, then NewKnowledgeContext, NewRuleBuilder, NewGengine and eng.Execute(ruleBuilder, true). I think those methods are just duplicated running for 10 times, how can I inject each of the user object in the list by the function dataContext.Add("User",user), but do not execute other function each time I inject a new user object?
If you have a list of data need to checks by rules, you should NEVER DO IT LIKE THIS "NewDataContext, then NewKnowledgeContext, NewRuleBuilder, NewGengine and eng.Execute(ruleBuilder, true) " again and again !
First, I am so sorry for you that we just finished the gengine Chinese doc, not finish the gengine English doc, in future,we will finish it!
We recommend users to use the gengine pool, and the methods are one-to-one between gengine.go and gengine_pool.go
if you have a list of data(users) ,you could do it like this:
import (
"fmt"
"gengine/engine"
"testing"
)
type User struct {
Name string
Age int
}
func Test_list_data_check_by_rules(t *testing.T) {
// there are many rules you add
var rules = `
rule "1"
begin
// you want to do
end
rule "2"
begin
// you want to do
end
// rules "n"...
`
//apis use to inject non-state function or service which will not cause thread-safety bug
apis := make(map[string]interface{})
apis["println"] = fmt.Println
//just init once !
pool, e := engine.NewGenginePool(10, 20, 1, rules, apis)
if e != nil {
//init pool failed
panic(e)
}
// there is your a list of data(users)
users := make([]*User, 100)
for _, user := range users {
tmpApisOrData := make(map[string]interface{})
tmpApisOrData["user"] = user
//execute rules
//all methods the gengine pool supply is thread-safety
e, _ := pool.Execute(tmpApisOrData, true)
if e != nil {
//log or return the err
//errors.New(fmt.Sprintf("execute rules err:%+v", e))
}
// err== nil to do you want to do
}
// or concurrent to execute
/*
for _, user := range users {
u := user
go func() {
tmpApisOrData := make(map[string]interface{})
tmpApisOrData["user"] = u
//execute rules
//all methods the gengine pool supply is thread-safety
e, _ := pool.Execute(tmpApisOrData, true)
if e != nil {
//log or return the err
//errors.New(fmt.Sprintf("execute rules err:%+v", e))
}
// err== nil to do you want to do
}()
}
*/
}
```
Got it, thanks for the quick answer.