mirror of https://github.com/pikami/cosmium.git
Implement AVG, COUNT, MAX, MIN, SUM functions
This commit is contained in:
parent
b72bba86c8
commit
6ed74688ca
|
@ -119,9 +119,23 @@ const (
|
||||||
FunctionCallSetIntersect FunctionCallType = "SetIntersect"
|
FunctionCallSetIntersect FunctionCallType = "SetIntersect"
|
||||||
FunctionCallSetUnion FunctionCallType = "SetUnion"
|
FunctionCallSetUnion FunctionCallType = "SetUnion"
|
||||||
|
|
||||||
|
FunctionCallAggregateAvg FunctionCallType = "AggregateAvg"
|
||||||
|
FunctionCallAggregateCount FunctionCallType = "AggregateCount"
|
||||||
|
FunctionCallAggregateMax FunctionCallType = "AggregateMax"
|
||||||
|
FunctionCallAggregateMin FunctionCallType = "AggregateMin"
|
||||||
|
FunctionCallAggregateSum FunctionCallType = "AggregateSum"
|
||||||
|
|
||||||
FunctionCallIn FunctionCallType = "In"
|
FunctionCallIn FunctionCallType = "In"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var AggregateFunctions = []FunctionCallType{
|
||||||
|
FunctionCallAggregateAvg,
|
||||||
|
FunctionCallAggregateCount,
|
||||||
|
FunctionCallAggregateMax,
|
||||||
|
FunctionCallAggregateMin,
|
||||||
|
FunctionCallAggregateSum,
|
||||||
|
}
|
||||||
|
|
||||||
type FunctionCall struct {
|
type FunctionCall struct {
|
||||||
Arguments []interface{}
|
Arguments []interface{}
|
||||||
Type FunctionCallType
|
Type FunctionCallType
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
package nosql_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pikami/cosmium/parsers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Parse_AggregateFunctions(t *testing.T) {
|
||||||
|
|
||||||
|
t.Run("Should parse function AVG()", func(t *testing.T) {
|
||||||
|
testQueryParse(
|
||||||
|
t,
|
||||||
|
`SELECT AVG(c.a1) FROM c`,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallAggregateAvg,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "a1"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should parse function COUNT()", func(t *testing.T) {
|
||||||
|
testQueryParse(
|
||||||
|
t,
|
||||||
|
`SELECT COUNT(c.a1) FROM c`,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallAggregateCount,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "a1"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should parse function MAX()", func(t *testing.T) {
|
||||||
|
testQueryParse(
|
||||||
|
t,
|
||||||
|
`SELECT MAX(c.a1) FROM c`,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallAggregateMax,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "a1"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should parse function MIN()", func(t *testing.T) {
|
||||||
|
testQueryParse(
|
||||||
|
t,
|
||||||
|
`SELECT MIN(c.a1) FROM c`,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallAggregateMin,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "a1"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should parse function SUM()", func(t *testing.T) {
|
||||||
|
testQueryParse(
|
||||||
|
t,
|
||||||
|
`SELECT SUM(c.a1) FROM c`,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallAggregateSum,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "a1"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -327,6 +327,7 @@ FunctionCall <- StringFunctions
|
||||||
/ TypeCheckingFunctions
|
/ TypeCheckingFunctions
|
||||||
/ ArrayFunctions
|
/ ArrayFunctions
|
||||||
/ InFunction
|
/ InFunction
|
||||||
|
/ AggregateFunctions
|
||||||
|
|
||||||
StringFunctions <- StringEqualsExpression
|
StringFunctions <- StringEqualsExpression
|
||||||
/ ToStringExpression
|
/ ToStringExpression
|
||||||
|
@ -356,6 +357,12 @@ TypeCheckingFunctions <- IsDefined
|
||||||
/ IsPrimitive
|
/ IsPrimitive
|
||||||
/ IsString
|
/ IsString
|
||||||
|
|
||||||
|
AggregateFunctions <- AvgAggregateExpression
|
||||||
|
/ CountAggregateExpression
|
||||||
|
/ MaxAggregateExpression
|
||||||
|
/ MinAggregateExpression
|
||||||
|
/ SumAggregateExpression
|
||||||
|
|
||||||
ArrayFunctions <- ArrayConcatExpression
|
ArrayFunctions <- ArrayConcatExpression
|
||||||
/ ArrayLengthExpression
|
/ ArrayLengthExpression
|
||||||
/ ArraySliceExpression
|
/ ArraySliceExpression
|
||||||
|
@ -509,6 +516,26 @@ InFunction <- ex1:SelectProperty ws "IN"i ws "(" ws ex2:SelectItem others:(ws ",
|
||||||
return createFunctionCall(parsers.FunctionCallIn, append([]interface{}{ex1, ex2}, others.([]interface{})...))
|
return createFunctionCall(parsers.FunctionCallIn, append([]interface{}{ex1, ex2}, others.([]interface{})...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AvgAggregateExpression <- "AVG"i "(" ws ex:SelectItem ws ")" {
|
||||||
|
return createFunctionCall(parsers.FunctionCallAggregateAvg, []interface{}{ex})
|
||||||
|
}
|
||||||
|
|
||||||
|
CountAggregateExpression <- "COUNT"i "(" ws ex:SelectItem ws ")" {
|
||||||
|
return createFunctionCall(parsers.FunctionCallAggregateCount, []interface{}{ex})
|
||||||
|
}
|
||||||
|
|
||||||
|
MaxAggregateExpression <- "MAX"i "(" ws ex:SelectItem ws ")" {
|
||||||
|
return createFunctionCall(parsers.FunctionCallAggregateMax, []interface{}{ex})
|
||||||
|
}
|
||||||
|
|
||||||
|
MinAggregateExpression <- "MIN"i "(" ws ex:SelectItem ws ")" {
|
||||||
|
return createFunctionCall(parsers.FunctionCallAggregateMin, []interface{}{ex})
|
||||||
|
}
|
||||||
|
|
||||||
|
SumAggregateExpression <- "SUM"i "(" ws ex:SelectItem ws ")" {
|
||||||
|
return createFunctionCall(parsers.FunctionCallAggregateSum, []interface{}{ex})
|
||||||
|
}
|
||||||
|
|
||||||
Integer <- [0-9]+ {
|
Integer <- [0-9]+ {
|
||||||
return strconv.Atoi(string(c.text))
|
return strconv.Atoi(string(c.text))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
package memoryexecutor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/pikami/cosmium/parsers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c memoryExecutorContext) aggregate_Avg(arguments []interface{}, row RowType) interface{} {
|
||||||
|
selectExpression := arguments[0].(parsers.SelectItem)
|
||||||
|
sum := 0.0
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
if array, isArray := row.([]RowType); isArray {
|
||||||
|
for _, item := range array {
|
||||||
|
value := c.getFieldValue(selectExpression, item)
|
||||||
|
if numericValue, ok := value.(float64); ok {
|
||||||
|
sum += numericValue
|
||||||
|
count++
|
||||||
|
} else if numericValue, ok := value.(int); ok {
|
||||||
|
sum += float64(numericValue)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if count > 0 {
|
||||||
|
return sum / float64(count)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c memoryExecutorContext) aggregate_Count(arguments []interface{}, row RowType) interface{} {
|
||||||
|
selectExpression := arguments[0].(parsers.SelectItem)
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
if array, isArray := row.([]RowType); isArray {
|
||||||
|
for _, item := range array {
|
||||||
|
value := c.getFieldValue(selectExpression, item)
|
||||||
|
if value != nil {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c memoryExecutorContext) aggregate_Max(arguments []interface{}, row RowType) interface{} {
|
||||||
|
selectExpression := arguments[0].(parsers.SelectItem)
|
||||||
|
max := 0.0
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
if array, isArray := row.([]RowType); isArray {
|
||||||
|
for _, item := range array {
|
||||||
|
value := c.getFieldValue(selectExpression, item)
|
||||||
|
if numericValue, ok := value.(float64); ok {
|
||||||
|
if numericValue > max {
|
||||||
|
max = numericValue
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
} else if numericValue, ok := value.(int); ok {
|
||||||
|
if float64(numericValue) > max {
|
||||||
|
max = float64(numericValue)
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if count > 0 {
|
||||||
|
return max
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c memoryExecutorContext) aggregate_Min(arguments []interface{}, row RowType) interface{} {
|
||||||
|
selectExpression := arguments[0].(parsers.SelectItem)
|
||||||
|
min := math.MaxFloat64
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
if array, isArray := row.([]RowType); isArray {
|
||||||
|
for _, item := range array {
|
||||||
|
value := c.getFieldValue(selectExpression, item)
|
||||||
|
if numericValue, ok := value.(float64); ok {
|
||||||
|
if numericValue < min {
|
||||||
|
min = numericValue
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
} else if numericValue, ok := value.(int); ok {
|
||||||
|
if float64(numericValue) < min {
|
||||||
|
min = float64(numericValue)
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if count > 0 {
|
||||||
|
return min
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c memoryExecutorContext) aggregate_Sum(arguments []interface{}, row RowType) interface{} {
|
||||||
|
selectExpression := arguments[0].(parsers.SelectItem)
|
||||||
|
sum := 0.0
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
if array, isArray := row.([]RowType); isArray {
|
||||||
|
for _, item := range array {
|
||||||
|
value := c.getFieldValue(selectExpression, item)
|
||||||
|
if numericValue, ok := value.(float64); ok {
|
||||||
|
sum += numericValue
|
||||||
|
count++
|
||||||
|
} else if numericValue, ok := value.(int); ok {
|
||||||
|
sum += float64(numericValue)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if count > 0 {
|
||||||
|
return sum
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,210 @@
|
||||||
|
package memoryexecutor_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pikami/cosmium/parsers"
|
||||||
|
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Execute_AggregateFunctions(t *testing.T) {
|
||||||
|
mockData := []memoryexecutor.RowType{
|
||||||
|
map[string]interface{}{"id": "123", "number": 123, "key": "a"},
|
||||||
|
map[string]interface{}{"id": "456", "number": 456, "key": "a"},
|
||||||
|
map[string]interface{}{"id": "789", "number": 789, "key": "b"},
|
||||||
|
map[string]interface{}{"id": "no-number", "key": "b"},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("Should execute function AVG()", func(t *testing.T) {
|
||||||
|
testQueryExecute(
|
||||||
|
t,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "key"}},
|
||||||
|
{
|
||||||
|
Alias: "avg",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallAggregateAvg,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "number"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GroupBy: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "key"}},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
mockData,
|
||||||
|
[]memoryexecutor.RowType{
|
||||||
|
map[string]interface{}{"key": "a", "avg": 289.5},
|
||||||
|
map[string]interface{}{"key": "b", "avg": 789.0},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should execute function AVG() without GROUP BY clause", func(t *testing.T) {
|
||||||
|
testQueryExecute(
|
||||||
|
t,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{
|
||||||
|
Alias: "avg",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallAggregateAvg,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "number"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
mockData,
|
||||||
|
[]memoryexecutor.RowType{
|
||||||
|
map[string]interface{}{"avg": 456.0},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should execute function COUNT()", func(t *testing.T) {
|
||||||
|
testQueryExecute(
|
||||||
|
t,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "key"}},
|
||||||
|
{
|
||||||
|
Alias: "cnt",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallAggregateCount,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "number"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GroupBy: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "key"}},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
mockData,
|
||||||
|
[]memoryexecutor.RowType{
|
||||||
|
map[string]interface{}{"key": "a", "cnt": 2},
|
||||||
|
map[string]interface{}{"key": "b", "cnt": 1},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should execute function MAX()", func(t *testing.T) {
|
||||||
|
testQueryExecute(
|
||||||
|
t,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "key"}},
|
||||||
|
{
|
||||||
|
Alias: "max",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallAggregateMax,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "number"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GroupBy: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "key"}},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
mockData,
|
||||||
|
[]memoryexecutor.RowType{
|
||||||
|
map[string]interface{}{"key": "a", "max": 456.0},
|
||||||
|
map[string]interface{}{"key": "b", "max": 789.0},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should execute function MIN()", func(t *testing.T) {
|
||||||
|
testQueryExecute(
|
||||||
|
t,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "key"}},
|
||||||
|
{
|
||||||
|
Alias: "min",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallAggregateMin,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "number"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GroupBy: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "key"}},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
mockData,
|
||||||
|
[]memoryexecutor.RowType{
|
||||||
|
map[string]interface{}{"key": "a", "min": 123.0},
|
||||||
|
map[string]interface{}{"key": "b", "min": 789.0},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should execute function SUM()", func(t *testing.T) {
|
||||||
|
testQueryExecute(
|
||||||
|
t,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "key"}},
|
||||||
|
{
|
||||||
|
Alias: "sum",
|
||||||
|
Type: parsers.SelectItemTypeFunctionCall,
|
||||||
|
Value: parsers.FunctionCall{
|
||||||
|
Type: parsers.FunctionCallAggregateSum,
|
||||||
|
Arguments: []interface{}{
|
||||||
|
parsers.SelectItem{
|
||||||
|
Path: []string{"c", "number"},
|
||||||
|
Type: parsers.SelectItemTypeField,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GroupBy: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "key"}},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
},
|
||||||
|
mockData,
|
||||||
|
[]memoryexecutor.RowType{
|
||||||
|
map[string]interface{}{"key": "a", "sum": 579.0},
|
||||||
|
map[string]interface{}{"key": "b", "sum": 789.0},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/pikami/cosmium/internal/logger"
|
"github.com/pikami/cosmium/internal/logger"
|
||||||
"github.com/pikami/cosmium/parsers"
|
"github.com/pikami/cosmium/parsers"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RowType interface{}
|
type RowType interface{}
|
||||||
|
@ -45,9 +46,16 @@ func Execute(query parsers.SelectStmt, data []RowType) []RowType {
|
||||||
// Apply select
|
// Apply select
|
||||||
if !isGroupSelect {
|
if !isGroupSelect {
|
||||||
selectedData := make([]RowType, 0)
|
selectedData := make([]RowType, 0)
|
||||||
for _, row := range result {
|
if hasAggregateFunctions(query.SelectItems) {
|
||||||
selectedData = append(selectedData, ctx.selectRow(query.SelectItems, row))
|
// When can have aggregate functions without GROUP BY clause,
|
||||||
|
// we should aggregate all rows in that case
|
||||||
|
selectedData = append(selectedData, ctx.selectRow(query.SelectItems, result))
|
||||||
|
} else {
|
||||||
|
for _, row := range result {
|
||||||
|
selectedData = append(selectedData, ctx.selectRow(query.SelectItems, row))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = selectedData
|
result = selectedData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,6 +283,17 @@ func (c memoryExecutorContext) getFieldValue(field parsers.SelectItem, row RowTy
|
||||||
case parsers.FunctionCallSetUnion:
|
case parsers.FunctionCallSetUnion:
|
||||||
return c.set_Union(typedValue.Arguments, rowValue)
|
return c.set_Union(typedValue.Arguments, rowValue)
|
||||||
|
|
||||||
|
case parsers.FunctionCallAggregateAvg:
|
||||||
|
return c.aggregate_Avg(typedValue.Arguments, row)
|
||||||
|
case parsers.FunctionCallAggregateCount:
|
||||||
|
return c.aggregate_Count(typedValue.Arguments, row)
|
||||||
|
case parsers.FunctionCallAggregateMax:
|
||||||
|
return c.aggregate_Max(typedValue.Arguments, row)
|
||||||
|
case parsers.FunctionCallAggregateMin:
|
||||||
|
return c.aggregate_Min(typedValue.Arguments, row)
|
||||||
|
case parsers.FunctionCallAggregateSum:
|
||||||
|
return c.aggregate_Sum(typedValue.Arguments, row)
|
||||||
|
|
||||||
case parsers.FunctionCallIn:
|
case parsers.FunctionCallIn:
|
||||||
return c.misc_In(typedValue.Arguments, rowValue)
|
return c.misc_In(typedValue.Arguments, rowValue)
|
||||||
}
|
}
|
||||||
|
@ -431,3 +450,23 @@ func deduplicate(slice []RowType) []RowType {
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasAggregateFunctions(selectItems []parsers.SelectItem) bool {
|
||||||
|
if selectItems == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, selectItem := range selectItems {
|
||||||
|
if selectItem.Type == parsers.SelectItemTypeFunctionCall {
|
||||||
|
if typedValue, ok := selectItem.Value.(parsers.FunctionCall); ok && slices.Contains[[]parsers.FunctionCallType](parsers.AggregateFunctions, typedValue.Type) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasAggregateFunctions(selectItem.SelectItems) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue