From 22fdfe141bfb88796a6e6c2029d0df9cbfce9617 Mon Sep 17 00:00:00 2001 From: Dean Wahle <60762514+DeanWahle@users.noreply.github.com> Date: Sun, 27 Dec 2020 22:05:55 -0500 Subject: [PATCH] [query] implemented Graphite's `powSeries` function (#3038) --- .../graphite/native/aggregation_functions.go | 7 ++++ .../native/aggregation_functions_test.go | 34 +++++++++++++++++++ .../graphite/native/builtin_functions.go | 1 + .../graphite/native/builtin_functions_test.go | 1 + src/query/graphite/native/functions.go | 1 + src/query/graphite/ts/series.go | 3 ++ 6 files changed, 47 insertions(+) diff --git a/src/query/graphite/native/aggregation_functions.go b/src/query/graphite/native/aggregation_functions.go index 2bd556214b..ac72670ae8 100644 --- a/src/query/graphite/native/aggregation_functions.go +++ b/src/query/graphite/native/aggregation_functions.go @@ -88,6 +88,13 @@ func minSeries(ctx *common.Context, series multiplePathSpecs) (ts.SeriesList, er return combineSeries(ctx, series, wrapPathExpr(minSeriesFnName, ts.SeriesList(series)), ts.Min) } +// powSeries takes a list of series and returns a new series containing the +// pow value across the series at each datapoint +// nolint: gocritic +func powSeries(ctx *common.Context, series multiplePathSpecs) (ts.SeriesList, error) { + return combineSeries(ctx, series, wrapPathExpr(powSeriesFnName, ts.SeriesList(series)), ts.Pow) +} + // maxSeries takes a list of series and returns a new series containing the // maximum value across the series at each datapoint func maxSeries(ctx *common.Context, series multiplePathSpecs) (ts.SeriesList, error) { diff --git a/src/query/graphite/native/aggregation_functions_test.go b/src/query/graphite/native/aggregation_functions_test.go index 4bbf8186aa..0510e0f728 100644 --- a/src/query/graphite/native/aggregation_functions_test.go +++ b/src/query/graphite/native/aggregation_functions_test.go @@ -166,6 +166,40 @@ func TestStdDevSeries(t *testing.T) { common.CompareOutputsAndExpected(t, 60000, start, expectedResults, result.Values) } +func TestPowSeries(t *testing.T) { + var ( + ctrl = xgomock.NewController(t) + store = storage.NewMockStorage(ctrl) + now = time.Now().Truncate(time.Hour) + engine = NewEngine(store, CompileOptions{}) + startTime = now.Add(-3 * time.Minute) + endTime = now.Add(-time.Minute) + ctx = common.NewContext(common.ContextOptions{ + Start: startTime, + End: endTime, + Engine: engine, + }) + ) + + fakeSeries1 := ts.NewSeries(ctx, "foo.bar.g.zed.g", startTime, + common.NewTestSeriesValues(ctx, 60000, []float64{0, 1, 2, 3, 4})) + fakeSeries2 := ts.NewSeries(ctx, "foo.bar.g.zed.g", startTime, + common.NewTestSeriesValues(ctx, 60000, []float64{2, 4, 1, 3, 3})) + fakeSeries3 := ts.NewSeries(ctx, "foo.bar.g.zed.g", startTime, + common.NewTestSeriesValues(ctx, 60000, []float64{5, 4, 3, 2, 1})) + + listOfFakeSeries := []*ts.Series{fakeSeries1, fakeSeries2, fakeSeries3} + + expectedValues := []float64{0, 1, 8, 729, 64} + result, err := powSeries(ctx, multiplePathSpecs(singlePathSpec{Values: listOfFakeSeries})) + if err != nil { + fmt.Println(err) + } + for i := 0; i < result.Values[0].Len(); i++ { + require.Equal(t, result.Values[0].ValueAt(i), expectedValues[i]) + } +} + func TestAggregate(t *testing.T) { testAggregatedSeries(t, func(ctx *common.Context, series multiplePathSpecs) (ts.SeriesList, error) { return aggregate(ctx, singlePathSpec(series), "sum") diff --git a/src/query/graphite/native/builtin_functions.go b/src/query/graphite/native/builtin_functions.go index bc65fc74d6..2d5073208f 100644 --- a/src/query/graphite/native/builtin_functions.go +++ b/src/query/graphite/native/builtin_functions.go @@ -2471,6 +2471,7 @@ func init() { MustRegisterFunction(perSecond).WithDefaultParams(map[uint8]interface{}{ 2: math.NaN(), // maxValue }) + MustRegisterFunction(powSeries) MustRegisterFunction(rangeOfSeries) MustRegisterFunction(randomWalkFunction).WithDefaultParams(map[uint8]interface{}{ 2: 60, // step diff --git a/src/query/graphite/native/builtin_functions_test.go b/src/query/graphite/native/builtin_functions_test.go index ee47294c5e..41dfcd3a36 100644 --- a/src/query/graphite/native/builtin_functions_test.go +++ b/src/query/graphite/native/builtin_functions_test.go @@ -3528,6 +3528,7 @@ func TestFunctionsRegistered(t *testing.T) { "offset", "offsetToZero", "perSecond", + "powSeries", "randomWalk", "randomWalkFunction", "rangeOfSeries", diff --git a/src/query/graphite/native/functions.go b/src/query/graphite/native/functions.go index b0109922ac..d1b73e5141 100644 --- a/src/query/graphite/native/functions.go +++ b/src/query/graphite/native/functions.go @@ -61,6 +61,7 @@ const ( minSeriesFnName = "minSeries" multiplyFnName = "multiply" multiplySeriesFnName = "multiplySeries" + powSeriesFnName = "powSeries" rangeFnName = "range" rangeOfFnName = "rangeOf" rangeOfSeriesFnName = "rangeOfSeries" diff --git a/src/query/graphite/ts/series.go b/src/query/graphite/ts/series.go index be211c95ef..02d88c712a 100644 --- a/src/query/graphite/ts/series.go +++ b/src/query/graphite/ts/series.go @@ -599,6 +599,9 @@ func Max(a, b float64, count int) float64 { return math.Max(a, b) } // Last finds the latter of two values. func Last(a, b float64, count int) float64 { return b } +// Pow returns the first value to the power of the second value +func Pow(a, b float64, count int) float64 { return math.Pow(a, b) } + // Median finds the median of a slice of values. func Median(vals []float64, count int) float64 { if count < 1 {