|
@@ -16,6 +16,7 @@ package function
|
|
|
|
|
|
import (
|
|
|
"fmt"
|
|
|
+ "math"
|
|
|
|
|
|
"github.com/lf-edge/ekuiper/pkg/api"
|
|
|
"github.com/lf-edge/ekuiper/pkg/ast"
|
|
@@ -23,8 +24,13 @@ import (
|
|
|
)
|
|
|
|
|
|
var (
|
|
|
- errorArrayArgumentError = fmt.Errorf("first argument should be array of interface{}")
|
|
|
- errorArrayIndex = fmt.Errorf("index out of range")
|
|
|
+ errorArrayFirstArgumentNotArrayError = fmt.Errorf("first argument should be array of interface{}")
|
|
|
+ errorArrayIndex = fmt.Errorf("index out of range")
|
|
|
+ errorArraySecondArgumentNotArrayError = fmt.Errorf("second argument should be array of interface{}")
|
|
|
+ errorArrayFirstArgumentNotIntError = fmt.Errorf("first argument should be int")
|
|
|
+ errorArraySecondArgumentNotIntError = fmt.Errorf("second argument should be int")
|
|
|
+ errorArrayThirdArgumentNotIntError = fmt.Errorf("third argument should be int")
|
|
|
+ errorArrayContainsNonNumOrBoolValError = fmt.Errorf("array contain elements that are not numeric or Boolean")
|
|
|
)
|
|
|
|
|
|
func registerArrayFunc() {
|
|
@@ -42,7 +48,7 @@ func registerArrayFunc() {
|
|
|
exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
|
|
|
array, ok := args[0].([]interface{})
|
|
|
if !ok {
|
|
|
- return errorArrayArgumentError, false
|
|
|
+ return errorArrayFirstArgumentNotArrayError, false
|
|
|
}
|
|
|
for i, item := range array {
|
|
|
if item == args[1] {
|
|
@@ -92,7 +98,7 @@ func registerArrayFunc() {
|
|
|
exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
|
|
|
array, ok := args[0].([]interface{})
|
|
|
if !ok {
|
|
|
- return errorArrayArgumentError, false
|
|
|
+ return errorArrayFirstArgumentNotArrayError, false
|
|
|
}
|
|
|
for _, item := range array {
|
|
|
if item == args[1] {
|
|
@@ -105,4 +111,313 @@ func registerArrayFunc() {
|
|
|
return ValidateLen(2, len(args))
|
|
|
},
|
|
|
}
|
|
|
+
|
|
|
+ builtins["array_remove"] = builtinFunc{
|
|
|
+ fType: ast.FuncTypeScalar,
|
|
|
+ exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
|
|
|
+ array, ok := args[0].([]interface{})
|
|
|
+ if !ok {
|
|
|
+ return errorArrayFirstArgumentNotArrayError, false
|
|
|
+ }
|
|
|
+
|
|
|
+ index := 0
|
|
|
+
|
|
|
+ for _, item := range array {
|
|
|
+ if item != args[1] {
|
|
|
+ array[index] = item
|
|
|
+ index++
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return array[:index], true
|
|
|
+ },
|
|
|
+ val: func(ctx api.FunctionContext, args []ast.Expr) error {
|
|
|
+ return ValidateLen(2, len(args))
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ builtins["array_last_position"] = builtinFunc{
|
|
|
+ fType: ast.FuncTypeScalar,
|
|
|
+ exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
|
|
|
+ array, ok := args[0].([]interface{})
|
|
|
+ if !ok {
|
|
|
+ return errorArrayFirstArgumentNotArrayError, false
|
|
|
+ }
|
|
|
+
|
|
|
+ lastPos := -1
|
|
|
+ for i := len(array) - 1; i >= 0; i-- {
|
|
|
+ if array[i] == args[1] {
|
|
|
+ lastPos = i
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return lastPos, true
|
|
|
+ },
|
|
|
+ val: func(ctx api.FunctionContext, args []ast.Expr) error {
|
|
|
+ return ValidateLen(2, len(args))
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ builtins["array_contains_any"] = builtinFunc{
|
|
|
+ fType: ast.FuncTypeScalar,
|
|
|
+ exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
|
|
|
+ array1, ok1 := args[0].([]interface{})
|
|
|
+ if !ok1 {
|
|
|
+ return errorArrayFirstArgumentNotArrayError, false
|
|
|
+ }
|
|
|
+ array2, ok2 := args[1].([]interface{})
|
|
|
+ if !ok2 {
|
|
|
+ return errorArraySecondArgumentNotArrayError, false
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, a := range array1 {
|
|
|
+ for _, b := range array2 {
|
|
|
+ if a == b {
|
|
|
+ return true, true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false, true
|
|
|
+ },
|
|
|
+ val: func(ctx api.FunctionContext, args []ast.Expr) error {
|
|
|
+ return ValidateLen(2, len(args))
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ builtins["array_intersect"] = builtinFunc{
|
|
|
+ fType: ast.FuncTypeScalar,
|
|
|
+ exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
|
|
|
+ array1, ok1 := args[0].([]interface{})
|
|
|
+ if !ok1 {
|
|
|
+ return errorArrayFirstArgumentNotArrayError, false
|
|
|
+ }
|
|
|
+ array2, ok2 := args[1].([]interface{})
|
|
|
+ if !ok2 {
|
|
|
+ return errorArraySecondArgumentNotArrayError, false
|
|
|
+ }
|
|
|
+
|
|
|
+ capacity := len(array1)
|
|
|
+ if len(array2) > capacity {
|
|
|
+ capacity = len(array2)
|
|
|
+ }
|
|
|
+
|
|
|
+ intersection := make([]interface{}, 0, capacity)
|
|
|
+ set := make(map[interface{}]bool)
|
|
|
+
|
|
|
+ for _, a := range array1 {
|
|
|
+ set[a] = true
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, b := range array2 {
|
|
|
+ if set[b] {
|
|
|
+ intersection = append(intersection, b)
|
|
|
+ set[b] = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return intersection, true
|
|
|
+ },
|
|
|
+ val: func(ctx api.FunctionContext, args []ast.Expr) error {
|
|
|
+ return ValidateLen(2, len(args))
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ builtins["array_union"] = builtinFunc{
|
|
|
+ fType: ast.FuncTypeScalar,
|
|
|
+ exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
|
|
|
+ array1, ok1 := args[0].([]interface{})
|
|
|
+ if !ok1 {
|
|
|
+ return errorArrayFirstArgumentNotArrayError, false
|
|
|
+ }
|
|
|
+ array2, ok2 := args[1].([]interface{})
|
|
|
+ if !ok2 {
|
|
|
+ return errorArraySecondArgumentNotArrayError, false
|
|
|
+ }
|
|
|
+ union := make([]interface{}, 0, len(array1)+len(array2))
|
|
|
+ set := make(map[interface{}]bool)
|
|
|
+
|
|
|
+ for _, a := range array1 {
|
|
|
+ if !set[a] {
|
|
|
+ union = append(union, a)
|
|
|
+ set[a] = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for _, b := range array2 {
|
|
|
+ if !set[b] {
|
|
|
+ set[b] = true
|
|
|
+ union = append(union, b)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return union, true
|
|
|
+ },
|
|
|
+ val: func(ctx api.FunctionContext, args []ast.Expr) error {
|
|
|
+ return ValidateLen(2, len(args))
|
|
|
+ },
|
|
|
+ }
|
|
|
+ builtins["array_max"] = builtinFunc{
|
|
|
+ fType: ast.FuncTypeScalar,
|
|
|
+ exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
|
|
|
+ array, ok := args[0].([]interface{})
|
|
|
+ if !ok {
|
|
|
+ return errorArrayFirstArgumentNotArrayError, false
|
|
|
+ }
|
|
|
+ var res interface{}
|
|
|
+ var maxVal float64 = math.Inf(-1)
|
|
|
+
|
|
|
+ for _, val := range array {
|
|
|
+ if val == nil {
|
|
|
+ return nil, true
|
|
|
+ }
|
|
|
+ f, err := cast.ToFloat64(val, cast.CONVERT_ALL)
|
|
|
+ if err != nil {
|
|
|
+ return errorArrayContainsNonNumOrBoolValError, false
|
|
|
+ }
|
|
|
+
|
|
|
+ if f > maxVal {
|
|
|
+ maxVal = f
|
|
|
+ res = val
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return res, true
|
|
|
+ },
|
|
|
+ val: func(ctx api.FunctionContext, args []ast.Expr) error {
|
|
|
+ return ValidateLen(1, len(args))
|
|
|
+ },
|
|
|
+ }
|
|
|
+ builtins["array_min"] = builtinFunc{
|
|
|
+ fType: ast.FuncTypeScalar,
|
|
|
+ exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
|
|
|
+ array, ok := args[0].([]interface{})
|
|
|
+ if !ok {
|
|
|
+ return errorArrayFirstArgumentNotArrayError, false
|
|
|
+ }
|
|
|
+ var res interface{}
|
|
|
+ var min float64 = math.Inf(1)
|
|
|
+
|
|
|
+ for _, val := range array {
|
|
|
+ if val == nil {
|
|
|
+ return nil, true
|
|
|
+ }
|
|
|
+
|
|
|
+ f, err := cast.ToFloat64(val, cast.CONVERT_ALL)
|
|
|
+ if err != nil {
|
|
|
+ return errorArrayContainsNonNumOrBoolValError, false
|
|
|
+ }
|
|
|
+
|
|
|
+ if f < min {
|
|
|
+ min = f
|
|
|
+ res = val
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return res, true
|
|
|
+ },
|
|
|
+ val: func(ctx api.FunctionContext, args []ast.Expr) error {
|
|
|
+ return ValidateLen(1, len(args))
|
|
|
+ },
|
|
|
+ }
|
|
|
+ builtins["array_except"] = builtinFunc{
|
|
|
+ fType: ast.FuncTypeScalar,
|
|
|
+ exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
|
|
|
+ array1, ok1 := args[0].([]interface{})
|
|
|
+ if !ok1 {
|
|
|
+ return errorArrayFirstArgumentNotArrayError, false
|
|
|
+ }
|
|
|
+ array2, ok2 := args[1].([]interface{})
|
|
|
+ if !ok2 {
|
|
|
+ return errorArraySecondArgumentNotArrayError, false
|
|
|
+ }
|
|
|
+ except := make([]interface{}, 0, len(array1))
|
|
|
+ set := make(map[interface{}]bool)
|
|
|
+
|
|
|
+ for _, v := range array2 {
|
|
|
+ set[v] = true
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, v := range array1 {
|
|
|
+ if !set[v] {
|
|
|
+ except = append(except, v)
|
|
|
+ set[v] = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return except, true
|
|
|
+ },
|
|
|
+ val: func(ctx api.FunctionContext, args []ast.Expr) error {
|
|
|
+ return ValidateLen(2, len(args))
|
|
|
+ },
|
|
|
+ }
|
|
|
+ builtins["repeat"] = builtinFunc{
|
|
|
+ fType: ast.FuncTypeScalar,
|
|
|
+ exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
|
|
|
+ elemt, ok := args[0].(interface{})
|
|
|
+ if !ok {
|
|
|
+ return errorArrayFirstArgumentNotArrayError, false
|
|
|
+ }
|
|
|
+ count, ok := args[1].(int)
|
|
|
+ if !ok {
|
|
|
+ return errorArraySecondArgumentNotIntError, false
|
|
|
+ }
|
|
|
+
|
|
|
+ arr := make([]interface{}, count)
|
|
|
+ for i := range arr {
|
|
|
+ arr[i] = elemt
|
|
|
+ }
|
|
|
+
|
|
|
+ return arr, true
|
|
|
+ },
|
|
|
+ val: func(ctx api.FunctionContext, args []ast.Expr) error {
|
|
|
+ return ValidateLen(2, len(args))
|
|
|
+ },
|
|
|
+ }
|
|
|
+ builtins["sequence"] = builtinFunc{
|
|
|
+ fType: ast.FuncTypeScalar,
|
|
|
+ exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
|
|
|
+ var step, start, stop int
|
|
|
+ var ok bool
|
|
|
+ start, ok = args[0].(int)
|
|
|
+ if !ok {
|
|
|
+ return errorArrayFirstArgumentNotIntError, false
|
|
|
+ }
|
|
|
+ stop, ok = args[1].(int)
|
|
|
+ if !ok {
|
|
|
+ return errorArraySecondArgumentNotIntError, false
|
|
|
+ }
|
|
|
+ if len(args) == 3 {
|
|
|
+ step, ok = args[2].(int)
|
|
|
+ if !ok {
|
|
|
+ return errorArrayThirdArgumentNotIntError, false
|
|
|
+ }
|
|
|
+
|
|
|
+ if step == 0 {
|
|
|
+ return fmt.Errorf("invalid step: should not be zero"), false
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ if start < stop {
|
|
|
+ step = 1
|
|
|
+ } else {
|
|
|
+ step = -1
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ n := (stop-start)/step + 1
|
|
|
+
|
|
|
+ arr := make([]interface{}, n)
|
|
|
+ for i := range arr {
|
|
|
+ arr[i] = start + i*step
|
|
|
+ }
|
|
|
+
|
|
|
+ return arr, true
|
|
|
+ },
|
|
|
+ val: func(ctx api.FunctionContext, args []ast.Expr) error {
|
|
|
+ if err := ValidateLen(2, len(args)); err != nil {
|
|
|
+ if err := ValidateLen(3, len(args)); err != nil {
|
|
|
+ return fmt.Errorf("Expect two or three arguments but found %d.", len(args))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ },
|
|
|
+ }
|
|
|
}
|