diff --git a/docs/en_US/sqls/functions/object_functions.md b/docs/en_US/sqls/functions/object_functions.md index 22a1fa148c..7d884270d6 100644 --- a/docs/en_US/sqls/functions/object_functions.md +++ b/docs/en_US/sqls/functions/object_functions.md @@ -126,3 +126,21 @@ result: ```sql {"a":1, "b":2} ``` + +## OBJECT_CONCAT + +```text +object_concat(obj1, obj2, ...) +``` + +This function concatenates the input objects and returns a new object. It requires a minimum of two input objects. In cases where there are duplicate attribute names among the input objects, the attribute from the last relevant object in the input list is selected and copied to the output object. To illustrate, here's an example: + +```sql +object_concat({"a": 1}, {"b": 2}, {"b": 3}) +``` + +result: + +```sql +{"a":1, "b":3} +``` diff --git a/docs/zh_CN/sqls/functions/object_functions.md b/docs/zh_CN/sqls/functions/object_functions.md index a93410132a..9fed29c10e 100644 --- a/docs/zh_CN/sqls/functions/object_functions.md +++ b/docs/zh_CN/sqls/functions/object_functions.md @@ -110,3 +110,21 @@ object_construct("a", 1, "b", 2) ```sql {"a":1, "b":2} ``` + +## OBJECT_CONCAT + +```text +object_concat(obj1, obj2, ...) +``` + +该函数是一个连接输入对象并返回新对象的函数。该函数至少需要两个输入对象作为参数。当输入对象中存在相同属性名称时,函数将选择输入列表中最后一个相关对象的属性,并将其复制到输出对象中。以下是一个示例: + +```sql +object_concat({"a": 1}, {"b": 2}, {"b": 3}) +``` + +得到如下结果: + +```sql +{"a":1, "b":3} +``` diff --git a/internal/binder/function/funcs_obj.go b/internal/binder/function/funcs_obj.go index b121005abb..0a3ff0a013 100644 --- a/internal/binder/function/funcs_obj.go +++ b/internal/binder/function/funcs_obj.go @@ -135,4 +135,24 @@ func registerObjectFunc() { val: ValidateOneArg, check: returnNilIfHasAnyNil, } + builtins["object_concat"] = builtinFunc{ + fType: ast.FuncTypeScalar, + exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) { + res := make(map[string]interface{}) + for i, arg := range args { + arg, ok := arg.(map[string]interface{}) + if !ok { + return fmt.Errorf("the argument should be map[string]interface{}, got %v", args[i]), false + } + for k, v := range arg { + 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 fafefc2f3d..c2b5d6c1ac 100644 --- a/internal/binder/function/funcs_obj_test.go +++ b/internal/binder/function/funcs_obj_test.go @@ -210,6 +210,47 @@ func TestObjectFunctions(t *testing.T) { }, result: fmt.Errorf("second argument should be string"), }, + { + name: "object_concat", + args: []interface{}{ + map[string]interface{}{ + "a": 1, + "b": 2, + }, + map[string]interface{}{ + "b": 3, + "c": 4, + }, + map[string]interface{}{ + "a": 2, + "d": 1, + }, + }, + result: map[string]interface{}{ + "a": 2, + "b": 3, + "c": 4, + "d": 1, + }, + }, + { + name: "object_concat", + args: []interface{}{ + map[string]interface{}{ + "a": 1, + "b": 2, + }, + map[string]interface{}{ + "b": 3, + "c": 4, + }, + []interface{}{ + 1, + 2, + }, + }, + result: fmt.Errorf("the argument should be map[string]interface{}, got %v", []interface{}{1, 2}), + }, } for i, tt := range tests { f, ok := builtins[tt.name]