diff --git a/pkg/services/alerting/conditions/reducer.go b/pkg/services/alerting/conditions/reducer.go index 9a5097b8dde..64d198d5c14 100644 --- a/pkg/services/alerting/conditions/reducer.go +++ b/pkg/services/alerting/conditions/reducer.go @@ -96,8 +96,12 @@ func (s *queryReducer) Reduce(series *tsdb.TimeSeries) null.Float { } case "diff": allNull, value = calculateDiff(series, allNull, value, diff) + case "diff_abs": + allNull, value = calculateDiff(series, allNull, value, diffAbs) case "percent_diff": allNull, value = calculateDiff(series, allNull, value, percentDiff) + case "percent_diff_abs": + allNull, value = calculateDiff(series, allNull, value, percentDiffAbs) case "count_non_null": for _, v := range series.Points { if isValid(v[0]) { @@ -141,8 +145,7 @@ func calculateDiff(series *tsdb.TimeSeries, allNull bool, value float64, fn func for i := 0; i < len(points); i++ { if isValid(points[i][0]) { allNull = false - val := fn(first, points[i][0].Float64) - value = math.Abs(val) + value = fn(first, points[i][0].Float64) break } } @@ -158,6 +161,14 @@ var diff = func(newest, oldest float64) float64 { return newest - oldest } -var percentDiff = func(newest, oldest float64) float64 { - return (newest - oldest) / oldest * 100 +var diffAbs = func(newest, oldest float64) float64 { + return math.Abs(newest - oldest) +} + +var percentDiff = func(newest, oldest float64) float64 { + return (newest - oldest) / math.Abs(oldest) * 100 +} + +var percentDiffAbs = func(newest, oldest float64) float64 { + return math.Abs((newest - oldest) / oldest * 100) } diff --git a/pkg/services/alerting/conditions/reducer_test.go b/pkg/services/alerting/conditions/reducer_test.go index 7f522f24f6b..1794e81b17e 100644 --- a/pkg/services/alerting/conditions/reducer_test.go +++ b/pkg/services/alerting/conditions/reducer_test.go @@ -56,7 +56,7 @@ func TestSimpleReducer(t *testing.T) { Convey("median should ignore null values", func() { reducer := newSimpleReducer("median") series := &tsdb.TimeSeries{ - Name: "test time serie", + Name: "test time series", } series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1)) @@ -79,7 +79,7 @@ func TestSimpleReducer(t *testing.T) { Convey("avg with only nulls", func() { reducer := newSimpleReducer("avg") series := &tsdb.TimeSeries{ - Name: "test time serie", + Name: "test time series", } series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1)) @@ -90,7 +90,7 @@ func TestSimpleReducer(t *testing.T) { Convey("with null values and real values", func() { reducer := newSimpleReducer("count_non_null") series := &tsdb.TimeSeries{ - Name: "test time serie", + Name: "test time series", } series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1)) @@ -105,7 +105,7 @@ func TestSimpleReducer(t *testing.T) { Convey("with null values", func() { reducer := newSimpleReducer("count_non_null") series := &tsdb.TimeSeries{ - Name: "test time serie", + Name: "test time series", } series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1)) @@ -118,7 +118,7 @@ func TestSimpleReducer(t *testing.T) { Convey("avg of number values and null values should ignore nulls", func() { reducer := newSimpleReducer("avg") series := &tsdb.TimeSeries{ - Name: "test time serie", + Name: "test time series", } series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(3), 1)) @@ -129,25 +129,61 @@ func TestSimpleReducer(t *testing.T) { So(reducer.Reduce(series).Float64, ShouldEqual, float64(3)) }) - Convey("diff one point", func() { + // diff function Test Suite + Convey("diff of one positive point", func() { result := testReducer("diff", 30) So(result, ShouldEqual, float64(0)) }) - Convey("diff two points", func() { + Convey("diff of one negative point", func() { + result := testReducer("diff", -30) + So(result, ShouldEqual, float64(0)) + }) + + Convey("diff of two positive points[1]", func() { result := testReducer("diff", 30, 40) So(result, ShouldEqual, float64(10)) }) - Convey("diff three points", func() { - result := testReducer("diff", 30, 40, 40) - So(result, ShouldEqual, float64(10)) + Convey("diff of two positive points[2]", func() { + result := testReducer("diff", 30, 20) + So(result, ShouldEqual, float64(-10)) + }) + + Convey("diff of two negative points[1]", func() { + result := testReducer("diff", -30, -40) + So(result, ShouldEqual, float64(-10)) + }) + + Convey("diff of two negative points[2]", func() { + result := testReducer("diff", -30, -10) + So(result, ShouldEqual, float64(20)) + }) + + Convey("diff of one positive and one negative point", func() { + result := testReducer("diff", 30, -40) + So(result, ShouldEqual, float64(-70)) + }) + + Convey("diff of one negative and one positive point", func() { + result := testReducer("diff", -30, 40) + So(result, ShouldEqual, float64(70)) + }) + + Convey("diff of three positive points", func() { + result := testReducer("diff", 30, 40, 50) + So(result, ShouldEqual, float64(20)) + }) + + Convey("diff of three negative points", func() { + result := testReducer("diff", -30, -40, -50) + So(result, ShouldEqual, float64(-20)) }) Convey("diff with only nulls", func() { reducer := newSimpleReducer("diff") series := &tsdb.TimeSeries{ - Name: "test time serie", + Name: "test time series", } series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1)) @@ -156,25 +192,187 @@ func TestSimpleReducer(t *testing.T) { So(reducer.Reduce(series).Valid, ShouldEqual, false) }) - Convey("percent_diff one point", func() { - result := testReducer("percent_diff", 40) + // diff_abs function Test Suite + Convey("diff_abs of one positive point", func() { + result := testReducer("diff_abs", 30) So(result, ShouldEqual, float64(0)) }) - Convey("percent_diff two points", func() { + Convey("diff_abs of one negative point", func() { + result := testReducer("diff_abs", -30) + So(result, ShouldEqual, float64(0)) + }) + + Convey("diff_abs of two positive points[1]", func() { + result := testReducer("diff_abs", 30, 40) + So(result, ShouldEqual, float64(10)) + }) + + Convey("diff_abs of two positive points[2]", func() { + result := testReducer("diff_abs", 30, 20) + So(result, ShouldEqual, float64(10)) + }) + + Convey("diff_abs of two negative points[1]", func() { + result := testReducer("diff_abs", -30, -40) + So(result, ShouldEqual, float64(10)) + }) + + Convey("diff_abs of two negative points[2]", func() { + result := testReducer("diff_abs", -30, -10) + So(result, ShouldEqual, float64(20)) + }) + + Convey("diff_abs of one positive and one negative point", func() { + result := testReducer("diff_abs", 30, -40) + So(result, ShouldEqual, float64(70)) + }) + + Convey("diff_abs of one negative and one positive point", func() { + result := testReducer("diff_abs", -30, 40) + So(result, ShouldEqual, float64(70)) + }) + + Convey("diff_abs of three positive points", func() { + result := testReducer("diff_abs", 30, 40, 50) + So(result, ShouldEqual, float64(20)) + }) + + Convey("diff_abs of three negative points", func() { + result := testReducer("diff_abs", -30, -40, -50) + So(result, ShouldEqual, float64(20)) + }) + + Convey("diff_abs with only nulls", func() { + reducer := newSimpleReducer("diff_abs") + series := &tsdb.TimeSeries{ + Name: "test time series", + } + + series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1)) + series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 2)) + + So(reducer.Reduce(series).Valid, ShouldEqual, false) + }) + + // percent_diff function Test Suite + Convey("percent_diff of one positive point", func() { + result := testReducer("percent_diff", 30) + So(result, ShouldEqual, float64(0)) + }) + + Convey("percent_diff of one negative point", func() { + result := testReducer("percent_diff", -30) + So(result, ShouldEqual, float64(0)) + }) + + Convey("percent_diff of two positive points[1]", func() { result := testReducer("percent_diff", 30, 40) So(result, ShouldEqual, float64(33.33333333333333)) }) - Convey("percent_diff three points", func() { - result := testReducer("percent_diff", 30, 40, 40) - So(result, ShouldEqual, float64(33.33333333333333)) + Convey("percent_diff of two positive points[2]", func() { + result := testReducer("percent_diff", 30, 20) + So(result, ShouldEqual, float64(-33.33333333333333)) + }) + + Convey("percent_diff of two negative points[1]", func() { + result := testReducer("percent_diff", -30, -40) + So(result, ShouldEqual, float64(-33.33333333333333)) + }) + + Convey("percent_diff of two negative points[2]", func() { + result := testReducer("percent_diff", -30, -10) + So(result, ShouldEqual, float64(66.66666666666666)) + }) + + Convey("percent_diff of one positive and one negative point", func() { + result := testReducer("percent_diff", 30, -40) + So(result, ShouldEqual, float64(-233.33333333333334)) + }) + + Convey("percent_diff of one negative and one positive point", func() { + result := testReducer("percent_diff", -30, 40) + So(result, ShouldEqual, float64(233.33333333333334)) + }) + + Convey("percent_diff of three positive points", func() { + result := testReducer("percent_diff", 30, 40, 50) + So(result, ShouldEqual, float64(66.66666666666666)) + }) + + Convey("percent_diff of three negative points", func() { + result := testReducer("percent_diff", -30, -40, -50) + So(result, ShouldEqual, float64(-66.66666666666666)) }) Convey("percent_diff with only nulls", func() { reducer := newSimpleReducer("percent_diff") series := &tsdb.TimeSeries{ - Name: "test time serie", + Name: "test time series", + } + + series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1)) + series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 2)) + + So(reducer.Reduce(series).Valid, ShouldEqual, false) + }) + + // percent_diff_abs function Test Suite + Convey("percent_diff_abs_abs of one positive point", func() { + result := testReducer("percent_diff_abs", 30) + So(result, ShouldEqual, float64(0)) + }) + + Convey("percent_diff_abs of one negative point", func() { + result := testReducer("percent_diff_abs", -30) + So(result, ShouldEqual, float64(0)) + }) + + Convey("percent_diff_abs of two positive points[1]", func() { + result := testReducer("percent_diff_abs", 30, 40) + So(result, ShouldEqual, float64(33.33333333333333)) + }) + + Convey("percent_diff_abs of two positive points[2]", func() { + result := testReducer("percent_diff_abs", 30, 20) + So(result, ShouldEqual, float64(33.33333333333333)) + }) + + Convey("percent_diff_abs of two negative points[1]", func() { + result := testReducer("percent_diff_abs", -30, -40) + So(result, ShouldEqual, float64(33.33333333333333)) + }) + + Convey("percent_diff_abs of two negative points[2]", func() { + result := testReducer("percent_diff_abs", -30, -10) + So(result, ShouldEqual, float64(66.66666666666666)) + }) + + Convey("percent_diff_abs of one positive and one negative point", func() { + result := testReducer("percent_diff_abs", 30, -40) + So(result, ShouldEqual, float64(233.33333333333334)) + }) + + Convey("percent_diff_abs of one negative and one positive point", func() { + result := testReducer("percent_diff_abs", -30, 40) + So(result, ShouldEqual, float64(233.33333333333334)) + }) + + Convey("percent_diff_abs of three positive points", func() { + result := testReducer("percent_diff_abs", 30, 40, 50) + So(result, ShouldEqual, float64(66.66666666666666)) + }) + + Convey("percent_diff_abs of three negative points", func() { + result := testReducer("percent_diff_abs", -30, -40, -50) + So(result, ShouldEqual, float64(66.66666666666666)) + }) + + Convey("percent_diff_abs with only nulls", func() { + reducer := newSimpleReducer("percent_diff_abs") + series := &tsdb.TimeSeries{ + Name: "test time series", } series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFromPtr(nil), 1)) @@ -203,7 +401,7 @@ func TestSimpleReducer(t *testing.T) { func testReducer(reducerType string, datapoints ...float64) float64 { reducer := newSimpleReducer(reducerType) series := &tsdb.TimeSeries{ - Name: "test time serie", + Name: "test time series", } for idx := range datapoints { diff --git a/public/app/features/alerting/state/alertDef.ts b/public/app/features/alerting/state/alertDef.ts index f0188c0dd1f..f64ee2bc9bd 100644 --- a/public/app/features/alerting/state/alertDef.ts +++ b/public/app/features/alerting/state/alertDef.ts @@ -47,7 +47,9 @@ const reducerTypes = [ { text: 'last()', value: 'last' }, { text: 'median()', value: 'median' }, { text: 'diff()', value: 'diff' }, + { text: 'diff_abs()', value: 'diff_abs' }, { text: 'percent_diff()', value: 'percent_diff' }, + { text: 'percent_diff_abs()', value: 'percent_diff_abs' }, { text: 'count_non_null()', value: 'count_non_null' }, ];