golang,go,博客,开源,编程
github.com/bytedance/go-tagexpr/v2
是字节跳动公司开源的一个高性能、灵活的 Go 语言库,主要用于解析和执行标签表达式(Tag Expression)。标签表达式通常用于在结构体、数据记录或其他上下文中动态地评估条件,例如在监控系统、数据过滤和规则引擎等场景中非常有用。
该库允许开发者通过结构体标签定义表达式,表达式可以基于某些条件来决定数据是否满足特定规则。它支持常见的条件运算符(如 =
、>
、<=
等),并且可以扩展自定义函数和操作符,具有很强的灵活性。
and
, or
, not
)和比较运算符(=
, !=
, >
, <
, >=
等)。len()
)和自定义函数。首先,我们从简单的标签表达式开始,展示如何解析和评估表达式。
假设我们有一个标签表达式 "env=prod and method=GET"
,我们希望检查是否有一个标签集合符合这个表达式。
package main
import (
"fmt"
"github.com/bytedance/go-tagexpr/v2"
)
func main() {
// 标签表达式
exprStr := "env=prod and method=GET"
// 解析表达式
expr, err := tagexpr.Parse(exprStr)
if err != nil {
fmt.Printf("Error parsing expression: %v\n", err)
return
}
// 定义标签集合
tags := map[string]string{
"env": "prod",
"method": "GET",
"status": "200",
}
// 评估表达式
result, err := expr.Evaluate(tags)
if err != nil {
fmt.Printf("Error evaluating expression: %v\n", err)
return
}
fmt.Printf("Expression result: %v\n", result) // true
}
exprStr := "env=prod and method=GET"
:这是我们定义的标签表达式,要求标签中同时满足 env=prod
和 method=GET
。tags := map[string]string{...}
:这是标签集合,用于评估表达式。expr.Evaluate(tags)
:评估表达式是否满足给定的标签集合。Expression result: true
你可以通过逻辑运算符(如 and
, or
, not
)和括号来构造更复杂的表达式。例如,下面的表达式要求 env
是 prod
或 staging
,并且 method
不能是 POST
。
package main
import (
"fmt"
"github.com/bytedance/go-tagexpr/v2"
)
func main() {
// 复杂表达式
exprStr := "(env=prod or env=staging) and not method=POST"
// 解析表达式
expr, err := tagexpr.Parse(exprStr)
if err != nil {
fmt.Printf("Error parsing expression: %v\n", err)
return
}
// 定义标签集合
tags := map[string]string{
"env": "prod",
"method": "GET",
}
// 评估表达式
result, err := expr.Evaluate(tags)
if err != nil {
fmt.Printf("Error evaluating expression: %v\n", err)
return
}
fmt.Printf("Expression result: %v\n", result) // true
}
(env=prod or env=staging)
:表示 env
可以是 prod
或 staging
。not method=POST
:表示 method
不能是 POST
。Expression result: true
go-tagexpr
支持常见的比较运算符,如 =
, !=
, >
, <
, >=
, <=
。我们来看看如何使用这些运算符。
package main
import (
"fmt"
"github.com/bytedance/go-tagexpr/v2"
)
func main() {
// 比较运算符
exprStr := "age>=18 and status=active"
// 解析表达式
expr, err := tagexpr.Parse(exprStr)
if err != nil {
fmt.Printf("Error parsing expression: %v\n", err)
return
}
// 定义标签集合
tags := map[string]string{
"age": "20", // 作为字符串处理
"status": "active",
}
// 评估表达式
result, err := expr.Evaluate(tags)
if err != nil {
fmt.Printf("Error evaluating expression: %v\n", err)
return
}
fmt.Printf("Expression result: %v\n", result) // true
}
age>=18
:检查 age
是否大于等于 18。status=active
:检查 status
是否为 active
。Expression result: true
go-tagexpr
提供了一些内置函数,如 len()
用来计算字符串的长度。你还可以自定义函数并在表达式中使用。
len()
函数package main
import (
"fmt"
"github.com/bytedance/go-tagexpr/v2"
)
func main() {
// 使用 len() 函数
exprStr := "len(name) > 3"
// 解析表达式
expr, err := tagexpr.Parse(exprStr)
if err != nil {
fmt.Printf("Error parsing expression: %v\n", err)
return
}
// 定义标签集合
tags := map[string]string{
"name": "John",
}
// 评估表达式
result, err := expr.Evaluate(tags)
if err != nil {
fmt.Printf("Error evaluating expression: %v\n", err)
return
}
fmt.Printf("Expression result: %v\n", result) // true
}
len(name) > 3
:检查标签 name
的值长度是否大于 3。Expression result: true
除了内置函数,go-tagexpr
还支持自定义函数,你可以创建自己的函数并将其注册到库中。
假设我们需要一个自定义函数,判断字符串的首字母是否为大写:
package main
import (
"fmt"
"github.com/bytedance/go-tagexpr/v2"
"strings"
)
// 自定义函数:判断首字母是否大写
func isUpperCase(args ...interface{}) (interface{}, error) {
if len(args) != 1 {
return nil, fmt.Errorf("isUpperCase requires exactly one argument")
}
str, ok := args[0].(string)
if !ok {
return nil, fmt.Errorf("isUpperCase argument must be a string")
}
return strings.ToUpper(string(str[0])) == string(str[0]), nil
}
func main() {
// 注册自定义函数
tagexpr.RegisterFunction("isUpperCase", isUpperCase)
// 使用自定义函数的表达式
exprStr := "isUpperCase(name)"
// 解析表达式
expr, err := tagexpr.Parse(exprStr)
if err != nil {
fmt.Printf("Error parsing expression: %v\n", err)
return
}
// 定义标签集合
tags := map[string]string{
"name": "John",
}
// 评估表达式
result, err := expr.Evaluate(tags)
if err != nil {
fmt.Printf("Error evaluating expression: %v\n", err)
return
}
fmt.Printf("Expression result: %v\n", result) // true
}
isUpperCase(name)
:自定义函数判断标签 name
的首字母是否为大写。Expression result: true
官方例子
package tagexpr_test
import (
"fmt"
tagexpr "github.com/bytedance/go-tagexpr/v2"
)
func Example() {
type T struct {
A int `tagexpr:"$<0||$>=100"`
B string `tagexpr:"len($)>1 && regexp('^\\w*$')"`
C bool `tagexpr:"expr1:(f.g)$>0 && $; expr2:'C must be true when T.f.g>0'"`
d []string `tagexpr:"@:len($)>0 && $[0]=='D'; msg:sprintf('invalid d: %v',$)"`
e map[string]int `tagexpr:"len($)==$['len']"`
e2 map[string]*int `tagexpr:"len($)==$['len']"`
f struct {
g int `tagexpr:"$"`
}
h int `tagexpr:"$>minVal"`
}
vm := tagexpr.New("tagexpr")
t := &T{
A: 107,
B: "abc",
C: true,
d: []string{"x", "y"},
e: map[string]int{"len": 1},
e2: map[string]*int{"len": new(int)},
f: struct {
g int `tagexpr:"$"`
}{1},
h: 10,
}
tagExpr, err := vm.Run(t)
if err != nil {
panic(err)
}
fmt.Println(tagExpr.Eval("A"))
fmt.Println(tagExpr.Eval("B"))
fmt.Println(tagExpr.Eval("C@expr1"))
fmt.Println(tagExpr.Eval("C@expr2"))
if !tagExpr.Eval("d").(bool) {
fmt.Println(tagExpr.Eval("d@msg"))
}
fmt.Println(tagExpr.Eval("e"))
fmt.Println(tagExpr.Eval("e2"))
fmt.Println(tagExpr.Eval("f.g"))
fmt.Println(tagExpr.EvalWithEnv("h", map[string]interface{}{"minVal": 9}))
fmt.Println(tagExpr.EvalWithEnv("h", map[string]interface{}{"minVal": 11}))
// Output:
// true
// true
// true
// C must be true when T.f.g>0
// invalid d: [x y]
// true
// false
// 1
// true
// false
}
在高并发或大数据量场景下,go-tagexpr
提供了几个优化策略:
如果你频繁评估相同的表达式,可以考虑先解析表达式,然后缓存解析结果,避免每次都重新解析。
expr, err := tagexpr.Parse("env=prod and method=GET")
if err != nil {
fmt.Println("Error parsing expression:", err)
return
}
// 之后多次评估
result, err := expr.Evaluate(tags)
评估时,尽量减少传入的标签数量。避免传递多余的标签,提高效率。
tags := map[string]string{
"env": "prod",
"method": "GET",
}
go-tagexpr/v2
是一个功能强大且高效的标签表达式解析库,能够帮助开发者通过标签定义和评估条件表达式。它支持丰富的表达式语法、内置和自定义函数,适用于大规模数据过滤、配置评估等应用场景。通过性能优化手段,它能在高频率和大数据量的场景下保持良好的性能表现。
你可以通过 GitHub 仓库访问更多的源代码、文档和示例:GitHub 仓库。