Implement ARRAY_CONCAT, ARRAY_LENGTH, ARRAY_SLICE, SetIntersect, SetUnion functions

This commit is contained in:
Pijus Kamandulis
2024-02-25 00:25:51 +02:00
parent b29608e4c8
commit 1c5e5ce85d
7 changed files with 1553 additions and 510 deletions

View File

@@ -0,0 +1,131 @@
package memoryexecutor
import (
"fmt"
"reflect"
"github.com/pikami/cosmium/parsers"
)
func array_Concat(arguments []interface{}, queryParameters map[string]interface{}, row RowType) []interface{} {
var result []interface{}
for _, arg := range arguments {
array := parseArray(arg, queryParameters, row)
result = append(result, array...)
}
return result
}
func array_Length(arguments []interface{}, queryParameters map[string]interface{}, row RowType) int {
array := parseArray(arguments[0], queryParameters, row)
if array == nil {
return 0
}
return len(array)
}
func array_Slice(arguments []interface{}, queryParameters map[string]interface{}, row RowType) []interface{} {
var ok bool
var start int
var length int
array := parseArray(arguments[0], queryParameters, row)
startEx := getFieldValue(arguments[1].(parsers.SelectItem), queryParameters, row)
if arguments[2] != nil {
lengthEx := getFieldValue(arguments[2].(parsers.SelectItem), queryParameters, row)
if length, ok = lengthEx.(int); !ok {
fmt.Println("array_Slice - got length parameters of wrong type")
return []interface{}{}
}
}
if start, ok = startEx.(int); !ok {
fmt.Println("array_Slice - got start parameters of wrong type")
return []interface{}{}
}
if start < 0 {
start = len(array) + start
}
if start < 0 {
start = 0
}
if array == nil || start >= len(array) {
return []interface{}{}
}
end := start + length
if end > len(array) {
end = len(array)
}
return array[start:end]
}
func set_Intersect(arguments []interface{}, queryParameters map[string]interface{}, row RowType) []interface{} {
set1 := parseArray(arguments[0], queryParameters, row)
set2 := parseArray(arguments[1], queryParameters, row)
intersection := make(map[interface{}]struct{})
if set1 == nil || set2 == nil {
return []interface{}{}
}
for _, item := range set1 {
intersection[item] = struct{}{}
}
var result []interface{}
for _, item := range set2 {
if _, exists := intersection[item]; exists {
result = append(result, item)
}
}
return result
}
func set_Union(arguments []interface{}, queryParameters map[string]interface{}, row RowType) []interface{} {
set1 := parseArray(arguments[0], queryParameters, row)
set2 := parseArray(arguments[1], queryParameters, row)
var result []interface{}
union := make(map[interface{}]struct{})
for _, item := range set1 {
if _, ok := union[item]; !ok {
union[item] = struct{}{}
result = append(result, item)
}
}
for _, item := range set2 {
if _, ok := union[item]; !ok {
union[item] = struct{}{}
result = append(result, item)
}
}
return result
}
func parseArray(argument interface{}, queryParameters map[string]interface{}, row RowType) []interface{} {
exItem := argument.(parsers.SelectItem)
ex := getFieldValue(exItem, queryParameters, row)
arrValue := reflect.ValueOf(ex)
if arrValue.Kind() != reflect.Slice {
fmt.Println("parseArray got parameters of wrong type")
return nil
}
result := make([]interface{}, arrValue.Len())
for i := 0; i < arrValue.Len(); i++ {
result[i] = arrValue.Index(i).Interface()
}
return result
}

View File

@@ -0,0 +1,212 @@
package memoryexecutor_test
import (
"testing"
"github.com/pikami/cosmium/parsers"
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
)
func Test_Execute_ArrayFunctions(t *testing.T) {
mockData := []memoryexecutor.RowType{
map[string]interface{}{"id": "123", "arr1": []int{1, 2, 3}, "arr2": []int{3, 4, 5}},
map[string]interface{}{"id": "456", "arr1": []int{4, 5, 6}, "arr2": []int{5, 6, 7, 8}},
map[string]interface{}{"id": "789", "arr1": []int{7, 8, 9}, "arr2": []int{7, 8, 9, 10, 11}},
}
t.Run("Should execute function CONCAT()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "Concat",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayConcat,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "arr1"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Path: []string{"c", "arr2"},
Type: parsers.SelectItemTypeField,
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "123", "Concat": []interface{}{1, 2, 3, 3, 4, 5}},
map[string]interface{}{"id": "456", "Concat": []interface{}{4, 5, 6, 5, 6, 7, 8}},
map[string]interface{}{"id": "789", "Concat": []interface{}{7, 8, 9, 7, 8, 9, 10, 11}},
},
)
})
t.Run("Should execute function ARRAY_LENGTH()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "Length",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayLength,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "arr2"},
Type: parsers.SelectItemTypeField,
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "123", "Length": 3},
map[string]interface{}{"id": "456", "Length": 4},
map[string]interface{}{"id": "789", "Length": 5},
},
)
})
t.Run("Should execute function ARRAY_SLICE()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "Slice",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArraySlice,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "arr2"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 1,
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 2,
},
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "123", "Slice": []interface{}{4, 5}},
map[string]interface{}{"id": "456", "Slice": []interface{}{6, 7}},
map[string]interface{}{"id": "789", "Slice": []interface{}{8, 9}},
},
)
})
t.Run("Should execute function SET_INTERSECT()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "Intersection",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallSetIntersect,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "arr1"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Path: []string{"c", "arr2"},
Type: parsers.SelectItemTypeField,
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "123", "Intersection": []interface{}{3}},
map[string]interface{}{"id": "456", "Intersection": []interface{}{5, 6}},
map[string]interface{}{"id": "789", "Intersection": []interface{}{7, 8, 9}},
},
)
})
t.Run("Should execute function SET_UNION()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
{
Alias: "Union",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallSetUnion,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "arr1"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Path: []string{"c", "arr2"},
Type: parsers.SelectItemTypeField,
},
},
},
},
},
Table: parsers.Table{Value: "c"},
},
mockData,
[]memoryexecutor.RowType{
map[string]interface{}{"id": "123", "Union": []interface{}{1, 2, 3, 4, 5}},
map[string]interface{}{"id": "456", "Union": []interface{}{4, 5, 6, 7, 8}},
map[string]interface{}{"id": "789", "Union": []interface{}{7, 8, 9, 10, 11}},
},
)
})
}

View File

@@ -236,6 +236,17 @@ func getFieldValue(field parsers.SelectItem, queryParameters map[string]interfac
case parsers.FunctionCallIsString:
return typeChecking_IsString(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallArrayConcat:
return array_Concat(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallArrayLength:
return array_Length(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallArraySlice:
return array_Slice(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallSetIntersect:
return set_Intersect(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallSetUnion:
return set_Union(typedValue.Arguments, queryParameters, row)
case parsers.FunctionCallIn:
return misc_In(typedValue.Arguments, queryParameters, row)
}