golang动态决策树规则引擎插件库ddt的使用
golang动态决策树规则引擎插件库ddt的使用
DDT是一个允许基于一组定义规则构建自定义决策树的Golang库,可以通过编程方式或从JSON创建。
动态决策树
DDT允许在比较决策树的下一个可能分支之前,对输入添加预处理阶段。DDT中默认的预处理函数包括使用反射调用结构体方法(CallStructMethod)和获取结构体属性(GetStructAttribute)。
示例
创建用户树
使用结构体作为树的输入,并在比较前预处理数据。在这个例子中,我们使用了用户结构体的一些方法和属性。
package main
import (
"fmt"
"github.com/sgrodriguez/ddt"
"github.com/sgrodriguez/ddt/compare"
"github.com/sgrodriguez/ddt/function"
"github.com/sgrodriguez/ddt/value"
)
type user struct {
Age int
FirstName string
LastName string
}
func (u *user) UnderAge() bool {
return u.Age < 18
}
func (u *user) FullName() string {
return u.FirstName + " " + u.LastName
}
func main() {
node6 := &ddt.Node{
ID: 6,
ParentID: 2,
ValueToCompare: &value.Value{Type: value.Int, Value: 30},
Result: &value.Value{Type: value.String, Value: "node6"},
Comparer: &compare.Greater{},
}
node5 := &ddt.Node{
ID: 5,
ParentID: 2,
ValueToCompare: &value.Value{Type: value.Int, Value: 30},
Result: &value.Value{Type: value.String, Value: "node5"},
Comparer: &compare.Lesser{Equal: true},
}
node3 := &ddt.Node{
ID: 3,
ParentID: 1,
ValueToCompare: &value.Value{Type: value.String, Value: "SANTIAGO LUCIA"},
Result: &value.Value{Type: value.String, Value: "node3"},
Comparer: &compare.Equal{},
}
node4 := &ddt.Node{
ID: 4,
ParentID: 1,
ValueToCompare: &value.Value{Type: value.String, Value: "LUCIA SANTIAGO"},
Result: &value.Value{Type: value.String, Value: "node4"},
Comparer: &compare.Equal{},
}
node1 := &ddt.Node{
ID: 1,
ParentID: 0,
Children: []*ddt.Node{node3, node4},
ValueToCompare: &value.Value{Type: value.Bool, Value: true},
PreProcessArgs: []*value.Value{{Type: value.String, Value: "FullName"}},
PreProcessFn: function.PreProcessFn{Function: function.CallStructMethod, Name: "CallStructMethod"},
Comparer: &compare.Equal{},
}
node2 := &ddt.Node{
ID: 2,
ParentID: 0,
Children: []*ddt.Node{node5, node6},
ValueToCompare: &value.Value{Type: value.Bool, Value: false},
Comparer: &compare.Equal{},
PreProcessArgs: []*value.Value{{Type: value.String, Value: "Age"}},
PreProcessFn: function.PreProcessFn{Function: function.GetStructAttribute, Name: "GetStructAttribute"},
}
root := &ddt.Node{
Children: []*ddt.Node{node1, node2},
PreProcessArgs: []*value.Value{{Type: value.String, Value: "UnderAge"}},
PreProcessFn: function.PreProcessFn{Function: function.CallStructMethod, Name: "CallStructMethod"},
ID: 0,
ParentID: -1,
}
userTree, err := ddt.NewTree("userTree", root)
if err != nil {
panic(err)
}
result, err := ddt.ResolveTree(userTree, &user{Age: 12, FirstName: "SANTIAGO", LastName: "LUCIA"})
if err != nil {
panic(err)
}
// 结果 node3
fmt.Println(result.(string))
}
从JSON创建用户树
package main
import (
"encoding/json"
"fmt"
"github.com/sgrodriguez/ddt"
)
type user struct {
Age int
FirstName string
LastName string
}
func (u *user) UnderAge() bool {
return u.Age < 18
}
func (u *user) FullName() string {
return u.FirstName + " " + u.LastName
}
func main() {
// 定义空树
tree, err := ddt.NewTree("newTree", &ddt.Node{ID: 0, ParentID: -1})
if err != nil {
panic(err)
}
treeFromJson := []byte(`
{
"nodes":[
{
"preProcessFnName":"CallStructMethod",
"id":0,
"parentId":-1,
"preProcessFnArgs":[
{
"Value":"UnderAge",
"Type":"string"
}
]
},
{
"preProcessFnName":"CallStructMethod",
"id":1,
"parentId":0,
"preProcessFnArgs":[
{
"Value":"FullName",
"Type":"string"
}
],
"comparer":{
"type":"eq"
},
"valueToCompare":{
"Value":true,
"Type":"bool"
}
},
{
"preProcessFnName":"GetStructAttribute",
"id":2,
"parentId":0,
"preProcessFnArgs":[
{
"Value":"Age",
"Type":"string"
}
],
"comparer":{
"type":"eq"
},
"valueToCompare":{
"Value":false,
"Type":"bool"
}
},
{
"preProcessFnName":"",
"id":3,
"parentId":1,
"comparer":{
"type":"eq"
},
"valueToCompare":{
"Value":"SANTIAGO LUCIA",
"Type":"string"
},
"result":{
"Value":"node3",
"Type":"string"
}
},
{
"preProcessFnName":"",
"id":4,
"parentId":1,
"comparer":{
"type":"eq"
},
"valueToCompare":{
"Value":"LUCIA SANTIAGO",
"Type":"string"
},
"result":{
"Value":"node4",
"Type":"string"
}
},
{
"preProcessFnName":"",
"id":5,
"parentId":2,
"comparer":{
"type":"lt",
"equal":true
},
"valueToCompare":{
"Value":30,
"Type":"int"
},
"result":{
"Value":"node5",
"Type":"string"
}
},
{
"preProcessFnName":"",
"id":6,
"parentId":2,
"comparer":{
"type":"gt",
"equal":false
},
"valueToCompare":{
"Value":30,
"Type":"int"
},
"result":{
"Value":"node6",
"Type":"string"
}
}
],
"name":"userTree"
}`)
err = json.Unmarshal(treeFromJson, tree)
if err != nil {
panic(err)
}
result, err := ddt.ResolveTree(tree, &user{Age: 12, FirstName: "SANTIAGO", LastName: "LUCIA"})
if err != nil {
panic(err)
}
// 结果 node3
fmt.Println(result.(string))
treeByte, err := json.Marshal(tree)
fmt.Println(string(treeByte))
}
创建简单树
创建一个仅使用基本类型的简单树。
package main
import (
"fmt"
"github.com/sgrodriguez/ddt"
"github.com/sgrodriguez/ddt/compare"
"github.com/sgrodriguez/ddt/value"
)
func main() {
leaf1 := ddt.Node{
ID: 1,
ParentID: 0,
ValueToCompare: &value.Value{Value: int64(60), Type: value.Int64},
Comparer: &compare.Greater{},
Result: &value.Value{Value: "prize1", Type: value.String},
}
leaf11 := ddt.Node{
ID: 3,
ParentID: 2,
ValueToCompare: &value.Value{Value: int64(30), Type: value.Int64},
Comparer: &compare.Equal{},
Result: &value.Value{Value: "prize2", Type: value.String},
}
leaf12 := ddt.Node{
ID: 4,
ParentID: 2,
ValueToCompare: &value.Value{Value: int64(30), Type: value.Int64},
Comparer: &compare.Greater{},
Result: &value.Value{Value: "prize3", Type: value.String},
}
leaf13 := ddt.Node{
ID: 5,
ParentID: 2,
ValueToCompare: &value.Value{Value: int64(30), Type: value.Int64},
Comparer: &compare.Lesser{},
Result: &value.Value{Value: "prize4", Type: value.String},
}
node1 := ddt.Node{
Children: []*ddt.Node{&leaf11, &leaf12, &leaf13},
ID: 2,
ParentID: 0,
ValueToCompare: &value.Value{Value: int64(60), Type: value.Int64},
Comparer: &compare.Lesser{Equal: true},
}
root := ddt.Node{
ID: 0,
ParentID: -1,
Children: []*ddt.Node{&node1, &leaf1},
}
simpleTree, err := ddt.NewTree("simpleTree", &root)
if err != nil {
panic(err)
}
result, err := ddt.ResolveTree(simpleTree,int64(15))
if err != nil {
panic(err)
}
fmt.Println(result.(string))
}
从JSON创建简单树
package main
import (
"encoding/json"
"fmt"
"github.com/sgrodriguez/ddt"
)
func main() {
// 定义空树
tree, err := ddt.NewTree("newTree", &ddt.Node{ID: 0, ParentID: -1})
if err != nil {
panic(err)
}
treeFromJson := []byte(`
{
"nodes":[
{
"preProcessFnName":"",
"id":0,
"parentId":-1
},
{
"preProcessFnName":"",
"id":2,
"parentId":0,
"comparer":{
"type":"lt",
"equal":true
},
"valueToCompare":{
"Value":60,
"Type":"int64"
}
},
{
"preProcessFnName":"",
"id":1,
"parentId":0,
"comparer":{
"type":"gt",
"equal":false
},
"valueToCompare":{
"Value":60,
"Type":"int64"
},
"result":{
"Value":"prize1",
"Type":"string"
}
},
{
"preProcessFnName":"",
"id":3,
"parentId":2,
"comparer":{
"type":"eq"
},
"valueToCompare":{
"Value":30,
"Type":"int64"
},
"result":{
"Value":"prize2",
"Type":"string"
}
},
{
"preProcessFnName":"",
"id":4,
"parentId":2,
"comparer":{
"type":"gt",
"equal":false
},
"valueToCompare":{
"Value":30,
"Type":"int64"
},
"result":{
"Value":"prize3",
"Type":"string"
}
},
{
"preProcessFnName":"",
"id":5,
"parentId":2,
"comparer":{
"type":"lt",
"equal":false
},
"valueToCompare":{
"Value":30,
"Type":"int64"
},
"result":{
"Value":"prize4",
"Type":"string"
}
}
],
"name":"simpleTree"
}`)
err = json.Unmarshal(treeFromJson, tree)
if err != nil {
panic(err)
}
result, err := ddt.ResolveTree(tree, int64(15))
if err != nil {
panic(err)
}
// 结果 prize4
fmt.Println(result.(string))
// 修改树的某些属性,例如prize4的结果
modifiedTree := []byte(`{"nodes":[{"preProcessFnName":"","id":0,"parentId":-1},{"preProcessFnName":"","id":2,"parentId":0,"comparer":{"type":"lt","equal":true},"valueToCompare":{"Value":60,"Type":"int64"}},{"preProcessFnName":"","id":1,"parentId":0,"comparer":{"type":"gt","equal":false},"valueToCompare":{"Value":60,"Type":"int64"},"result":{"Value":"prize1","Type":"string"}},{"preProcessFnName":"","id":3,"parentId":2,"comparer":{"type":"eq"},"valueToCompare":{"Value":30,"Type":"int64"},"result":{"Value":"prize2","Type":"string"}},{"preProcessFnName":"","id":4,"parentId":2,"comparer":{"type":"gt","equal":false},"valueToCompare":{"Value":30,"Type":"int64"},"result":{"Value":"prize3","Type":"string"}},{"preProcessFnName":"","id":5,"parentId":2,"comparer":{"type":"lt","equal":false},"valueToCompare":{"Value":30,"Type":"int64"},"result":{"Value": 420,"Type":"int64"}}],"name":"simpleTree"}`)
err = json.Unmarshal(modifiedTree, tree)
if err != nil {
panic(err)
}
result, err = ddt.ResolveTree(tree, int64(15))
if err != nil {
panic(err)
}
// 结果 420
fmt.Println(result.(int64))
}
概述
节点(Node)
- ID: 节点的ID,根节点必须为0
- ParentID: 父节点ID,根节点必须为-1
- Result: 如果节点是叶子节点并且是树的下一个节点,这就是结果
- Comparer: 比较器
- ValueToCompare: 比较值
- PreProcessFn: 在比较前预处理输入的函数
- PreProcessArgs: 预处理参数
值(Value)
可用于比较、结果和作为PreProcessArgs的基本类型:
- Int
- Int64
- Bool
- String
- Float64
比较器(Comparators)
- Greater (或 Equal)
- Lesser (或 Equal)
- Equal
预处理函数(Pre-Process Functions)
在比较树的下一个级别之前预处理输入的函数:
- CallStructMethod
- GetStructAttribute
更多关于golang动态决策树规则引擎插件库ddt的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang动态决策树规则引擎插件库ddt的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang动态决策树规则引擎插件库DDT使用指南
DDT (Dynamic Decision Tree) 是一个轻量级的Go语言动态决策树规则引擎库,它允许开发者以灵活的方式定义和执行决策逻辑。
安装DDT
go get github.com/yourusername/ddt
基本概念
DDT核心概念包括:
- 规则节点(RuleNode): 包含条件和动作的决策节点
- 条件(Condition): 决定是否执行当前节点的判断逻辑
- 动作(Action): 条件满足时执行的操作
基本使用示例
package main
import (
"fmt"
"github.com/yourusername/ddt"
)
func main() {
// 创建决策树引擎
engine := ddt.NewEngine()
// 定义规则树
tree := &ddt.TreeNode{
Rule: &ddt.RuleNode{
Name: "root",
Condition: func(ctx ddt.Context) bool {
// 检查年龄是否大于18
age, ok := ctx.Get("age").(int)
return ok && age > 18
},
Action: func(ctx ddt.Context) error {
fmt.Println("成年人逻辑处理")
return nil
},
Children: []*ddt.TreeNode{
{
Rule: &ddt.RuleNode{
Name: "vip_check",
Condition: func(ctx ddt.Context) bool {
// 检查是否是VIP
isVIP, ok := ctx.Get("is_vip").(bool)
return ok && isVIP
},
Action: func(ctx ddt.Context) error {
fmt.Println("VIP用户特殊处理")
return nil
},
},
},
},
},
}
// 注册决策树
engine.Register("age_check", tree)
// 执行决策树
ctx := ddt.NewContext()
ctx.Set("age", 25)
ctx.Set("is_vip", true)
err := engine.Execute("age_check", ctx)
if err != nil {
fmt.Printf("执行错误: %v\n", err)
}
}
高级特性
1. 动态加载规则
// 从JSON加载规则
func LoadRulesFromJSON(jsonStr string) (*ddt.TreeNode, error) {
var node ddt.TreeNode
err := json.Unmarshal([]byte(jsonStr), &node)
if err != nil {
return nil, err
}
return &node, nil
}
// 使用示例
jsonRule := `{
"rule": {
"name": "dynamic_rule",
"condition": "ctx.Get('score') > 60",
"action": "ctx.Set('passed', true)"
}
}`
tree, err := LoadRulesFromJSON(jsonRule)
if err != nil {
// 处理错误
}
engine.Register("dynamic_check", tree)
2. 插件系统
// 自定义条件插件
type ScoreCondition struct{}
func (c *ScoreCondition) Eval(ctx ddt.Context) bool {
score, ok := ctx.Get("score").(int)
return ok && score > 80
}
// 自定义动作插件
type NotifyAction struct{}
func (a *NotifyAction) Execute(ctx ddt.Context) error {
email, ok := ctx.Get("email").(string)
if ok {
fmt.Printf("发送通知到: %s\n", email)
}
return nil
}
// 注册插件
engine.RegisterCondition("high_score", &ScoreCondition{})
engine.RegisterAction("send_notify", &NotifyAction{})
// 使用插件
tree := &ddt.TreeNode{
Rule: &ddt.RuleNode{
Name: "plugin_demo",
Condition: "high_score",
Action: "send_notify",
},
}
3. 规则组合
// 组合多个规则
compositeRule := &ddt.TreeNode{
Rule: &ddt.RuleNode{
Name: "composite",
Condition: func(ctx ddt.Context) bool {
return engine.Evaluate("rule1", ctx) &&
engine.Evaluate("rule2", ctx)
},
Action: func(ctx ddt.Context) error {
_ = engine.Execute("action1", ctx)
return engine.Execute("action2", ctx)
},
},
}
最佳实践
- 规则分离: 将业务规则与核心逻辑分离,便于维护
- 性能考虑: 对于高频执行的规则,考虑预编译条件
- 错误处理: 为所有动作添加适当的错误处理
- 日志记录: 记录决策过程和结果,便于调试
性能优化
// 预编译条件表达式
type CompiledCondition struct {
expr *govaluate.EvaluableExpression
}
func (c *CompiledCondition) Eval(ctx ddt.Context) bool {
result, err := c.expr.Evaluate(ctx.ToMap())
if err != nil {
return false
}
return result.(bool)
}
// 使用预编译条件
expr, _ := govaluate.NewEvaluableExpression("age > 18 && score > 60")
engine.RegisterCondition("compiled_cond", &CompiledCondition{expr: expr})
DDT提供了灵活的方式来构建和管理复杂的业务规则,特别适合需要频繁变更决策逻辑的场景。通过合理设计规则树结构,可以大大提高系统的可维护性和扩展性。