Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sorting #79

Merged
merged 19 commits into from
Sep 10, 2022
12 changes: 5 additions & 7 deletions cmd/bargraph.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ go run . bars -sz -m "\[(.+?)\].*\" (\d+)" -e "{$ {buckettime {1} year nginx} {2

func bargraphFunction(c *cli.Context) error {
var (
stacked = c.Bool("stacked")
reverseSort = c.Bool("reverse")
stacked = c.Bool("stacked")
sortName = c.String(helpers.DefaultSortFlag.Name)
)

counter := aggregation.NewSubKeyCounter()
Expand All @@ -26,12 +26,13 @@ func bargraphFunction(c *cli.Context) error {

batcher := helpers.BuildBatcherFromArguments(c)
ext := helpers.BuildExtractorFromArguments(c, batcher)
sorter := helpers.BuildSorterOrFail(sortName)

helpers.RunAggregationLoop(ext, counter, func() {
line := 0

writer.SetKeys(counter.SubKeys()...)
for _, row := range counter.ItemsSorted(reverseSort) {
for _, row := range counter.ItemsSorted(sorter) {
writer.WriteBar(line, row.Name, row.Item.Items()...)
line++
}
Expand Down Expand Up @@ -61,10 +62,7 @@ func bargraphCommand() *cli.Command {
Aliases: []string{"s"},
Usage: "Display bargraph as stacked",
},
&cli.BoolFlag{
Name: "reverse",
Usage: "Reverses the display sort-order",
},
helpers.DefaultSortFlag,
},
})
}
16 changes: 15 additions & 1 deletion cmd/heatmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ func heatmapFunction(c *cli.Context) error {
minVal = c.Int64("min")
maxFixed = c.IsSet("max")
maxVal = c.Int64("max")
sortRows = c.String("sort-rows")
sortCols = c.String("sort-cols")
)

counter := aggregation.NewTable(delim)

batcher := helpers.BuildBatcherFromArguments(c)
ext := helpers.BuildExtractorFromArguments(c, batcher)
rowSorter := helpers.BuildSorterOrFail(sortRows)
colSorter := helpers.BuildSorterOrFail(sortCols)

writer := termrenderers.NewHeatmap(multiterm.New(), numRows, numCols)

Expand All @@ -37,7 +41,7 @@ func heatmapFunction(c *cli.Context) error {
}

helpers.RunAggregationLoop(ext, counter, func() {
writer.WriteTable(counter)
writer.WriteTable(counter, rowSorter, colSorter)
writer.WriteFooter(0, helpers.FWriteExtractorSummary(ext, counter.ParseErrors(),
fmt.Sprintf("(R: %v; C: %v)", color.Wrapi(color.Yellow, counter.RowCount()), color.Wrapi(color.BrightBlue, counter.ColumnCount()))))
writer.WriteFooter(1, batcher.StatusString())
Expand Down Expand Up @@ -80,6 +84,16 @@ func heatmapCommand() *cli.Command {
Name: "max",
Usage: "Sets the upper bounds of the heatmap (default: auto)",
},
&cli.StringFlag{
Name: "sort-rows",
Usage: helpers.DefaultSortFlag.Usage,
Value: helpers.DefaultSortFlag.Value,
},
&cli.StringFlag{
Name: "sort-cols",
Usage: helpers.DefaultSortFlag.Usage,
Value: helpers.DefaultSortFlag.Value,
},
},
})
}
95 changes: 95 additions & 0 deletions cmd/helpers/sorting.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package helpers

import (
"errors"
"fmt"
"rare/pkg/aggregation/sorting"
"rare/pkg/logger"
"rare/pkg/stringSplitter"
"strings"

"github.com/urfave/cli/v2"
)

var DefaultSortFlag = &cli.StringFlag{
Name: "sort",
Usage: "Sorting method for display (value, text, numeric, contextual, date)",
Value: "numeric",
zix99 marked this conversation as resolved.
Show resolved Hide resolved
}

// Create a sort flag with a different default value
func DefaultSortFlagWithDefault(dflt string) *cli.StringFlag {
if _, err := lookupSorter(dflt); err != nil {
panic(err)
}

flag := *DefaultSortFlag
flag.Value = dflt
return &flag
}

func BuildSorterOrFail(fullName string) sorting.NameValueSorter {
sorter, err := BuildSorter(fullName)
if err != nil {
logger.Fatal(err)
}
return sorter
}

func BuildSorter(fullName string) (sorting.NameValueSorter, error) {
name, reverse, err := parseSort(fullName)
if err != nil {
return nil, fmt.Errorf("error parsing sort: %v", err)
}

sorter, err := lookupSorter(name)
if err != nil {
return nil, fmt.Errorf("unknown sort: %s", name)
}
if reverse {
sorter = sorting.Reverse(sorter)
}
return sorter, nil
}

func parseSort(name string) (realname string, reverse bool, err error) {
splitter := stringSplitter.Splitter{
S: name,
Delim: ":",
}

realname = strings.ToLower(splitter.Next())
reverse = (realname == "value") // Value defaults descending

if modifier, hasModifier := splitter.NextOk(); hasModifier {
switch strings.ToLower(modifier) {
case "rev", "reverse":
reverse = !reverse
case "desc":
reverse = true
case "asc":
reverse = false
default:
return "", false, errors.New("invalid sort modifier")
}
}

return
}

func lookupSorter(name string) (sorting.NameValueSorter, error) {
name = strings.ToLower(name)
switch name {
case "text", "":
return sorting.ValueNilSorter(sorting.ByName), nil
case "numeric":
return sorting.ValueNilSorter(sorting.ByNameSmart), nil
case "contextual", "context":
return sorting.ValueNilSorter(sorting.ByContextual()), nil
case "date":
return sorting.ValueNilSorter(sorting.ByDateWithContextual()), nil
case "value":
return sorting.ValueSorterEx(sorting.ByName), nil
}
return nil, errors.New("unknown sort")
}
93 changes: 93 additions & 0 deletions cmd/helpers/sorting_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package helpers

import (
"rare/pkg/aggregation/sorting"
"testing"

"github.com/stretchr/testify/assert"
)

func TestBuildSorter(t *testing.T) {
assert.NotNil(t, BuildSorterOrFail("text"))
assert.NotNil(t, BuildSorterOrFail("numeric"))
assert.NotNil(t, BuildSorterOrFail("contextual"))
assert.NotNil(t, BuildSorterOrFail("value"))
assert.NotNil(t, BuildSorterOrFail("value:reverse"))
}

func TestOrderResults(t *testing.T) {
assertSortEquals(t, "text", 1, 4, 2, 0, 3)
assertSortEquals(t, "text:asc", 1, 4, 2, 0, 3)
assertSortEquals(t, "text:reverse", 3, 0, 2, 4, 1)
assertSortEquals(t, "text:desc", 3, 0, 2, 4, 1)

assertSortEquals(t, "numeric", 1, 4, 2, 0, 3)
assertSortEquals(t, "numeric:asc", 1, 4, 2, 0, 3)
assertSortEquals(t, "numeric:reverse", 3, 0, 2, 4, 1)
assertSortEquals(t, "numeric:desc", 3, 0, 2, 4, 1)

assertSortEquals(t, "value", 3, 2, 1, 0, 4)
assertSortEquals(t, "value:desc", 3, 2, 1, 0, 4)
assertSortEquals(t, "value:reverse", 4, 0, 1, 2, 3)
assertSortEquals(t, "value:asc", 4, 0, 1, 2, 3)
}

func TestInvalidSortNames(t *testing.T) {
sorter, err := BuildSorter("bla")
assert.Nil(t, sorter)
assert.Error(t, err)

sorter, err = BuildSorter("numeric:bla")
assert.Nil(t, sorter)
assert.Error(t, err)
}

// Given a hardcoded set of values, and a sort name assert the order is as expected
func assertSortEquals(t *testing.T, sortName string, order ...int) {
sorter, err := BuildSorter(sortName)
assert.NoError(t, err)

type orderedPair struct {
sorting.NameValuePair
id int
}

vals := []orderedPair{
{sorting.NameValuePair{Name: "qef", Value: 5}, 0},
{sorting.NameValuePair{Name: "abc", Value: 12}, 1},
{sorting.NameValuePair{Name: "egf", Value: 52}, 2},
{sorting.NameValuePair{Name: "zac", Value: 52}, 3},
{sorting.NameValuePair{Name: "bbb", Value: 3}, 4},
}

if len(order) != len(vals) {
panic("bad test")
}

sorting.SortBy(vals, sorter, func(obj orderedPair) sorting.NameValuePair {
return obj.NameValuePair
})

for i := 0; i < len(vals); i++ {
assert.Equal(t, order[i], vals[i].id)
}

}

func TestDefaultSortResolves(t *testing.T) {
sortName, _, err := parseSort(DefaultSortFlag.Value)
assert.NoError(t, err)

sorter, sorterErr := lookupSorter(sortName)
assert.NoError(t, sorterErr)
assert.NotNil(t, sorter)
}

func TestBuildSortFlag(t *testing.T) {
flag := DefaultSortFlagWithDefault("contextual")
assert.Equal(t, "contextual", flag.Value)

assert.Panics(t, func() {
DefaultSortFlagWithDefault("fake")
})
}
36 changes: 12 additions & 24 deletions cmd/histo.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,16 @@ import (
"os"
"rare/cmd/helpers"
"rare/pkg/aggregation"
"rare/pkg/aggregation/sorting"
"rare/pkg/color"
"rare/pkg/multiterm"
"rare/pkg/multiterm/termrenderers"

"github.com/urfave/cli/v2"
)

func writeHistoOutput(writer *termrenderers.HistoWriter, counter *aggregation.MatchCounter, count int, reverse bool, sortByKey bool, atLeast int64) {
var items []aggregation.MatchPair
if sortByKey {
items = counter.ItemsSortedByKey(count, reverse)
} else {
items = counter.ItemsSorted(count, reverse)
}
func writeHistoOutput(writer *termrenderers.HistoWriter, counter *aggregation.MatchCounter, count int, sorter sorting.NameValueSorter, atLeast int64) {
items := counter.ItemsSortedBy(count, sorter)
line := 0
writer.UpdateSamples(counter.Count())
for _, match := range items {
Expand All @@ -32,12 +28,11 @@ func writeHistoOutput(writer *termrenderers.HistoWriter, counter *aggregation.Ma

func histoFunction(c *cli.Context) error {
var (
topItems = c.Int("n")
reverseSort = c.Bool("reverse")
sortByKey = c.Bool("sk")
atLeast = c.Int64("atleast")
extra = c.Bool("extra")
all = c.Bool("all")
topItems = c.Int("n")
atLeast = c.Int64("atleast")
extra = c.Bool("extra")
all = c.Bool("all")
sortName = c.String(helpers.DefaultSortFlag.Name)
)

counter := aggregation.NewCounter()
Expand All @@ -47,6 +42,7 @@ func histoFunction(c *cli.Context) error {

batcher := helpers.BuildBatcherFromArguments(c)
ext := helpers.BuildExtractorFromArguments(c, batcher)
sorter := helpers.BuildSorterOrFail(sortName)

progressString := func() string {
return helpers.FWriteExtractorSummary(ext,
Expand All @@ -55,7 +51,7 @@ func histoFunction(c *cli.Context) error {
}

helpers.RunAggregationLoop(ext, counter, func() {
writeHistoOutput(writer, counter, topItems, reverseSort, sortByKey, atLeast)
writeHistoOutput(writer, counter, topItems, sorter, atLeast)
writer.WriteFooter(0, progressString())
writer.WriteFooter(1, batcher.StatusString())
})
Expand All @@ -66,7 +62,7 @@ func histoFunction(c *cli.Context) error {
fmt.Println("Full Table:")
vterm := multiterm.NewVirtualTerm()
vWriter := termrenderers.NewHistogram(vterm, counter.GroupCount())
writeHistoOutput(vWriter, counter, counter.GroupCount(), reverseSort, sortByKey, atLeast)
writeHistoOutput(vWriter, counter, counter.GroupCount(), sorter, atLeast)

vterm.WriteToOutput(os.Stdout)
fmt.Println(progressString())
Expand Down Expand Up @@ -121,15 +117,7 @@ func histogramCommand() *cli.Command {
Usage: "Only show results if there are at least this many samples",
Value: 0,
},
&cli.BoolFlag{
Name: "reverse",
Usage: "Reverses the display sort-order",
},
&cli.BoolFlag{
Name: "sortkey",
Aliases: []string{"sk"},
Usage: "Sort by key, rather than value",
},
helpers.DefaultSortFlagWithDefault("value"),
},
})
}
Loading