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
|
SelectItems []SelectItem
|
||||||
Table Table
|
Table Table
|
||||||
Filters interface{}
|
Filters interface{}
|
||||||
|
Distinct bool
|
||||||
Count int
|
Count int
|
||||||
Parameters map[string]interface{}
|
Parameters map[string]interface{}
|
||||||
OrderExpressions []OrderExpression
|
OrderExpressions []OrderExpression
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,7 +3,11 @@ package nosql
|
||||||
|
|
||||||
import "github.com/pikami/cosmium/parsers"
|
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{
|
selectStmt := parsers.SelectStmt{
|
||||||
SelectItems: columns.([]parsers.SelectItem),
|
SelectItems: columns.([]parsers.SelectItem),
|
||||||
Table: table.(parsers.Table),
|
Table: table.(parsers.Table),
|
||||||
|
@ -14,6 +18,10 @@ func makeSelectStmt(columns, table, whereClause interface{}, count interface{},
|
||||||
selectStmt.Filters = v
|
selectStmt.Filters = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if distinctClause != nil {
|
||||||
|
selectStmt.Distinct = true
|
||||||
|
}
|
||||||
|
|
||||||
if n, ok := count.(int); ok {
|
if n, ok := count.(int); ok {
|
||||||
selectStmt.Count = n
|
selectStmt.Count = n
|
||||||
}
|
}
|
||||||
|
@ -136,13 +144,17 @@ Input <- selectStmt:SelectStmt {
|
||||||
return selectStmt, nil
|
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
|
From ws table:TableName ws
|
||||||
whereClause:(ws Where ws condition:Condition { return condition, nil })?
|
whereClause:(ws Where ws condition:Condition { return condition, nil })?
|
||||||
orderByClause:OrderByClause? {
|
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 {
|
TopClause <- Top ws count:Integer {
|
||||||
return count, nil
|
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) {
|
t.Run("Should parse SELECT TOP", func(t *testing.T) {
|
||||||
testQueryParse(
|
testQueryParse(
|
||||||
t,
|
t,
|
||||||
|
|
|
@ -35,6 +35,18 @@ func Execute(query parsers.SelectStmt, data []RowType) []RowType {
|
||||||
ctx.orderBy(query.OrderExpressions, result)
|
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
|
// Apply result limit
|
||||||
if query.Count > 0 {
|
if query.Count > 0 {
|
||||||
count := func() int {
|
count := func() int {
|
||||||
|
@ -46,13 +58,7 @@ func Execute(query parsers.SelectStmt, data []RowType) []RowType {
|
||||||
result = result[:count]
|
result = result[:count]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply select
|
return result
|
||||||
selectedData := make([]RowType, 0)
|
|
||||||
for _, row := range result {
|
|
||||||
selectedData = append(selectedData, ctx.selectRow(query.SelectItems, row))
|
|
||||||
}
|
|
||||||
|
|
||||||
return selectedData
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c memoryExecutorContext) selectRow(selectItems []parsers.SelectItem, row RowType) interface{} {
|
func (c memoryExecutorContext) selectRow(selectItems []parsers.SelectItem, row RowType) interface{} {
|
||||||
|
@ -349,3 +355,23 @@ func compareValues(val1, val2 interface{}) int {
|
||||||
return 1
|
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) {
|
t.Run("Should execute SELECT TOP", func(t *testing.T) {
|
||||||
testQueryExecute(
|
testQueryExecute(
|
||||||
t,
|
t,
|
||||||
|
|
Loading…
Reference in New Issue