mirror of https://github.com/pikami/cosmium.git
Implement DISTINCT clause
This commit is contained in:
parent
f3f3966dd5
commit
5ff923ce2c
|
@ -4,6 +4,7 @@ type SelectStmt struct {
|
|||
SelectItems []SelectItem
|
||||
Table Table
|
||||
Filters interface{}
|
||||
Distinct bool
|
||||
Count int
|
||||
Parameters map[string]interface{}
|
||||
OrderExpressions []OrderExpression
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,24 +3,32 @@ package nosql
|
|||
|
||||
import "github.com/pikami/cosmium/parsers"
|
||||
|
||||
func makeSelectStmt(columns, table, whereClause interface{}, count interface{}, orderList interface{}) (parsers.SelectStmt, error) {
|
||||
func makeSelectStmt(
|
||||
columns, table,
|
||||
whereClause interface{}, distinctClause interface{},
|
||||
count interface{}, orderList interface{},
|
||||
) (parsers.SelectStmt, error) {
|
||||
selectStmt := parsers.SelectStmt{
|
||||
SelectItems: columns.([]parsers.SelectItem),
|
||||
Table: table.(parsers.Table),
|
||||
}
|
||||
|
||||
switch v := whereClause.(type) {
|
||||
case parsers.ComparisonExpression, parsers.LogicalExpression, parsers.Constant, parsers.SelectItem:
|
||||
selectStmt.Filters = v
|
||||
}
|
||||
switch v := whereClause.(type) {
|
||||
case parsers.ComparisonExpression, parsers.LogicalExpression, parsers.Constant, parsers.SelectItem:
|
||||
selectStmt.Filters = v
|
||||
}
|
||||
|
||||
if distinctClause != nil {
|
||||
selectStmt.Distinct = true
|
||||
}
|
||||
|
||||
if n, ok := count.(int); ok {
|
||||
selectStmt.Count = n
|
||||
}
|
||||
|
||||
if orderExpressions, ok := orderList.([]parsers.OrderExpression); ok {
|
||||
selectStmt.OrderExpressions = orderExpressions
|
||||
}
|
||||
if orderExpressions, ok := orderList.([]parsers.OrderExpression); ok {
|
||||
selectStmt.OrderExpressions = orderExpressions
|
||||
}
|
||||
|
||||
return selectStmt, nil
|
||||
}
|
||||
|
@ -136,13 +144,17 @@ Input <- selectStmt:SelectStmt {
|
|||
return selectStmt, nil
|
||||
}
|
||||
|
||||
SelectStmt <- Select ws topClause:TopClause? ws columns:Selection ws
|
||||
SelectStmt <- Select ws
|
||||
distinctClause:DistinctClause? ws
|
||||
topClause:TopClause? ws columns:Selection ws
|
||||
From ws table:TableName ws
|
||||
whereClause:(ws Where ws condition:Condition { return condition, nil })?
|
||||
orderByClause:OrderByClause? {
|
||||
return makeSelectStmt(columns, table, whereClause, topClause, orderByClause)
|
||||
return makeSelectStmt(columns, table, whereClause, distinctClause, topClause, orderByClause)
|
||||
}
|
||||
|
||||
DistinctClause <- "DISTINCT"i
|
||||
|
||||
TopClause <- Top ws count:Integer {
|
||||
return count, nil
|
||||
}
|
||||
|
|
|
@ -22,6 +22,20 @@ func Test_Parse_Select(t *testing.T) {
|
|||
)
|
||||
})
|
||||
|
||||
t.Run("Should parse SELECT DISTINCT", func(t *testing.T) {
|
||||
testQueryParse(
|
||||
t,
|
||||
`SELECT DISTINCT c.id FROM c`,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"c", "id"}},
|
||||
},
|
||||
Table: parsers.Table{Value: "c"},
|
||||
Distinct: true,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Should parse SELECT TOP", func(t *testing.T) {
|
||||
testQueryParse(
|
||||
t,
|
||||
|
|
|
@ -35,6 +35,18 @@ func Execute(query parsers.SelectStmt, data []RowType) []RowType {
|
|||
ctx.orderBy(query.OrderExpressions, result)
|
||||
}
|
||||
|
||||
// Apply select
|
||||
selectedData := make([]RowType, 0)
|
||||
for _, row := range result {
|
||||
selectedData = append(selectedData, ctx.selectRow(query.SelectItems, row))
|
||||
}
|
||||
result = selectedData
|
||||
|
||||
// Apply distinct
|
||||
if query.Distinct {
|
||||
result = deduplicate(result)
|
||||
}
|
||||
|
||||
// Apply result limit
|
||||
if query.Count > 0 {
|
||||
count := func() int {
|
||||
|
@ -46,13 +58,7 @@ func Execute(query parsers.SelectStmt, data []RowType) []RowType {
|
|||
result = result[:count]
|
||||
}
|
||||
|
||||
// Apply select
|
||||
selectedData := make([]RowType, 0)
|
||||
for _, row := range result {
|
||||
selectedData = append(selectedData, ctx.selectRow(query.SelectItems, row))
|
||||
}
|
||||
|
||||
return selectedData
|
||||
return result
|
||||
}
|
||||
|
||||
func (c memoryExecutorContext) selectRow(selectItems []parsers.SelectItem, row RowType) interface{} {
|
||||
|
@ -349,3 +355,23 @@ func compareValues(val1, val2 interface{}) int {
|
|||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
func deduplicate(slice []RowType) []RowType {
|
||||
var result []RowType
|
||||
|
||||
for i := 0; i < len(slice); i++ {
|
||||
unique := true
|
||||
for j := 0; j < len(result); j++ {
|
||||
if compareValues(slice[i], result[j]) == 0 {
|
||||
unique = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if unique {
|
||||
result = append(result, slice[i])
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -35,6 +35,24 @@ func Test_Execute_Select(t *testing.T) {
|
|||
)
|
||||
})
|
||||
|
||||
t.Run("Should execute SELECT DISTINCT", func(t *testing.T) {
|
||||
testQueryExecute(
|
||||
t,
|
||||
parsers.SelectStmt{
|
||||
SelectItems: []parsers.SelectItem{
|
||||
{Path: []string{"c", "pk"}},
|
||||
},
|
||||
Table: parsers.Table{Value: "c"},
|
||||
Distinct: true,
|
||||
},
|
||||
mockData,
|
||||
[]memoryexecutor.RowType{
|
||||
map[string]interface{}{"pk": 123},
|
||||
map[string]interface{}{"pk": 456},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("Should execute SELECT TOP", func(t *testing.T) {
|
||||
testQueryExecute(
|
||||
t,
|
||||
|
|
Loading…
Reference in New Issue