From 35815fa8db464f54a13bf0b48345016716665052 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 15 Aug 2023 11:46:26 +0800 Subject: [PATCH] feat(func): support func erase (#2179) Signed-off-by: hantmac --- docs/en_US/sqls/functions/object_functions.md | 18 +++++++ docs/zh_CN/sqls/functions/object_functions.md | 18 +++++++ internal/binder/function/funcs_obj.go | 54 +++++++++++++++++++ internal/binder/function/funcs_obj_test.go | 46 ++++++++++++++++ 4 files changed, 136 insertions(+) diff --git a/docs/en_US/sqls/functions/object_functions.md b/docs/en_US/sqls/functions/object_functions.md index 7d884270d6..22d1c89006 100644 --- a/docs/en_US/sqls/functions/object_functions.md +++ b/docs/en_US/sqls/functions/object_functions.md @@ -144,3 +144,21 @@ result: ```sql {"a":1, "b":3} ``` + +## ERASE + +```text +erase(obj, k) +``` + +If k is a string, return a new object where the key k is erased. If k is an array of strings, return a new object where the keys in k are erased. + +```sql +erase({"baz": [1, 2, 3], "bar": 'hello world',"foo":'emq'}, 'foo') +``` + +result: + +```sql +{"baz": [1, 2, 3], "bar": 'hello world'} +``` diff --git a/docs/zh_CN/sqls/functions/object_functions.md b/docs/zh_CN/sqls/functions/object_functions.md index 9fed29c10e..d9a5f8c656 100644 --- a/docs/zh_CN/sqls/functions/object_functions.md +++ b/docs/zh_CN/sqls/functions/object_functions.md @@ -128,3 +128,21 @@ object_concat({"a": 1}, {"b": 2}, {"b": 3}) ```sql {"a":1, "b":3} ``` + +## ERASE + +```text +erase(obj, k) +``` + +如果 k 是一个字符串,则返回一个新对象,其中键 k 被删除。如果 k 是一个字符串数组,则返回一个新对象,其中包含 k 中的键被删除。 + +```sql +erase({"baz": [1, 2, 3], "bar": 'hello world',"foo":'emq'}, 'foo') +``` + +得到如下结果: + +```sql +{"baz": [1, 2, 3], "bar": 'hello world'} +``` diff --git a/internal/binder/function/funcs_obj.go b/internal/binder/function/funcs_obj.go index 0a3ff0a013..3a27f64e9d 100644 --- a/internal/binder/function/funcs_obj.go +++ b/internal/binder/function/funcs_obj.go @@ -16,9 +16,11 @@ package function import ( "fmt" + "reflect" "github.com/lf-edge/ekuiper/pkg/api" "github.com/lf-edge/ekuiper/pkg/ast" + "github.com/lf-edge/ekuiper/pkg/cast" ) func registerObjectFunc() { @@ -155,4 +157,56 @@ func registerObjectFunc() { }, check: returnNilIfHasAnyNil, } + builtins["erase"] = builtinFunc{ + fType: ast.FuncTypeScalar, + exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) { + contains := func(array []string, target string) bool { + for _, v := range array { + if target == v { + return true + } + } + return false + } + if len(args) != 2 { + return fmt.Errorf("the argument number should be 2, got %v", len(args)), false + } + res := make(map[string]interface{}) + argMap, ok := args[0].(map[string]interface{}) + if !ok { + return fmt.Errorf("the first argument should be map[string]interface{}, got %v", args[0]), false + } + eraseArray := make([]string, 0) + v := reflect.ValueOf(args[1]) + switch v.Kind() { + case reflect.Slice: + array, err := cast.ToStringSlice(args[1], cast.CONVERT_ALL) + if err != nil { + return err, false + } + eraseArray = append(eraseArray, array...) + case reflect.String: + str := args[1].(string) + for k, v := range argMap { + if k != str { + res[k] = v + } + } + return res, true + default: + return fmt.Errorf("the augument should be slice or string"), false + } + for k, v := range argMap { + if !contains(eraseArray, k) { + res[k] = v + } + } + + return res, true + }, + val: func(_ api.FunctionContext, args []ast.Expr) error { + return ValidateAtLeast(2, len(args)) + }, + check: returnNilIfHasAnyNil, + } } diff --git a/internal/binder/function/funcs_obj_test.go b/internal/binder/function/funcs_obj_test.go index c2b5d6c1ac..7df03daa31 100644 --- a/internal/binder/function/funcs_obj_test.go +++ b/internal/binder/function/funcs_obj_test.go @@ -251,6 +251,52 @@ func TestObjectFunctions(t *testing.T) { }, result: fmt.Errorf("the argument should be map[string]interface{}, got %v", []interface{}{1, 2}), }, + { + name: "erase", + args: []interface{}{ + map[string]interface{}{ + "a": 1, + "b": 2, + }, + "a", + }, + result: map[string]interface{}{ + "b": 2, + }, + }, + { + name: "erase", + args: []interface{}{ + map[string]interface{}{ + "a": 1, + "b": 2, + "c": 3, + }, + []string{ + "a", + "b", + }, + }, + result: map[string]interface{}{ + "c": 3, + }, + }, + { + name: "erase", + args: []interface{}{ + map[string]interface{}{ + "a": 1, + "b": 2, + "c": 3, + }, + []string{ + "a", + "b", + }, + "c", + }, + result: fmt.Errorf("the argument number should be 2, got 3"), + }, } for i, tt := range tests { f, ok := builtins[tt.name]