Compare commits

...

2 Commits

Author SHA1 Message Date
Pijus Kamandulis
69b76c1c3e Simplify constant initialization in unit tests 2024-12-26 20:43:57 +02:00
Pijus Kamandulis
8e3db3e44d Added support for 'ARRAY_CONTAINS', 'ARRAY_CONTAINS_ANY' and 'ARRAY_CONTAINS_ALL' functions 2024-12-26 20:27:59 +02:00
15 changed files with 2097 additions and 1496 deletions

View File

@ -65,9 +65,9 @@ Cosmium strives to support the core features of Cosmos DB, including:
| Function | Implemented |
| ------------------ | ----------- |
| ARRAY_CONCAT | Yes |
| ARRAY_CONTAINS | No |
| ARRAY_CONTAINS_ANY | No |
| ARRAY_CONTAINS_ALL | No |
| ARRAY_CONTAINS | Yes |
| ARRAY_CONTAINS_ANY | Yes |
| ARRAY_CONTAINS_ALL | Yes |
| ARRAY_LENGTH | Yes |
| ARRAY_SLICE | Yes |
| CHOOSE | No |

View File

@ -123,11 +123,14 @@ const (
FunctionCallIsPrimitive FunctionCallType = "IsPrimitive"
FunctionCallIsString FunctionCallType = "IsString"
FunctionCallArrayConcat FunctionCallType = "ArrayConcat"
FunctionCallArrayLength FunctionCallType = "ArrayLength"
FunctionCallArraySlice FunctionCallType = "ArraySlice"
FunctionCallSetIntersect FunctionCallType = "SetIntersect"
FunctionCallSetUnion FunctionCallType = "SetUnion"
FunctionCallArrayConcat FunctionCallType = "ArrayConcat"
FunctionCallArrayContains FunctionCallType = "ArrayContains"
FunctionCallArrayContainsAny FunctionCallType = "ArrayContainsAny"
FunctionCallArrayContainsAll FunctionCallType = "ArrayContainsAll"
FunctionCallArrayLength FunctionCallType = "ArrayLength"
FunctionCallArraySlice FunctionCallType = "ArraySlice"
FunctionCallSetIntersect FunctionCallType = "SetIntersect"
FunctionCallSetUnion FunctionCallType = "SetUnion"
FunctionCallMathAbs FunctionCallType = "MathAbs"
FunctionCallMathAcos FunctionCallType = "MathAcos"

View File

@ -4,6 +4,7 @@ import (
"testing"
"github.com/pikami/cosmium/parsers"
testutils "github.com/pikami/cosmium/test_utils"
)
func Test_Parse_ArrayFunctions(t *testing.T) {
@ -36,6 +37,119 @@ func Test_Parse_ArrayFunctions(t *testing.T) {
)
})
t.Run("Should parse function ARRAY_CONTAINS()", func(t *testing.T) {
testQueryParse(
t,
`SELECT ARRAY_CONTAINS(c.a1, "value") FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContains,
Arguments: []interface{}{
parsers.SelectItem{
Path: []string{"c", "a1"},
Type: parsers.SelectItemTypeField,
},
testutils.SelectItem_Constant_String("value"),
nil,
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse function ARRAY_CONTAINS() with partial match", func(t *testing.T) {
testQueryParse(
t,
`SELECT ARRAY_CONTAINS(["a", "b"], "value", true) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContains,
Arguments: []interface{}{
parsers.SelectItem{
Type: parsers.SelectItemTypeArray,
SelectItems: []parsers.SelectItem{
testutils.SelectItem_Constant_String("a"),
testutils.SelectItem_Constant_String("b"),
},
},
testutils.SelectItem_Constant_String("value"),
testutils.SelectItem_Constant_Bool(true),
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse function ARRAY_CONTAINS_ANY()", func(t *testing.T) {
testQueryParse(
t,
`SELECT ARRAY_CONTAINS_ANY(["a", "b"], "value", true) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContainsAny,
Arguments: []interface{}{
parsers.SelectItem{
Type: parsers.SelectItemTypeArray,
SelectItems: []parsers.SelectItem{
testutils.SelectItem_Constant_String("a"),
testutils.SelectItem_Constant_String("b"),
},
},
testutils.SelectItem_Constant_String("value"),
testutils.SelectItem_Constant_Bool(true),
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse function ARRAY_CONTAINS_ALL()", func(t *testing.T) {
testQueryParse(
t,
`SELECT ARRAY_CONTAINS_ALL(["a", "b"], "value", true) FROM c`,
parsers.SelectStmt{
SelectItems: []parsers.SelectItem{
{
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContainsAll,
Arguments: []interface{}{
parsers.SelectItem{
Type: parsers.SelectItemTypeArray,
SelectItems: []parsers.SelectItem{
testutils.SelectItem_Constant_String("a"),
testutils.SelectItem_Constant_String("b"),
},
},
testutils.SelectItem_Constant_String("value"),
testutils.SelectItem_Constant_Bool(true),
},
},
},
},
Table: parsers.Table{Value: "c"},
},
)
})
t.Run("Should parse function ARRAY_LENGTH()", func(t *testing.T) {
testQueryParse(
t,
@ -75,20 +189,8 @@ func Test_Parse_ArrayFunctions(t *testing.T) {
Path: []string{"c", "array"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 0,
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 2,
},
},
testutils.SelectItem_Constant_Int(0),
testutils.SelectItem_Constant_Int(2),
},
},
},

View File

@ -7,6 +7,7 @@ import (
"github.com/pikami/cosmium/parsers"
"github.com/pikami/cosmium/parsers/nosql"
testutils "github.com/pikami/cosmium/test_utils"
)
// For Parser Debugging
@ -102,20 +103,8 @@ func Test_Parse(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "123",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "456",
},
},
testutils.SelectItem_Constant_String("123"),
testutils.SelectItem_Constant_String("456"),
},
},
},

File diff suppressed because it is too large Load Diff

View File

@ -457,6 +457,9 @@ AggregateFunctions <- AvgAggregateExpression
/ SumAggregateExpression
ArrayFunctions <- ArrayConcatExpression
/ ArrayContainsExpression
/ ArrayContainsAnyExpression
/ ArrayContainsAllExpression
/ ArrayLengthExpression
/ ArraySliceExpression
/ SetIntersectExpression
@ -626,6 +629,18 @@ ArrayConcatExpression <- "ARRAY_CONCAT"i ws "(" ws arrays:SelectItem others:(ws
return createFunctionCall(parsers.FunctionCallArrayConcat, append([]interface{}{arrays}, others.([]interface{})...))
}
ArrayContainsExpression <- "ARRAY_CONTAINS"i ws "(" ws array:SelectItem ws "," ws item:SelectItem partialMatch:(ws "," ws ex:SelectItem { return ex, nil })? ws ")" {
return createFunctionCall(parsers.FunctionCallArrayContains, []interface{}{array, item, partialMatch})
}
ArrayContainsAnyExpression <- "ARRAY_CONTAINS_ANY"i ws "(" ws array:SelectItem items:(ws "," ws ex:SelectItem { return ex, nil })+ ws ")" {
return createFunctionCall(parsers.FunctionCallArrayContainsAny, append([]interface{}{array}, items.([]interface{})...))
}
ArrayContainsAllExpression <- "ARRAY_CONTAINS_ALL"i ws "(" ws array:SelectItem items:(ws "," ws ex:SelectItem { return ex, nil })+ ws ")" {
return createFunctionCall(parsers.FunctionCallArrayContainsAll, append([]interface{}{array}, items.([]interface{})...))
}
ArrayLengthExpression <- "ARRAY_LENGTH"i ws "(" ws array:SelectItem ws ")" {
return createFunctionCall(parsers.FunctionCallArrayLength, []interface{}{array})
}

View File

@ -4,6 +4,7 @@ import (
"testing"
"github.com/pikami/cosmium/parsers"
testutils "github.com/pikami/cosmium/test_utils"
)
func Test_Execute_StringFunctions(t *testing.T) {
@ -23,20 +24,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "123",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeBoolean,
Value: true,
},
},
testutils.SelectItem_Constant_String("123"),
testutils.SelectItem_Constant_Bool(true),
},
},
},
@ -61,13 +50,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "123",
},
},
testutils.SelectItem_Constant_String("123"),
nil,
},
},
@ -93,13 +76,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "123",
},
},
testutils.SelectItem_Constant_String("123"),
parsers.SelectItem{
Path: []string{"c", "pk"},
Type: parsers.SelectItemTypeField,
@ -128,20 +105,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "123",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeBoolean,
Value: true,
},
},
testutils.SelectItem_Constant_String("123"),
testutils.SelectItem_Constant_Bool(true),
},
},
},
@ -166,20 +131,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "123",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeBoolean,
Value: true,
},
},
testutils.SelectItem_Constant_String("123"),
testutils.SelectItem_Constant_Bool(true),
},
},
},
@ -204,20 +157,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "123",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeBoolean,
Value: true,
},
},
testutils.SelectItem_Constant_String("123"),
testutils.SelectItem_Constant_Bool(true),
},
},
},
@ -242,20 +183,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "2",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 1,
},
},
testutils.SelectItem_Constant_String("2"),
testutils.SelectItem_Constant_Int(1),
},
},
},
@ -352,13 +281,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 5,
},
},
testutils.SelectItem_Constant_Int(5),
},
},
},
@ -431,20 +354,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "old",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "new",
},
},
testutils.SelectItem_Constant_String("old"),
testutils.SelectItem_Constant_String("new"),
},
},
},
@ -469,13 +380,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 3,
},
},
testutils.SelectItem_Constant_Int(3),
},
},
},
@ -524,13 +429,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 3,
},
},
testutils.SelectItem_Constant_Int(3),
},
},
},
@ -579,20 +478,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
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: 5,
},
},
testutils.SelectItem_Constant_Int(1),
testutils.SelectItem_Constant_Int(5),
},
},
},

View File

@ -4,6 +4,7 @@ import (
"testing"
"github.com/pikami/cosmium/parsers"
testutils "github.com/pikami/cosmium/test_utils"
)
func Test_Parse_Were(t *testing.T) {
@ -22,10 +23,7 @@ func Test_Parse_Were(t *testing.T) {
Filters: parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
},
Right: testutils.SelectItem_Constant_Bool(true),
},
},
)
@ -51,18 +49,12 @@ func Test_Parse_Were(t *testing.T) {
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "12345"},
},
Right: testutils.SelectItem_Constant_String("12345"),
},
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "pk"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeInteger, Value: 123},
},
Right: testutils.SelectItem_Constant_Int(123),
},
},
},
@ -87,10 +79,7 @@ func Test_Parse_Were(t *testing.T) {
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
},
Right: testutils.SelectItem_Constant_Bool(true),
},
parsers.LogicalExpression{
Operation: parsers.LogicalExpressionTypeOr,
@ -98,18 +87,12 @@ func Test_Parse_Were(t *testing.T) {
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "123"},
},
Right: testutils.SelectItem_Constant_String("123"),
},
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "456"},
},
Right: testutils.SelectItem_Constant_String("456"),
},
},
},
@ -135,43 +118,28 @@ func Test_Parse_Were(t *testing.T) {
Filters: parsers.LogicalExpression{
Expressions: []interface{}{
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "boolean"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
},
Left: parsers.SelectItem{Path: []string{"c", "boolean"}},
Right: testutils.SelectItem_Constant_Bool(true),
Operation: "=",
},
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "integer"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeInteger, Value: 1},
},
Left: parsers.SelectItem{Path: []string{"c", "integer"}},
Right: testutils.SelectItem_Constant_Int(1),
Operation: "=",
},
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "float"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeFloat, Value: 6.9},
},
Left: parsers.SelectItem{Path: []string{"c", "float"}},
Right: testutils.SelectItem_Constant_Float(6.9),
Operation: "=",
},
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "string"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "hello"},
},
Left: parsers.SelectItem{Path: []string{"c", "string"}},
Right: testutils.SelectItem_Constant_String("hello"),
Operation: "=",
},
parsers.ComparisonExpression{
Left: parsers.SelectItem{Path: []string{"c", "param"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeParameterConstant, Value: "@param_id1"},
},
Left: parsers.SelectItem{Path: []string{"c", "param"}},
Right: testutils.SelectItem_Constant_Parameter("@param_id1"),
Operation: "=",
},
},

View File

@ -16,6 +16,86 @@ func (r rowContext) array_Concat(arguments []interface{}) []interface{} {
return result
}
func (r rowContext) array_Contains(arguments []interface{}) bool {
array := r.parseArray(arguments[0])
if array == nil {
return false
}
exprToSearch := r.resolveSelectItem(arguments[1].(parsers.SelectItem))
partialSearch := false
if len(arguments) > 2 {
boolExpr := r.resolveSelectItem(arguments[2].(parsers.SelectItem))
if boolValue, ok := boolExpr.(bool); ok {
partialSearch = boolValue
} else {
logger.Error("array_Contains - got parameters of wrong type")
return false
}
}
for _, item := range array {
if partialSearch {
if r.partialMatch(item, exprToSearch) {
return true
}
} else {
if reflect.DeepEqual(item, exprToSearch) {
return true
}
}
}
return false
}
func (r rowContext) array_Contains_Any(arguments []interface{}) bool {
array := r.parseArray(arguments[0])
if array == nil {
return false
}
valueSelectItems := arguments[1:]
for _, valueSelectItem := range valueSelectItems {
value := r.resolveSelectItem(valueSelectItem.(parsers.SelectItem))
for _, item := range array {
if reflect.DeepEqual(item, value) {
return true
}
}
}
return false
}
func (r rowContext) array_Contains_All(arguments []interface{}) bool {
array := r.parseArray(arguments[0])
if array == nil {
return false
}
valueSelectItems := arguments[1:]
for _, valueSelectItem := range valueSelectItems {
value := r.resolveSelectItem(valueSelectItem.(parsers.SelectItem))
found := false
for _, item := range array {
if reflect.DeepEqual(item, value) {
found = true
break
}
}
if !found {
return false
}
}
return true
}
func (r rowContext) array_Length(arguments []interface{}) int {
array := r.parseArray(arguments[0])
if array == nil {
@ -129,3 +209,21 @@ func (r rowContext) parseArray(argument interface{}) []interface{} {
return result
}
func (r rowContext) partialMatch(item interface{}, exprToSearch interface{}) bool {
itemValue := reflect.ValueOf(item)
exprValue := reflect.ValueOf(exprToSearch)
if itemValue.Kind() != reflect.Map || exprValue.Kind() != reflect.Map {
logger.Error("partialMatch got parameters of wrong type")
return false
}
for _, key := range exprValue.MapKeys() {
if itemValue.MapIndex(key).Interface() != exprValue.MapIndex(key).Interface() {
return false
}
}
return true
}

View File

@ -5,6 +5,7 @@ import (
"github.com/pikami/cosmium/parsers"
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
testutils "github.com/pikami/cosmium/test_utils"
)
func Test_Execute_ArrayFunctions(t *testing.T) {
@ -52,6 +53,286 @@ func Test_Execute_ArrayFunctions(t *testing.T) {
)
})
t.Run("Should execute function ARRAY_CONTAINS()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
Parameters: map[string]interface{}{
"@categories": []interface{}{"coats", "jackets", "sweatshirts"},
"@objectArray": []interface{}{map[string]interface{}{"category": "shirts", "color": "blue"}},
"@fullMatchObject": map[string]interface{}{"category": "shirts", "color": "blue"},
"@partialMatchObject": map[string]interface{}{"category": "shirts"},
"@missingPartialMatchObject": map[string]interface{}{"category": "shorts", "color": "blue"},
},
SelectItems: []parsers.SelectItem{
{
Alias: "ContainsItem",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContains,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@categories"),
testutils.SelectItem_Constant_String("coats"),
},
},
},
{
Alias: "MissingItem",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContains,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@categories"),
testutils.SelectItem_Constant_String("hoodies"),
},
},
},
{
Alias: "ContainsFullMatchObject",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContains,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@objectArray"),
testutils.SelectItem_Constant_Parameter("@fullMatchObject"),
},
},
},
{
Alias: "MissingFullMatchObject",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContains,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@objectArray"),
testutils.SelectItem_Constant_Parameter("@partialMatchObject"),
},
},
},
{
Alias: "ContainsPartialMatchObject",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContains,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@objectArray"),
testutils.SelectItem_Constant_Parameter("@partialMatchObject"),
testutils.SelectItem_Constant_Bool(true),
},
},
},
{
Alias: "MissingPartialMatchObject",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContains,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@objectArray"),
testutils.SelectItem_Constant_Parameter("@missingPartialMatchObject"),
testutils.SelectItem_Constant_Bool(true),
},
},
},
},
},
[]memoryexecutor.RowType{map[string]interface{}{"id": "123"}},
[]memoryexecutor.RowType{
map[string]interface{}{
"ContainsItem": true,
"MissingItem": false,
"ContainsFullMatchObject": true,
"MissingFullMatchObject": false,
"ContainsPartialMatchObject": true,
"MissingPartialMatchObject": false,
},
},
)
})
t.Run("Should execute function ARRAY_CONTAINS_ANY()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
Parameters: map[string]interface{}{
"@mixedArray": []interface{}{1, true, "3", []int{1, 2, 3}},
"@numbers": []interface{}{1, 2, 3, 4},
"@emptyArray": []interface{}{},
"@arr123": []interface{}{1, 2, 3},
},
SelectItems: []parsers.SelectItem{
{
Alias: "matchesEntireArray",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContainsAny,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@mixedArray"),
testutils.SelectItem_Constant_Int(1),
testutils.SelectItem_Constant_Bool(true),
testutils.SelectItem_Constant_String("3"),
testutils.SelectItem_Constant_Parameter("@arr123"),
},
},
},
{
Alias: "matchesSomeValues",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContainsAny,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@numbers"),
testutils.SelectItem_Constant_Int(2),
testutils.SelectItem_Constant_Int(3),
testutils.SelectItem_Constant_Int(4),
testutils.SelectItem_Constant_Int(5),
},
},
},
{
Alias: "matchSingleValue",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContainsAny,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@numbers"),
testutils.SelectItem_Constant_Int(1),
},
},
},
{
Alias: "noMatches",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContainsAny,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@numbers"),
testutils.SelectItem_Constant_Int(5),
testutils.SelectItem_Constant_Int(6),
testutils.SelectItem_Constant_Int(7),
testutils.SelectItem_Constant_Int(8),
},
},
},
{
Alias: "emptyArray",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContainsAny,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@emptyArray"),
testutils.SelectItem_Constant_Int(1),
testutils.SelectItem_Constant_Int(2),
testutils.SelectItem_Constant_Int(3),
},
},
},
},
},
[]memoryexecutor.RowType{map[string]interface{}{"id": "123"}},
[]memoryexecutor.RowType{
map[string]interface{}{
"matchesEntireArray": true,
"matchesSomeValues": true,
"matchSingleValue": true,
"noMatches": false,
"emptyArray": false,
},
},
)
})
t.Run("Should execute function ARRAY_CONTAINS_ALL()", func(t *testing.T) {
testQueryExecute(
t,
parsers.SelectStmt{
Parameters: map[string]interface{}{
"@mixedArray": []interface{}{1, true, "3", []interface{}{1, 2, 3}},
"@numbers": []interface{}{1, 2, 3, 4},
"@emptyArray": []interface{}{},
"@arr123": []interface{}{1, 2, 3},
},
SelectItems: []parsers.SelectItem{
{
Alias: "matchesEntireArray",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContainsAll,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@mixedArray"),
testutils.SelectItem_Constant_Int(1),
testutils.SelectItem_Constant_Bool(true),
testutils.SelectItem_Constant_String("3"),
testutils.SelectItem_Constant_Parameter("@arr123"),
},
},
},
{
Alias: "matchesSomeValues",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContainsAll,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@numbers"),
testutils.SelectItem_Constant_Int(2),
testutils.SelectItem_Constant_Int(3),
testutils.SelectItem_Constant_Int(4),
testutils.SelectItem_Constant_Int(5),
},
},
},
{
Alias: "matchSingleValue",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContainsAll,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@numbers"),
testutils.SelectItem_Constant_Int(1),
},
},
},
{
Alias: "noMatches",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContainsAll,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@numbers"),
testutils.SelectItem_Constant_Int(5),
testutils.SelectItem_Constant_Int(6),
testutils.SelectItem_Constant_Int(7),
testutils.SelectItem_Constant_Int(8),
},
},
},
{
Alias: "emptyArray",
Type: parsers.SelectItemTypeFunctionCall,
Value: parsers.FunctionCall{
Type: parsers.FunctionCallArrayContainsAll,
Arguments: []interface{}{
testutils.SelectItem_Constant_Parameter("@emptyArray"),
testutils.SelectItem_Constant_Int(1),
testutils.SelectItem_Constant_Int(2),
testutils.SelectItem_Constant_Int(3),
},
},
},
},
},
[]memoryexecutor.RowType{map[string]interface{}{"id": "123"}},
[]memoryexecutor.RowType{
map[string]interface{}{
"matchesEntireArray": true,
"matchesSomeValues": false,
"matchSingleValue": true,
"noMatches": false,
"emptyArray": false,
},
},
)
})
t.Run("Should execute function ARRAY_LENGTH()", func(t *testing.T) {
testQueryExecute(
t,
@ -105,20 +386,8 @@ func Test_Execute_ArrayFunctions(t *testing.T) {
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,
},
},
testutils.SelectItem_Constant_Int(1),
testutils.SelectItem_Constant_Int(2),
},
},
},

View File

@ -470,6 +470,12 @@ func (r rowContext) selectItem_SelectItemTypeFunctionCall(functionCall parsers.F
case parsers.FunctionCallArrayConcat:
return r.array_Concat(functionCall.Arguments)
case parsers.FunctionCallArrayContains:
return r.array_Contains(functionCall.Arguments)
case parsers.FunctionCallArrayContainsAny:
return r.array_Contains_Any(functionCall.Arguments)
case parsers.FunctionCallArrayContainsAll:
return r.array_Contains_All(functionCall.Arguments)
case parsers.FunctionCallArrayLength:
return r.array_Length(functionCall.Arguments)
case parsers.FunctionCallArraySlice:

View File

@ -6,6 +6,7 @@ import (
"github.com/pikami/cosmium/parsers"
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
testutils "github.com/pikami/cosmium/test_utils"
)
func testQueryExecute(
@ -111,20 +112,8 @@ func Test_Execute(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "123",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "456",
},
},
testutils.SelectItem_Constant_String("123"),
testutils.SelectItem_Constant_String("456"),
},
},
},

View File

@ -5,6 +5,7 @@ import (
"github.com/pikami/cosmium/parsers"
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
testutils "github.com/pikami/cosmium/test_utils"
)
func Test_Execute_StringFunctions(t *testing.T) {
@ -33,20 +34,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "pk"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "aaa",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeBoolean,
Value: true,
},
},
testutils.SelectItem_Constant_String("aaa"),
testutils.SelectItem_Constant_Bool(true),
},
},
},
@ -81,13 +70,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "pk"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "aaa",
},
},
testutils.SelectItem_Constant_String("aaa"),
nil,
},
},
@ -119,20 +102,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: " ",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: 123,
},
},
testutils.SelectItem_Constant_String(" "),
testutils.SelectItem_Constant_Int(123),
parsers.SelectItem{
Path: []string{"c", "pk"},
Type: parsers.SelectItemTypeField,
@ -171,20 +142,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "2",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeBoolean,
Value: true,
},
},
testutils.SelectItem_Constant_String("2"),
testutils.SelectItem_Constant_Bool(true),
},
},
},
@ -219,20 +178,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "3",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeBoolean,
Value: true,
},
},
testutils.SelectItem_Constant_String("3"),
testutils.SelectItem_Constant_Bool(true),
},
},
},
@ -267,20 +214,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "id"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "1",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeBoolean,
Value: true,
},
},
testutils.SelectItem_Constant_String("1"),
testutils.SelectItem_Constant_Bool(true),
},
},
},
@ -315,20 +250,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "o",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 4,
},
},
testutils.SelectItem_Constant_String("o"),
testutils.SelectItem_Constant_Int(4),
},
},
},
@ -397,13 +320,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 3,
},
},
testutils.SelectItem_Constant_Int(3),
},
},
},
@ -506,20 +423,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "world",
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: "universe",
},
},
testutils.SelectItem_Constant_String("world"),
testutils.SelectItem_Constant_String("universe"),
},
},
},
@ -554,13 +459,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 3,
},
},
testutils.SelectItem_Constant_Int(3),
},
},
},
@ -629,13 +528,7 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 3,
},
},
testutils.SelectItem_Constant_Int(3),
},
},
},
@ -704,20 +597,8 @@ func Test_Execute_StringFunctions(t *testing.T) {
Path: []string{"c", "str"},
Type: parsers.SelectItemTypeField,
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 2,
},
},
parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: 4,
},
},
testutils.SelectItem_Constant_Int(2),
testutils.SelectItem_Constant_Int(4),
},
},
},

View File

@ -5,6 +5,7 @@ import (
"github.com/pikami/cosmium/parsers"
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
testutils "github.com/pikami/cosmium/test_utils"
)
func Test_Execute_Where(t *testing.T) {
@ -26,10 +27,7 @@ func Test_Execute_Where(t *testing.T) {
Filters: parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
},
Right: testutils.SelectItem_Constant_Bool(true),
},
},
mockData,
@ -52,10 +50,7 @@ func Test_Execute_Where(t *testing.T) {
Filters: parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeParameterConstant, Value: "@param_id"},
},
Right: testutils.SelectItem_Constant_Parameter("@param_id"),
},
Parameters: map[string]interface{}{
"@param_id": "456",
@ -83,18 +78,12 @@ func Test_Execute_Where(t *testing.T) {
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "67890"},
},
Right: testutils.SelectItem_Constant_String("67890"),
},
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "pk"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeInteger, Value: 456},
},
Right: testutils.SelectItem_Constant_Int(456),
},
},
},
@ -120,10 +109,7 @@ func Test_Execute_Where(t *testing.T) {
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "isCool"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
},
Right: testutils.SelectItem_Constant_Bool(true),
},
parsers.LogicalExpression{
Operation: parsers.LogicalExpressionTypeOr,
@ -131,18 +117,12 @@ func Test_Execute_Where(t *testing.T) {
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "123"},
},
Right: testutils.SelectItem_Constant_String("123"),
},
parsers.ComparisonExpression{
Operation: "=",
Left: parsers.SelectItem{Path: []string{"c", "id"}},
Right: parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{Type: parsers.ConstantTypeString, Value: "456"},
},
Right: testutils.SelectItem_Constant_String("456"),
},
},
},

View File

@ -0,0 +1,53 @@
package testutils
import "github.com/pikami/cosmium/parsers"
func SelectItem_Constant_String(value string) parsers.SelectItem {
return parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeString,
Value: value,
},
}
}
func SelectItem_Constant_Int(value int) parsers.SelectItem {
return parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeInteger,
Value: value,
},
}
}
func SelectItem_Constant_Float(value float64) parsers.SelectItem {
return parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeFloat,
Value: value,
},
}
}
func SelectItem_Constant_Bool(value bool) parsers.SelectItem {
return parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeBoolean,
Value: value,
},
}
}
func SelectItem_Constant_Parameter(name string) parsers.SelectItem {
return parsers.SelectItem{
Type: parsers.SelectItemTypeConstant,
Value: parsers.Constant{
Type: parsers.ConstantTypeParameterConstant,
Value: name,
},
}
}