Browse Source

feat(func): support func erase (#2179)

Signed-off-by: hantmac <hantmac@outlook.com>
Jeremy 1 year ago
parent
commit
35815fa8db

+ 18 - 0
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'}
+```

+ 18 - 0
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'}
+```

+ 54 - 0
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,
+	}
 }

+ 46 - 0
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]