From 790e6c615df7bec82d2fc781c2f59612add9a73a Mon Sep 17 00:00:00 2001 From: rulego-team Date: Thu, 7 Aug 2025 19:20:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=87=BD=E6=95=B0=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=88=AB=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- functions/base.go | 136 +++++++++++++++++++--------------- functions/functions_math.go | 13 ++-- functions/functions_string.go | 29 ++++++-- functions/registry.go | 25 ++++++- 4 files changed, 132 insertions(+), 71 deletions(-) diff --git a/functions/base.go b/functions/base.go index 6847ffd..dbd7e3f 100644 --- a/functions/base.go +++ b/functions/base.go @@ -1,58 +1,78 @@ -package functions - -import ( - "fmt" -) - -// BaseFunction provides basic function implementation with common functionality -type BaseFunction struct { - name string - fnType FunctionType - category string - description string - minArgs int - maxArgs int // -1 means unlimited -} - -// NewBaseFunction creates a new base function -func NewBaseFunction(name string, fnType FunctionType, category, description string, minArgs, maxArgs int) *BaseFunction { - return &BaseFunction{ - name: name, - fnType: fnType, - category: category, - description: description, - minArgs: minArgs, - maxArgs: maxArgs, - } -} - -func (bf *BaseFunction) GetName() string { - return bf.name -} - -func (bf *BaseFunction) GetType() FunctionType { - return bf.fnType -} - -func (bf *BaseFunction) GetCategory() string { - return bf.category -} - -func (bf *BaseFunction) GetDescription() string { - return bf.description -} - -// ValidateArgCount validates the number of arguments -func (bf *BaseFunction) ValidateArgCount(args []interface{}) error { - argCount := len(args) - - if argCount < bf.minArgs { - return fmt.Errorf("function %s requires at least %d arguments, got %d", bf.name, bf.minArgs, argCount) - } - - if bf.maxArgs != -1 && argCount > bf.maxArgs { - return fmt.Errorf("function %s accepts at most %d arguments, got %d", bf.name, bf.maxArgs, argCount) - } - - return nil -} +package functions + +import ( + "fmt" +) + +// BaseFunction provides basic function implementation with common functionality +type BaseFunction struct { + name string + fnType FunctionType + category string + description string + minArgs int + maxArgs int // -1 means unlimited + aliases []string // function aliases +} + +// NewBaseFunction creates a new base function +func NewBaseFunction(name string, fnType FunctionType, category, description string, minArgs, maxArgs int) *BaseFunction { + return &BaseFunction{ + name: name, + fnType: fnType, + category: category, + description: description, + minArgs: minArgs, + maxArgs: maxArgs, + aliases: []string{}, + } +} + +// NewBaseFunctionWithAliases creates base function with aliases +func NewBaseFunctionWithAliases(name string, fnType FunctionType, category, description string, minArgs, maxArgs int, aliases []string) *BaseFunction { + return &BaseFunction{ + name: name, + fnType: fnType, + category: category, + description: description, + minArgs: minArgs, + maxArgs: maxArgs, + aliases: aliases, + } +} + +func (bf *BaseFunction) GetName() string { + return bf.name +} + +func (bf *BaseFunction) GetType() FunctionType { + return bf.fnType +} + +func (bf *BaseFunction) GetCategory() string { + return bf.category +} + +func (bf *BaseFunction) GetDescription() string { + return bf.description +} + +// GetAliases returns function alias list +func (bf *BaseFunction) GetAliases() []string { + return bf.aliases +} + +// ValidateArgCount validates the number of arguments +func (bf *BaseFunction) ValidateArgCount(args []interface{}) error { + argCount := len(args) + + if argCount < bf.minArgs { + return fmt.Errorf("function %s requires at least %d arguments, got %d", bf.name, bf.minArgs, argCount) + } + + if bf.maxArgs != -1 && argCount > bf.maxArgs { + return fmt.Errorf("function %s accepts at most %d arguments, got %d", bf.name, bf.maxArgs, argCount) + } + + return nil +} diff --git a/functions/functions_math.go b/functions/functions_math.go index 9acc3ad..f067bf8 100644 --- a/functions/functions_math.go +++ b/functions/functions_math.go @@ -2,10 +2,11 @@ package functions import ( "fmt" - "github.com/rulego/streamsql/utils/cast" "math" "math/rand" "time" + + "github.com/rulego/streamsql/utils/cast" ) // AbsFunction calculates absolute value @@ -270,7 +271,7 @@ type CeilingFunction struct { func NewCeilingFunction() *CeilingFunction { return &CeilingFunction{ - BaseFunction: NewBaseFunction("ceiling", TypeMath, "数学函数", "向上取整", 1, 1), + BaseFunction: NewBaseFunctionWithAliases("ceiling", TypeMath, "数学函数", "向上取整", 1, 1, []string{"ceil"}), } } @@ -404,14 +405,14 @@ func (f *LnFunction) Execute(ctx *FunctionContext, args []interface{}) (interfac return math.Log(val), nil } -// LogFunction 自然对数函数 (log的别名) +// LogFunction 以10为底的对数函数 (log的别名) type LogFunction struct { *BaseFunction } func NewLogFunction() *LogFunction { return &LogFunction{ - BaseFunction: NewBaseFunction("log", TypeMath, "数学函数", "计算自然对数", 1, 1), + BaseFunction: NewBaseFunction("log", TypeMath, "数学函数", "计算以10为底的对数", 1, 1), } } @@ -427,7 +428,7 @@ func (f *LogFunction) Execute(ctx *FunctionContext, args []interface{}) (interfa if val <= 0 { return nil, fmt.Errorf("log: value must be positive") } - return math.Log(val), nil + return math.Log10(val), nil } // Log10Function 以10为底的对数函数 @@ -695,7 +696,7 @@ type PowerFunction struct { func NewPowerFunction() *PowerFunction { return &PowerFunction{ - BaseFunction: NewBaseFunction("power", TypeMath, "数学函数", "计算x的y次幂", 2, 2), + BaseFunction: NewBaseFunctionWithAliases("power", TypeMath, "数学函数", "计算x的y次幂", 2, 2, []string{"pow"}), } } diff --git a/functions/functions_string.go b/functions/functions_string.go index dac3f49..ca4e545 100644 --- a/functions/functions_string.go +++ b/functions/functions_string.go @@ -2,6 +2,7 @@ package functions import ( "fmt" + "reflect" "regexp" "strings" @@ -42,7 +43,7 @@ type LengthFunction struct { func NewLengthFunction() *LengthFunction { return &LengthFunction{ - BaseFunction: NewBaseFunction("length", TypeString, "string", "Get string length", 1, 1), + BaseFunction: NewBaseFunctionWithAliases("length", TypeString, "string", "Get length of string or array", 1, 1, []string{"len"}), } } @@ -50,12 +51,30 @@ func (f *LengthFunction) Validate(args []interface{}) error { return f.ValidateArgCount(args) } +// Execute calculates the length of a string or array. +// Supports strings, arrays, slices, etc., using Go's standard len() function. func (f *LengthFunction) Execute(ctx *FunctionContext, args []interface{}) (interface{}, error) { - str, err := cast.ToStringE(args[0]) - if err != nil { - return nil, err + arg := args[0] + + v := reflect.ValueOf(arg) + var length int + switch v.Kind() { + case reflect.String: + length = len(v.String()) + case reflect.Array, reflect.Slice: + length = v.Len() + case reflect.Map: + length = v.Len() + case reflect.Chan: + length = v.Len() + default: + str, err := cast.ToStringE(arg) + if err != nil { + return nil, fmt.Errorf("unsupported type for len function: %T", arg) + } + length = len(str) } - return int64(len(str)), nil + return length, nil } // UpperFunction converts string to uppercase diff --git a/functions/registry.go b/functions/registry.go index b5b3214..8a8d29b 100644 --- a/functions/registry.go +++ b/functions/registry.go @@ -53,6 +53,8 @@ type Function interface { GetType() FunctionType // GetCategory returns the function category GetCategory() string + // GetAliases returns the function aliases + GetAliases() []string // Validate validates the arguments Validate(args []interface{}) error // Execute executes the function @@ -80,6 +82,7 @@ func NewFunctionRegistry() *FunctionRegistry { } // Register registers a function +// 注册函数及其别名到注册表中 func (r *FunctionRegistry) Register(fn Function) error { r.mu.Lock() defer r.mu.Unlock() @@ -91,7 +94,18 @@ func (r *FunctionRegistry) Register(fn Function) error { return fmt.Errorf("function %s already registered", name) } + // 注册主函数名 r.functions[name] = fn + + // 注册所有别名 + for _, alias := range fn.GetAliases() { + alias = strings.ToLower(alias) + if _, exists := r.functions[alias]; exists { + return fmt.Errorf("function alias %s already registered", alias) + } + r.functions[alias] = fn + } + r.categories[fn.GetType()] = append(r.categories[fn.GetType()], fn) // Register aggregator adapter if fn.GetType() == TypeAggregation { @@ -132,6 +146,7 @@ func (r *FunctionRegistry) ListAll() map[string]Function { } // Unregister removes a function +// 从注册表中移除函数及其所有别名 func (r *FunctionRegistry) Unregister(name string) bool { r.mu.Lock() defer r.mu.Unlock() @@ -142,13 +157,19 @@ func (r *FunctionRegistry) Unregister(name string) bool { return false } - delete(r.functions, name) + // 删除主函数名 + delete(r.functions, strings.ToLower(fn.GetName())) + + // 删除所有别名 + for _, alias := range fn.GetAliases() { + delete(r.functions, strings.ToLower(alias)) + } // Remove from categories fnType := fn.GetType() if funcs, ok := r.categories[fnType]; ok { for i, f := range funcs { - if strings.ToLower(f.GetName()) == name { + if strings.ToLower(f.GetName()) == strings.ToLower(fn.GetName()) { r.categories[fnType] = append(funcs[:i], funcs[i+1:]...) break }