mirror of
https://github.com/pikami/cosmium.git
synced 2024-11-25 06:57:10 +00:00
Added support for 'ORDER BY'
This commit is contained in:
parent
eb7b3045d2
commit
7339e06eee
@ -25,12 +25,20 @@ const (
|
|||||||
SelectItemTypeArray
|
SelectItemTypeArray
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type OrderDirection int
|
||||||
|
|
||||||
|
const (
|
||||||
|
OrderDirectionAsc OrderDirection = iota
|
||||||
|
OrderDirectionDesc
|
||||||
|
)
|
||||||
|
|
||||||
type SelectStmt struct {
|
type SelectStmt struct {
|
||||||
SelectItems []SelectItem
|
SelectItems []SelectItem
|
||||||
Table Table
|
Table Table
|
||||||
Filters interface{}
|
Filters interface{}
|
||||||
Count int
|
Count int
|
||||||
Parameters map[string]interface{}
|
Parameters map[string]interface{}
|
||||||
|
OrderExpressions []OrderExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
type Table struct {
|
type Table struct {
|
||||||
@ -60,3 +68,8 @@ type Constant struct {
|
|||||||
Type ConstantType
|
Type ConstantType
|
||||||
Value interface{}
|
Value interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OrderExpression struct {
|
||||||
|
SelectItem SelectItem
|
||||||
|
Direction OrderDirection
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@ package nosql
|
|||||||
|
|
||||||
import "github.com/pikami/cosmium/parsers"
|
import "github.com/pikami/cosmium/parsers"
|
||||||
|
|
||||||
func makeSelectStmt(columns, table, whereClause interface{}, count interface{}) (parsers.SelectStmt, error) {
|
func makeSelectStmt(columns, table, whereClause interface{}, count interface{}, orderList interface{}) (parsers.SelectStmt, error) {
|
||||||
selectStmt := parsers.SelectStmt{
|
selectStmt := parsers.SelectStmt{
|
||||||
SelectItems: columns.([]parsers.SelectItem),
|
SelectItems: columns.([]parsers.SelectItem),
|
||||||
Table: table.(parsers.Table),
|
Table: table.(parsers.Table),
|
||||||
@ -21,6 +21,10 @@ func makeSelectStmt(columns, table, whereClause interface{}, count interface{})
|
|||||||
selectStmt.Count = n
|
selectStmt.Count = n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if orderExpressions, ok := orderList.([]parsers.OrderExpression); ok {
|
||||||
|
selectStmt.OrderExpressions = orderExpressions
|
||||||
|
}
|
||||||
|
|
||||||
return selectStmt, nil
|
return selectStmt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,6 +78,33 @@ func makeSelectObject(field interface{}, other_fields interface{}) (parsers.Sele
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeOrderByClause(ex1 interface{}, others interface{}) ([]parsers.OrderExpression, error) {
|
||||||
|
othersArray := others.([]interface{})
|
||||||
|
orderList := make([]parsers.OrderExpression, len(othersArray)+1)
|
||||||
|
orderList[0] = ex1.(parsers.OrderExpression)
|
||||||
|
|
||||||
|
for i, v := range othersArray {
|
||||||
|
if col, ok := v.(parsers.OrderExpression); ok {
|
||||||
|
orderList[i+1] = col
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return orderList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeOrderExpression(field interface{}, order interface{}) (parsers.OrderExpression, error) {
|
||||||
|
value := parsers.OrderExpression{
|
||||||
|
SelectItem: field.(parsers.SelectItem),
|
||||||
|
Direction: parsers.OrderDirectionAsc,
|
||||||
|
}
|
||||||
|
|
||||||
|
if orderValue, ok := order.(parsers.OrderDirection); ok {
|
||||||
|
value.Direction = orderValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
func joinStrings(array []interface{}) string {
|
func joinStrings(array []interface{}) string {
|
||||||
var stringsArray []string
|
var stringsArray []string
|
||||||
for _, elem := range array {
|
for _, elem := range array {
|
||||||
@ -106,8 +137,9 @@ Input <- selectStmt:SelectStmt {
|
|||||||
|
|
||||||
SelectStmt <- Select ws topClause:TopClause? ws columns:Selection ws
|
SelectStmt <- Select ws topClause:TopClause? ws columns:Selection ws
|
||||||
From ws table:TableName ws
|
From ws table:TableName ws
|
||||||
whereClause:(ws Where ws condition:Condition { return condition, nil })? {
|
whereClause:(ws Where ws condition:Condition { return condition, nil })?
|
||||||
return makeSelectStmt(columns, table, whereClause, topClause)
|
orderByClause:OrderByClause? {
|
||||||
|
return makeSelectStmt(columns, table, whereClause, topClause, orderByClause)
|
||||||
}
|
}
|
||||||
|
|
||||||
TopClause <- Top ws count:Integer {
|
TopClause <- Top ws count:Integer {
|
||||||
@ -193,6 +225,22 @@ ComparisonExpression <- "(" ws ex:OrExpression ws ")" { return ex, nil }
|
|||||||
return parsers.ComparisonExpression{Left:left,Right:right,Operation:string(op.([]uint8))}, nil
|
return parsers.ComparisonExpression{Left:left,Right:right,Operation:string(op.([]uint8))}, nil
|
||||||
} / ex:BooleanLiteral { return ex, nil }
|
} / ex:BooleanLiteral { return ex, nil }
|
||||||
|
|
||||||
|
OrderByClause <- OrderBy ws ex1:OrderExpression others:(ws "," ws ex:OrderExpression { return ex, nil })* {
|
||||||
|
return makeOrderByClause(ex1, others)
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderExpression <- field:SelectProperty ws order:OrderDirection? {
|
||||||
|
return makeOrderExpression(field, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderDirection <- ("ASC" / "asc" / "DESC" / "desc") {
|
||||||
|
switch string(c.text) {
|
||||||
|
case "DESC", "desc":
|
||||||
|
return parsers.OrderDirectionDesc, nil
|
||||||
|
}
|
||||||
|
return parsers.OrderDirectionAsc, nil
|
||||||
|
}
|
||||||
|
|
||||||
Select <- ("select" / "SELECT")
|
Select <- ("select" / "SELECT")
|
||||||
|
|
||||||
Top <- ("top" / "TOP")
|
Top <- ("top" / "TOP")
|
||||||
@ -207,6 +255,8 @@ And <- ("and" / "AND")
|
|||||||
|
|
||||||
Or <- ("or" / "OR")
|
Or <- ("or" / "OR")
|
||||||
|
|
||||||
|
OrderBy <- ("order" / "ORDER") ws ("by" / "BY")
|
||||||
|
|
||||||
ComparisonOperator <- "=" / "!=" / "<" / "<=" / ">" / ">=" {
|
ComparisonOperator <- "=" / "!=" / "<" / "<=" / ">" / ">=" {
|
||||||
return string(c.text), nil
|
return string(c.text), nil
|
||||||
}
|
}
|
||||||
|
@ -132,6 +132,30 @@ func Test_Parse(t *testing.T) {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Should parse SELECT with ORDER BY", func(t *testing.T) {
|
||||||
|
testQueryParse(
|
||||||
|
t,
|
||||||
|
`SELECT c.id, c["pk"] FROM c ORDER BY c.id DESC, c.pk`,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "id"}},
|
||||||
|
{Path: []string{"c", "pk"}},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
OrderExpressions: []parsers.OrderExpression{
|
||||||
|
{
|
||||||
|
SelectItem: parsers.SelectItem{Path: []string{"c", "id"}},
|
||||||
|
Direction: parsers.OrderDirectionDesc,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SelectItem: parsers.SelectItem{Path: []string{"c", "pk"}},
|
||||||
|
Direction: parsers.OrderDirectionAsc,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Should parse SELECT with single WHERE condition", func(t *testing.T) {
|
t.Run("Should parse SELECT with single WHERE condition", func(t *testing.T) {
|
||||||
testQueryParse(
|
testQueryParse(
|
||||||
t,
|
t,
|
||||||
|
@ -2,6 +2,8 @@ package memoryexecutor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pikami/cosmium/parsers"
|
"github.com/pikami/cosmium/parsers"
|
||||||
)
|
)
|
||||||
@ -12,14 +14,19 @@ type ExpressionType interface{}
|
|||||||
func Execute(query parsers.SelectStmt, data []RowType) []RowType {
|
func Execute(query parsers.SelectStmt, data []RowType) []RowType {
|
||||||
result := make([]RowType, 0)
|
result := make([]RowType, 0)
|
||||||
|
|
||||||
// Iterate over each row in the data
|
// Apply Filter
|
||||||
for _, row := range data {
|
for _, row := range data {
|
||||||
// Check if the row satisfies the filter conditions
|
// Check if the row satisfies the filter conditions
|
||||||
if evaluateFilters(query.Filters, query.Parameters, row) {
|
if evaluateFilters(query.Filters, query.Parameters, row) {
|
||||||
result = append(result, selectRow(query.SelectItems, row))
|
result = append(result, row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply order
|
||||||
|
if query.OrderExpressions != nil && len(query.OrderExpressions) > 0 {
|
||||||
|
orderBy(query.OrderExpressions, result)
|
||||||
|
}
|
||||||
|
|
||||||
// Apply result limit
|
// Apply result limit
|
||||||
if query.Count > 0 {
|
if query.Count > 0 {
|
||||||
count := func() int {
|
count := func() int {
|
||||||
@ -31,7 +38,13 @@ func Execute(query parsers.SelectStmt, data []RowType) []RowType {
|
|||||||
result = result[:count]
|
result = result[:count]
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
// Apply select
|
||||||
|
selectedData := make([]RowType, 0)
|
||||||
|
for _, row := range result {
|
||||||
|
selectedData = append(selectedData, selectRow(query.SelectItems, row))
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedData
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectRow(selectItems []parsers.SelectItem, row RowType) interface{} {
|
func selectRow(selectItems []parsers.SelectItem, row RowType) interface{} {
|
||||||
@ -159,3 +172,42 @@ func getExpressionParameterValue(
|
|||||||
// TODO: Handle error
|
// TODO: Handle error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func orderBy(orderBy []parsers.OrderExpression, data []RowType) {
|
||||||
|
less := func(i, j int) bool {
|
||||||
|
for _, order := range orderBy {
|
||||||
|
val1 := getFieldValue(order.SelectItem, data[i])
|
||||||
|
val2 := getFieldValue(order.SelectItem, data[j])
|
||||||
|
|
||||||
|
cmp := compareValues(val1, val2)
|
||||||
|
if cmp != 0 {
|
||||||
|
if order.Direction == parsers.OrderDirectionDesc {
|
||||||
|
return cmp > 0
|
||||||
|
}
|
||||||
|
return cmp < 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i < j
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.SliceStable(data, less)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareValues(val1, val2 interface{}) int {
|
||||||
|
switch val1 := val1.(type) {
|
||||||
|
case int:
|
||||||
|
val2 := val2.(int)
|
||||||
|
if val1 < val2 {
|
||||||
|
return -1
|
||||||
|
} else if val1 > val2 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
case string:
|
||||||
|
val2 := val2.(string)
|
||||||
|
return strings.Compare(val1, val2)
|
||||||
|
// TODO: Add more types
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -126,7 +126,7 @@ func Test_Execute(t *testing.T) {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should parse SELECT object", func(t *testing.T) {
|
t.Run("Should execute SELECT object", func(t *testing.T) {
|
||||||
testQueryExecute(
|
testQueryExecute(
|
||||||
t,
|
t,
|
||||||
parsers.SelectStmt{
|
parsers.SelectStmt{
|
||||||
@ -152,6 +152,36 @@ func Test_Execute(t *testing.T) {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Should execute SELECT with ORDER BY", func(t *testing.T) {
|
||||||
|
testQueryExecute(
|
||||||
|
t,
|
||||||
|
parsers.SelectStmt{
|
||||||
|
SelectItems: []parsers.SelectItem{
|
||||||
|
{Path: []string{"c", "id"}},
|
||||||
|
{Path: []string{"c", "pk"}},
|
||||||
|
},
|
||||||
|
Table: parsers.Table{Value: "c"},
|
||||||
|
OrderExpressions: []parsers.OrderExpression{
|
||||||
|
{
|
||||||
|
SelectItem: parsers.SelectItem{Path: []string{"c", "pk"}},
|
||||||
|
Direction: parsers.OrderDirectionAsc,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SelectItem: parsers.SelectItem{Path: []string{"c", "id"}},
|
||||||
|
Direction: parsers.OrderDirectionDesc,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mockData,
|
||||||
|
[]memoryexecutor.RowType{
|
||||||
|
map[string]interface{}{"id": "12345", "pk": 123},
|
||||||
|
map[string]interface{}{"id": "67890", "pk": 456},
|
||||||
|
map[string]interface{}{"id": "456", "pk": 456},
|
||||||
|
map[string]interface{}{"id": "123", "pk": 456},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Should execute SELECT with single WHERE condition", func(t *testing.T) {
|
t.Run("Should execute SELECT with single WHERE condition", func(t *testing.T) {
|
||||||
testQueryExecute(
|
testQueryExecute(
|
||||||
t,
|
t,
|
||||||
|
Loading…
Reference in New Issue
Block a user