/
card.go
109 lines (99 loc) · 3.21 KB
/
card.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Copyright 2020 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"errors"
"fmt"
"sort"
"strings"
"github.com/aclements/go-moremath/stats"
"github.com/jedib0t/go-pretty/table"
"github.com/urfave/cli/v2"
"github.com/chaos-mesh/horoscope/pkg/horoscope"
)
var (
cardinalitor *horoscope.Cardinalitor
cardOptions = &options.Card
)
func cardCommand() *cli.Command {
return &cli.Command{
Name: "card",
Usage: "test the cardinality estimations",
Action: testCard,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "columns",
Usage: "collect cardinality estimation error, format of 't1:c1,t1:c2,t2:c1...'",
Value: cardOptions.Columns,
Destination: &cardOptions.Columns,
},
&cli.StringFlag{
Name: "type",
Aliases: []string{"t"},
Usage: "emq means exact match queries(A = x); rge means range(lb <= A < ub)",
Value: cardOptions.Typ,
Destination: &cardOptions.Typ,
},
&cli.DurationFlag{
Name: "timeout",
Usage: "the timeout of testing",
Value: cardOptions.Timeout,
Destination: &cardOptions.Timeout,
},
},
}
}
func testCard(*cli.Context) error {
tableColumns := make(map[string][]string)
columns := strings.Split(cardOptions.Columns, ",")
if len(columns) == 0 {
return errors.New("columns are empty")
}
for _, pair := range columns {
values := strings.Split(pair, ".")
tb := values[0]
column := values[1]
if _, e := tableColumns[tb]; !e {
tableColumns[tb] = make([]string, 0)
}
tableColumns[tb] = append(tableColumns[tb], column)
}
cardinalitor = horoscope.NewCardinalitor(Pool.Executor(), tableColumns, horoscope.CardinalityQueryType(cardOptions.Typ), cardOptions.Timeout)
result, err := cardinalitor.Test()
if err != nil {
return err
}
fmt.Print(renderCardTable(result))
return nil
}
func renderCardTable(coll map[string]map[string]map[string]*horoscope.Metrics) string {
t := table.NewWriter()
t.AppendHeader(table.Row{"Table", "Column", "Type", "<= 2", "<= 3", "<= 4", "<= 5", "<= 6", "<= 7", "<= 8", "<= 9", "<= 10", "> 10", "max q-error"})
for tableName, tbl := range coll {
for columnName, mt := range tbl {
for typeName, m := range mt {
s := &stats.Sample{Xs: m.Values}
s.Sort()
c2, c3, c4, c5, c6, c7, c8, c9, c10 := countOf(s, 2), countOf(s, 3), countOf(s, 4), countOf(s, 5), countOf(s, 6), countOf(s, 7), countOf(s, 8), countOf(s, 9), countOf(s, 10)
cb10, max := len(m.Values)-c10, s.Quantile(1)
t.AppendRow(table.Row{tableName, columnName, typeName, c2, c3 - c2, c4 - c3, c5 - c4, c6 - c5, c7 - c6, c8 - c7, c9 - c8, c10 - c9, cb10, max})
}
}
}
return t.Render()
}
func countOf(s *stats.Sample, f float64) int {
return sort.Search(len(s.Xs), func(i int) bool {
return s.Xs[i] > f
})
}