mirror of
				https://github.com/pikami/cosmium.git
				synced 2025-11-04 00:41:02 +00:00 
			
		
		
		
	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},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										98
									
								
								query_executors/memory_executor/memory_executor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								query_executors/memory_executor/memory_executor.go
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										100
									
								
								query_executors/memory_executor/memory_executor_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								query_executors/memory_executor/memory_executor_test.go
									
									
									
									
									
										Normal file
									
								
							@ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user