mirror of https://github.com/pikami/cosmium.git
Implement in-memory query executor
This commit is contained in:
parent
12510ea3fa
commit
ca49228c66
|
@ -57,7 +57,7 @@ func Test_Parse(t *testing.T) {
|
|||
t,
|
||||
`select c.id
|
||||
FROM c
|
||||
WHERE c.pk=true`,
|
||||
WHERE c.isCool=true`,
|
||||
parsers.SelectStmt{
|
||||
Columns: []parsers.FieldPath{
|
||||
{Path: []string{"c", "id"}},
|
||||
|
@ -65,7 +65,7 @@ func Test_Parse(t *testing.T) {
|
|||
Table: parsers.Table{Value: "c"},
|
||||
Filters: parsers.ComparisonExpression{
|
||||
Operation: "=",
|
||||
Left: parsers.FieldPath{Path: []string{"c", "pk"}},
|
||||
Left: parsers.FieldPath{Path: []string{"c", "isCool"}},
|
||||
Right: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
package memoryexecutor
|
||||
|
||||
import (
|
||||
"github.com/pikami/cosmium/parsers"
|
||||
)
|
||||
|
||||
type RowType interface{}
|
||||
type ExpressionType interface{}
|
||||
|
||||
func Execute(query parsers.SelectStmt, data []RowType) []RowType {
|
||||
var result []RowType
|
||||
|
||||
// Iterate over each row in the data
|
||||
for _, row := range data {
|
||||
// Check if the row satisfies the filter conditions
|
||||
if evaluateFilters(query.Filters, row) {
|
||||
// Construct a new row based on the selected columns
|
||||
newRow := make(map[string]interface{})
|
||||
for _, column := range query.Columns {
|
||||
destinationName := column.Alias
|
||||
if destinationName == "" {
|
||||
destinationName = column.Path[len(column.Path)-1]
|
||||
}
|
||||
|
||||
newRow[destinationName] = getFieldValue(column, row)
|
||||
}
|
||||
// Add the new row to the result
|
||||
result = append(result, newRow)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Helper function to evaluate filter conditions recursively
|
||||
func evaluateFilters(expr ExpressionType, row RowType) bool {
|
||||
if expr == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
switch typedValue := expr.(type) {
|
||||
case parsers.ComparisonExpression:
|
||||
leftValue := getExpressionParameterValue(typedValue.Left, row)
|
||||
rightValue := getExpressionParameterValue(typedValue.Right, row)
|
||||
|
||||
switch typedValue.Operation {
|
||||
case "=":
|
||||
return leftValue == rightValue
|
||||
// Handle other comparison operators as needed
|
||||
}
|
||||
case parsers.LogicalExpression:
|
||||
var result bool
|
||||
for i, expression := range typedValue.Expressions {
|
||||
expressionResult := evaluateFilters(expression, row)
|
||||
if i == 0 {
|
||||
result = expressionResult
|
||||
}
|
||||
|
||||
switch typedValue.Operation {
|
||||
case parsers.LogicalExpressionTypeAnd:
|
||||
result = result && expressionResult
|
||||
if !result {
|
||||
return false
|
||||
}
|
||||
case parsers.LogicalExpressionTypeOr:
|
||||
result = result || expressionResult
|
||||
if result {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getFieldValue(field parsers.FieldPath, row RowType) interface{} {
|
||||
value := row
|
||||
for _, pathSegment := range field.Path[1:] {
|
||||
if nestedValue, ok := value.(map[string]interface{}); ok {
|
||||
value = nestedValue[pathSegment]
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func getExpressionParameterValue(parameter interface{}, row RowType) interface{} {
|
||||
switch typedParameter := parameter.(type) {
|
||||
case parsers.FieldPath:
|
||||
return getFieldValue(typedParameter, row)
|
||||
case parsers.Constant:
|
||||
return typedParameter.Value
|
||||
}
|
||||
// TODO: Handle error
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package memoryexecutor_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/pikami/cosmium/parsers"
|
||||
memoryexecutor "github.com/pikami/cosmium/query_executors/memory_executor"
|
||||
)
|
||||
|
||||
func testQueryExecute(
|
||||
t *testing.T,
|
||||
query parsers.SelectStmt,
|
||||
data []memoryexecutor.RowType,
|
||||
expectedData []memoryexecutor.RowType,
|
||||
) {
|
||||
result := memoryexecutor.Execute(query, data)
|
||||
|
||||
if !reflect.DeepEqual(result, expectedData) {
|
||||
t.Errorf("parsed query does not match expected structure.\nExpected: %+v\nGot: %+v", expectedData, result)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Execute(t *testing.T) {
|
||||
mockData := []memoryexecutor.RowType{
|
||||
map[string]interface{}{"id": "12345", "pk": 123, "_self": "self1", "_rid": "rid1", "_ts": 123456, "isCool": false},
|
||||
map[string]interface{}{"id": "67890", "pk": 456, "_self": "self2", "_rid": "rid2", "_ts": 789012, "isCool": true},
|
||||
}
|
||||
|
||||
t.Run("Shoul execute simple SELECT", func(t *testing.T) {
|
||||
testQueryExecute(
|
||||
t,
|
||||
parsers.SelectStmt{
|
||||
Columns: []parsers.FieldPath{
|
||||
{Path: []string{"c", "id"}},
|
||||
{Path: []string{"c", "pk"}},
|
||||
},
|
||||
Table: parsers.Table{Value: "c"},
|
||||
},
|
||||
mockData,
|
||||
[]memoryexecutor.RowType{
|
||||
map[string]interface{}{"id": "12345", "pk": 123},
|
||||
map[string]interface{}{"id": "67890", "pk": 456},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Shoul execute SELECT with single WHERE condition", func(t *testing.T) {
|
||||
testQueryExecute(
|
||||
t,
|
||||
parsers.SelectStmt{
|
||||
Columns: []parsers.FieldPath{
|
||||
{Path: []string{"c", "id"}},
|
||||
},
|
||||
Table: parsers.Table{Value: "c"},
|
||||
Filters: parsers.ComparisonExpression{
|
||||
Operation: "=",
|
||||
Left: parsers.FieldPath{Path: []string{"c", "isCool"}},
|
||||
Right: parsers.Constant{Type: parsers.ConstantTypeBoolean, Value: true},
|
||||
},
|
||||
},
|
||||
mockData,
|
||||
[]memoryexecutor.RowType{
|
||||
map[string]interface{}{"id": "67890"},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Should execute SELECT with multiple WHERE conditions", func(t *testing.T) {
|
||||
testQueryExecute(
|
||||
t,
|
||||
parsers.SelectStmt{
|
||||
Columns: []parsers.FieldPath{
|
||||
{Path: []string{"c", "id"}},
|
||||
{Path: []string{"c", "_self"}, Alias: "self"},
|
||||
},
|
||||
Table: parsers.Table{Value: "c"},
|
||||
Filters: parsers.LogicalExpression{
|
||||
Operation: parsers.LogicalExpressionTypeAnd,
|
||||
Expressions: []interface{}{
|
||||
parsers.ComparisonExpression{
|
||||
Operation: "=",
|
||||
Left: parsers.FieldPath{Path: []string{"c", "id"}},
|
||||
Right: parsers.Constant{Type: parsers.ConstantTypeString, Value: "67890"},
|
||||
},
|
||||
parsers.ComparisonExpression{
|
||||
Operation: "=",
|
||||
Left: parsers.FieldPath{Path: []string{"c", "pk"}},
|
||||
Right: parsers.Constant{Type: parsers.ConstantTypeInteger, Value: 456},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
mockData,
|
||||
[]memoryexecutor.RowType{
|
||||
map[string]interface{}{"id": "67890", "self": "self2"},
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue