From 7376a5749dbb4581eff2b192c4b6c2ae7137e173 Mon Sep 17 00:00:00 2001 From: Artyom Antonov Date: Thu, 2 May 2024 19:40:22 +0500 Subject: [PATCH 1/2] Add field names to struct literal --- expr/functions/sortBy/function_test.go | 51 ++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/expr/functions/sortBy/function_test.go b/expr/functions/sortBy/function_test.go index e9a29949c..318412cd3 100644 --- a/expr/functions/sortBy/function_test.go +++ b/expr/functions/sortBy/function_test.go @@ -28,75 +28,100 @@ func TestFunction(t *testing.T) { tests := []th.EvalTestItem{ { +<<<<<<< HEAD "sortByTotal(metric1)", map[parser.MetricRequest][]*types.MetricData{ +======= + Target: "sortByTotal(metric1)", + M: map[parser.MetricRequest][]*types.MetricData{ +>>>>>>> 6447e792 (Add field names to struct literal) {Metric: "metric1", From: 0, Until: 1}: { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{5, 5, 5, 5, 5, 5}, 1, now32), types.MakeMetricData("metricC", []float64{4, 4, 5, 5, 4, 4}, 1, now32), }, }, - []*types.MetricData{ + Want: []*types.MetricData{ types.MakeMetricData("metricB", []float64{5, 5, 5, 5, 5, 5}, 1, now32), types.MakeMetricData("metricC", []float64{4, 4, 5, 5, 4, 4}, 1, now32), types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), }, }, { +<<<<<<< HEAD "sortByMaxima(metric*)", map[parser.MetricRequest][]*types.MetricData{ +======= + Target: "sortByMaxima(metric*)", + M: map[parser.MetricRequest][]*types.MetricData{ +>>>>>>> 6447e792 (Add field names to struct literal) {Metric: "metric*", From: 0, Until: 1}: { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{5, 5, 5, 5, 5, 5}, 1, now32), types.MakeMetricData("metricC", []float64{2, 2, 10, 5, 2, 2}, 1, now32), }, }, - []*types.MetricData{ + Want: []*types.MetricData{ types.MakeMetricData("metricC", []float64{2, 2, 10, 5, 2, 2}, 1, now32), types.MakeMetricData("metricB", []float64{5, 5, 5, 5, 5, 5}, 1, now32), types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), }, }, { +<<<<<<< HEAD "sortByMinima(metric*)", map[parser.MetricRequest][]*types.MetricData{ +======= + Target: "sortByMinima(metric*)", + M: map[parser.MetricRequest][]*types.MetricData{ +>>>>>>> 6447e792 (Add field names to struct literal) {Metric: "metric*", From: 0, Until: 1}: { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{3, 4, 5, 6, 7, 8}, 1, now32), types.MakeMetricData("metricC", []float64{4, 4, 5, 5, 6, 6}, 1, now32), }, }, - []*types.MetricData{ + Want: []*types.MetricData{ types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{3, 4, 5, 6, 7, 8}, 1, now32), types.MakeMetricData("metricC", []float64{4, 4, 5, 5, 6, 6}, 1, now32), }, }, { +<<<<<<< HEAD "sortBy(metric*)", map[parser.MetricRequest][]*types.MetricData{ +======= + Target: "sortBy(metric*)", + M: map[parser.MetricRequest][]*types.MetricData{ +>>>>>>> 6447e792 (Add field names to struct literal) {Metric: "metric*", From: 0, Until: 1}: { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{3, 4, 5, 6, 7, 8}, 1, now32), types.MakeMetricData("metricC", []float64{1, 2, 3, 4, 5, 6}, 1, now32), }, }, - []*types.MetricData{ + Want: []*types.MetricData{ types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricC", []float64{1, 2, 3, 4, 5, 6}, 1, now32), types.MakeMetricData("metricB", []float64{3, 4, 5, 6, 7, 8}, 1, now32), }, }, { +<<<<<<< HEAD "sortBy(metric*, 'median')", map[parser.MetricRequest][]*types.MetricData{ +======= + Target: "sortBy(metric*, 'median')", + M: map[parser.MetricRequest][]*types.MetricData{ +>>>>>>> 6447e792 (Add field names to struct literal) {Metric: "metric*", From: 0, Until: 1}: { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{4, 4, 5, 5, 6, 6}, 1, now32), types.MakeMetricData("metricC", []float64{3, 4, 5, 6, 7, 8}, 1, now32), }, }, - []*types.MetricData{ + Want: []*types.MetricData{ types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{4, 4, 5, 5, 6, 6}, 1, now32), types.MakeMetricData("metricC", []float64{3, 4, 5, 6, 7, 8}, 1, now32), @@ -104,15 +129,20 @@ func TestFunction(t *testing.T) { }, { +<<<<<<< HEAD "sortBy(metric*, 'max', true)", map[parser.MetricRequest][]*types.MetricData{ +======= + Target: "sortBy(metric*, 'max', true)", + M: map[parser.MetricRequest][]*types.MetricData{ +>>>>>>> 6447e792 (Add field names to struct literal) {Metric: "metric*", From: 0, Until: 1}: { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{3, 4, 5, 6, 7, 8}, 1, now32), types.MakeMetricData("metricC", []float64{4, 4, 5, 5, 6, 6}, 1, now32), }, }, - []*types.MetricData{ + Want: []*types.MetricData{ types.MakeMetricData("metricB", []float64{3, 4, 5, 6, 7, 8}, 1, now32), types.MakeMetricData("metricC", []float64{4, 4, 5, 5, 6, 6}, 1, now32), types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), @@ -135,16 +165,21 @@ func TestErrorInvalidConsolidationFunction(t *testing.T) { tests := []th.EvalTestItemWithError{ { +<<<<<<< HEAD "sortBy(metric*, 'test')", map[parser.MetricRequest][]*types.MetricData{ +======= + Target: "sortBy(metric*, 'test')", + M: map[parser.MetricRequest][]*types.MetricData{ +>>>>>>> 6447e792 (Add field names to struct literal) {Metric: "metric*", From: 0, Until: 1}: { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{4, 4, 5, 5, 6, 6}, 1, now32), types.MakeMetricData("metricC", []float64{3, 4, 5, 6, 7, 8}, 1, now32), }, }, - nil, - consolidations.ErrInvalidConsolidationFunc, + Want: nil, + Error: consolidations.ErrInvalidConsolidationFunc, }, } From 7311204680a1203af29fb8ac53b9f24d583c80f3 Mon Sep 17 00:00:00 2001 From: Artyom Antonov Date: Thu, 2 May 2024 20:55:10 +0500 Subject: [PATCH 2/2] keep only NaN series at the bottom of the result in sortBy functions --- cmd/mockbackend/testcases/sortBy/sortBy.yaml | 56 ++++++++++++++++++++ expr/functions/sortBy/function.go | 4 ++ expr/functions/sortBy/function_test.go | 55 +++++++------------ 3 files changed, 78 insertions(+), 37 deletions(-) create mode 100644 cmd/mockbackend/testcases/sortBy/sortBy.yaml diff --git a/cmd/mockbackend/testcases/sortBy/sortBy.yaml b/cmd/mockbackend/testcases/sortBy/sortBy.yaml new file mode 100644 index 000000000..ea19ca950 --- /dev/null +++ b/cmd/mockbackend/testcases/sortBy/sortBy.yaml @@ -0,0 +1,56 @@ +version: "v1" +test: + apps: + - name: "carbonapi" + binary: "./carbonapi" + args: + - "-config" + - "./cmd/mockbackend/carbonapi_singlebackend.yaml" + queries: + - endpoint: "http://127.0.0.1:8081" + delay: 1 + type: "GET" + URL: "/render?format=json&target=limit(sortByMaxima(metric*),2)" + expectedResponse: + httpCode: 200 + contentType: "application/json" + expectedResults: + - metrics: + - target: "metricZ2" + datapoints: [[8.0, 1],[1.0, 2],[1.0, 3],[7.0, 4],[4.0, 5]] + - target: "metricZ1" + datapoints: [[4.0, 1],[6.0, 2],[2.0, 3],[2.0, 4],[3.0, 5]] + - endpoint: "http://127.0.0.1:8081" + delay: 1 + type: "GET" + URL: "/render?format=json&target=limit(sortByMaxima(metric*),3)" + expectedResponse: + httpCode: 200 + contentType: "application/json" + expectedResults: + - metrics: + - target: "metricZ2" + datapoints: [[8.0, 1],[1.0, 2],[1.0, 3],[7.0, 4],[4.0, 5]] + - target: "metricZ1" + datapoints: [[4.0, 1],[6.0, 2],[2.0, 3],[2.0, 4],[3.0, 5]] + - target: "metricNaN" + datapoints: [["null", 1],["null", 2],["null", 3],["null", 4],["null", 5]] + +listeners: + - address: ":9070" + expressions: + "metric*": + pathExpression: "metric*" + data: + - metricName: "metricNaN" + values: [.NaN, .NaN, .NaN, .NaN, .NaN] + step: 1 + startTime: 1 + - metricName: "metricZ1" + values: [4.0, 6.0, 2.0, 2.0, 3.0] + step: 1 + startTime: 1 + - metricName: "metricZ2" + values: [8.0, 1.0, 1.0, 7.0, 4.0] + step: 1 + startTime: 1 diff --git a/expr/functions/sortBy/function.go b/expr/functions/sortBy/function.go index b731d414c..f2b5da98b 100644 --- a/expr/functions/sortBy/function.go +++ b/expr/functions/sortBy/function.go @@ -3,6 +3,7 @@ package sortBy import ( "context" "fmt" + "math" "sort" "github.com/go-graphite/carbonapi/expr/consolidations" @@ -80,6 +81,9 @@ func doSort(aggFuncName string, ascending bool, original []*types.MetricData) [] for i, a := range arg { vals[i] = consolidations.SummarizeValues(aggFuncName, a.Values, a.XFilesFactor) + if math.IsNaN(vals[i]) { + vals[i] = math.Inf(-1) + } } if ascending { diff --git a/expr/functions/sortBy/function_test.go b/expr/functions/sortBy/function_test.go index 318412cd3..91f222fa9 100644 --- a/expr/functions/sortBy/function_test.go +++ b/expr/functions/sortBy/function_test.go @@ -1,6 +1,7 @@ package sortBy import ( + "math" "testing" "time" @@ -14,7 +15,8 @@ import ( ) var ( - md []interfaces.FunctionMetadata = New("") + md []interfaces.FunctionMetadata = New("") + nan = math.NaN() ) func init() { @@ -28,13 +30,8 @@ func TestFunction(t *testing.T) { tests := []th.EvalTestItem{ { -<<<<<<< HEAD - "sortByTotal(metric1)", - map[parser.MetricRequest][]*types.MetricData{ -======= Target: "sortByTotal(metric1)", M: map[parser.MetricRequest][]*types.MetricData{ ->>>>>>> 6447e792 (Add field names to struct literal) {Metric: "metric1", From: 0, Until: 1}: { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{5, 5, 5, 5, 5, 5}, 1, now32), @@ -48,13 +45,8 @@ func TestFunction(t *testing.T) { }, }, { -<<<<<<< HEAD - "sortByMaxima(metric*)", - map[parser.MetricRequest][]*types.MetricData{ -======= Target: "sortByMaxima(metric*)", M: map[parser.MetricRequest][]*types.MetricData{ ->>>>>>> 6447e792 (Add field names to struct literal) {Metric: "metric*", From: 0, Until: 1}: { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{5, 5, 5, 5, 5, 5}, 1, now32), @@ -68,13 +60,8 @@ func TestFunction(t *testing.T) { }, }, { -<<<<<<< HEAD - "sortByMinima(metric*)", - map[parser.MetricRequest][]*types.MetricData{ -======= Target: "sortByMinima(metric*)", M: map[parser.MetricRequest][]*types.MetricData{ ->>>>>>> 6447e792 (Add field names to struct literal) {Metric: "metric*", From: 0, Until: 1}: { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{3, 4, 5, 6, 7, 8}, 1, now32), @@ -88,13 +75,8 @@ func TestFunction(t *testing.T) { }, }, { -<<<<<<< HEAD - "sortBy(metric*)", - map[parser.MetricRequest][]*types.MetricData{ -======= Target: "sortBy(metric*)", M: map[parser.MetricRequest][]*types.MetricData{ ->>>>>>> 6447e792 (Add field names to struct literal) {Metric: "metric*", From: 0, Until: 1}: { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{3, 4, 5, 6, 7, 8}, 1, now32), @@ -108,13 +90,8 @@ func TestFunction(t *testing.T) { }, }, { -<<<<<<< HEAD - "sortBy(metric*, 'median')", - map[parser.MetricRequest][]*types.MetricData{ -======= Target: "sortBy(metric*, 'median')", M: map[parser.MetricRequest][]*types.MetricData{ ->>>>>>> 6447e792 (Add field names to struct literal) {Metric: "metric*", From: 0, Until: 1}: { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{4, 4, 5, 5, 6, 6}, 1, now32), @@ -127,15 +104,9 @@ func TestFunction(t *testing.T) { types.MakeMetricData("metricC", []float64{3, 4, 5, 6, 7, 8}, 1, now32), }, }, - { -<<<<<<< HEAD - "sortBy(metric*, 'max', true)", - map[parser.MetricRequest][]*types.MetricData{ -======= Target: "sortBy(metric*, 'max', true)", M: map[parser.MetricRequest][]*types.MetricData{ ->>>>>>> 6447e792 (Add field names to struct literal) {Metric: "metric*", From: 0, Until: 1}: { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{3, 4, 5, 6, 7, 8}, 1, now32), @@ -148,6 +119,21 @@ func TestFunction(t *testing.T) { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), }, }, + { + Target: "sortBy(metric*, 'max', true)", + M: map[parser.MetricRequest][]*types.MetricData{ + {Metric: "metric*", From: 0, Until: 1}: { + types.MakeMetricData("metricA", []float64{nan, nan, nan, nan, nan, nan}, 1, now32), + types.MakeMetricData("metricB", []float64{3, 4, 5, 6, 7, 8}, 1, now32), + types.MakeMetricData("metricC", []float64{4, 4, 5, 5, 6, 6}, 1, now32), + }, + }, + Want: []*types.MetricData{ + types.MakeMetricData("metricB", []float64{3, 4, 5, 6, 7, 8}, 1, now32), + types.MakeMetricData("metricC", []float64{4, 4, 5, 5, 6, 6}, 1, now32), + types.MakeMetricData("metricA", []float64{nan, nan, nan, nan, nan, nan}, 1, now32), + }, + }, } for _, tt := range tests { @@ -165,13 +151,8 @@ func TestErrorInvalidConsolidationFunction(t *testing.T) { tests := []th.EvalTestItemWithError{ { -<<<<<<< HEAD - "sortBy(metric*, 'test')", - map[parser.MetricRequest][]*types.MetricData{ -======= Target: "sortBy(metric*, 'test')", M: map[parser.MetricRequest][]*types.MetricData{ ->>>>>>> 6447e792 (Add field names to struct literal) {Metric: "metric*", From: 0, Until: 1}: { types.MakeMetricData("metricA", []float64{0, 0, 0, 0, 0, 0}, 1, now32), types.MakeMetricData("metricB", []float64{4, 4, 5, 5, 6, 6}, 1, now32),