Pārlūkot izejas kodu

feat: support function array_sort (#2176)

Signed-off-by: hantmac <hantmac@outlook.com>
Jeremy 1 gadu atpakaļ
vecāks
revīzija
44a7afbb99

+ 18 - 0
docs/en_US/sqls/functions/array_functions.md

@@ -183,3 +183,21 @@ array_concat(array1, array2, ...)
 ```
 
 Returns the concatenation of the input arrays, this function does not modify the existing arrays, but returns new one.
+
+## ARRAY_SORT
+
+```text
+array_sort(array)
+```
+
+Returns a sorted copy of the input array.
+
+```sql
+array_sort([3, 2, "b", "a"])
+```
+
+Result:
+
+```sql
+[2, 3, "a", "b"]
+```

+ 18 - 0
docs/zh_CN/sqls/functions/array_functions.md

@@ -182,3 +182,21 @@ array_concat(array1, array2, ...)
 ```
 
 用于合并两个或多个数组。此函数不会更改现有数组,而是返回一个新的数组。
+
+## ARRAY_SORT
+
+```text
+array_sort(array)
+```
+
+返回输入数组的排序副本。
+
+```sql
+array_sort([3, 2, "b", "a"])
+```
+
+结果:
+
+```sql
+[2, 3, "a", "b"]
+```

+ 37 - 0
internal/binder/function/funcs_array.go

@@ -18,6 +18,7 @@ import (
 	"fmt"
 	"math/rand"
 	"reflect"
+	"sort"
 	"strings"
 
 	"github.com/lf-edge/ekuiper/pkg/api"
@@ -622,6 +623,42 @@ func registerArrayFunc() {
 		},
 		check: returnNilIfHasAnyNil,
 	}
+	builtins["array_sort"] = builtinFunc{
+		fType: ast.FuncTypeScalar,
+		exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
+			t := reflect.TypeOf(args)
+			k := t.Kind()
+			if k != reflect.Slice && k != reflect.Array {
+				return errorArrayNotArrayElementError, false
+			}
+			inValue := reflect.ValueOf(args)
+			inLen := inValue.Len()
+
+			if inLen <= 1 {
+				return args, true
+			}
+
+			sliceType := reflect.SliceOf(inValue.Index(0).Type())
+			outValue := reflect.MakeSlice(sliceType, inLen, inLen)
+
+			for i := 0; i < inLen; i++ {
+				outValue.Index(i).Set(inValue.Index(i))
+			}
+
+			// Sort the slice if it contains elements that are comparable
+			if inLen > 1 {
+				sort.Slice(outValue.Interface(), func(i, j int) bool {
+					return fmt.Sprint(outValue.Index(i).Interface()) < fmt.Sprint(outValue.Index(j).Interface())
+				})
+			}
+
+			return outValue.Interface(), true
+		},
+		val: func(ctx api.FunctionContext, args []ast.Expr) error {
+			return ValidateAtLeast(1, len(args))
+		},
+		check: returnNilIfHasAnyNil,
+	}
 	builtins["array_concat"] = builtinFunc{
 		fType: ast.FuncTypeScalar,
 		exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {

+ 47 - 0
internal/binder/function/funcs_array_test.go

@@ -773,6 +773,53 @@ func TestArrayShuffle(t *testing.T) {
 	}
 }
 
+func TestArraySort(t *testing.T) {
+	contextLogger := conf.Log.WithField("rule", "testExec")
+	ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
+	tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
+	fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
+	tests := []struct {
+		name   string
+		args   []interface{}
+		result []interface{}
+	}{
+		{
+			name: "array_sort",
+			args: []interface{}{3, 2, 1},
+
+			result: []interface{}{1, 2, 3},
+		},
+		{
+			name: "array_sort",
+			args: []interface{}{3, 1.6, -0.83},
+
+			result: []interface{}{-0.83, 1.6, 3},
+		},
+		{
+			name: "array_sort",
+			args: []interface{}{"abc", 3, "def", 1.6, -0.83},
+
+			result: []interface{}{-0.83, 1.6, 3, "abc", "def"},
+		},
+	}
+
+	for i, tt := range tests {
+		f, ok := builtins[tt.name]
+		if !ok {
+			t.Fatal(fmt.Sprintf("builtin %v not found", tt.name))
+		}
+		result, _ := f.exec(fctx, tt.args)
+		flag := false
+		if reflect.DeepEqual(result, tt.result) {
+			flag = true
+		}
+
+		if !flag {
+			t.Errorf("%d result mismatch,\ngot:\t%v \nwant in:\t%v", i, result, tt.result)
+		}
+	}
+}
+
 func TestArrayFuncNil(t *testing.T) {
 	contextLogger := conf.Log.WithField("rule", "testExec")
 	ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)