diff --git a/docs/sources/panels/query-a-data-source/use-expressions-to-manipulate-data/about-expressions.md b/docs/sources/panels/query-a-data-source/use-expressions-to-manipulate-data/about-expressions.md index 6158ba057ca..5fdade2cdd2 100644 --- a/docs/sources/panels/query-a-data-source/use-expressions-to-manipulate-data/about-expressions.md +++ b/docs/sources/panels/query-a-data-source/use-expressions-to-manipulate-data/about-expressions.md @@ -128,6 +128,18 @@ Log returns the natural logarithm of of its argument which can be a number or a The inf, infn, nan, and null functions all return a single value of the name. They primarily exist for testing. Example: `null()`. +##### round + +Round returns a rounded integer value. For example, `round(3.123)` or `round($A)`. (This function should probably take an argument so it can add precision to the rounded value). + +##### ceil + +Ceil rounds the number up to the nearest integer value. For example, `ceil(3.123)` returns 4. + +##### floor + +Floor rounds the number down to the nearest integer value. For example, `floor(3.123)` returns 3. + ### Reduce Reduce takes one or more time series returned from a query or an expression and turns each series into a single number. The labels of the time series are kept as labels on each outputted reduced number. @@ -157,6 +169,10 @@ Min and Max return the smallest or largest value in the series respectively. If Sum returns the total of all values in the series. If series is of zero length, the sum will be 0. If there are any NaN or Null values in the series, NaN is returned. +#### Last + +Last returns the last number in the series. If the series has no values then returns NaN. + ### Resample Resample changes the time stamps in each time series to have a consistent time interval. The main use case is so you can resample time series that do not share the same timestamps so math can be performed between them. This can be done by resample each of the two series, and then in a Math operation referencing the resampled variables. diff --git a/pkg/expr/mathexp/funcs.go b/pkg/expr/mathexp/funcs.go index a2deb4387f8..681d3d9afbd 100644 --- a/pkg/expr/mathexp/funcs.go +++ b/pkg/expr/mathexp/funcs.go @@ -53,6 +53,21 @@ var builtins = map[string]parse.Func{ VariantReturn: true, F: isNumber, }, + "round": { + Args: []parse.ReturnType{parse.TypeVariantSet}, + VariantReturn: true, + F: round, + }, + "ceil": { + Args: []parse.ReturnType{parse.TypeVariantSet}, + VariantReturn: true, + F: ceil, + }, + "floor": { + Args: []parse.ReturnType{parse.TypeVariantSet}, + VariantReturn: true, + F: floor, + }, } // abs returns the absolute value for each result in NumberSet, SeriesSet, or Scalar @@ -253,3 +268,42 @@ func perNullableFloat(e *State, val Value, floatF func(x *float64) *float64) (Va return newVal, nil } + +// round returns the rounded value for each result in NumberSet, SeriesSet, or Scalar +func round(e *State, varSet Results) (Results, error) { + newRes := Results{} + for _, res := range varSet.Values { + newVal, err := perFloat(e, res, math.Round) + if err != nil { + return newRes, err + } + newRes.Values = append(newRes.Values, newVal) + } + return newRes, nil +} + +// ceil returns the rounded up value for each result in NumberSet, SeriesSet, or Scalar +func ceil(e *State, varSet Results) (Results, error) { + newRes := Results{} + for _, res := range varSet.Values { + newVal, err := perFloat(e, res, math.Ceil) + if err != nil { + return newRes, err + } + newRes.Values = append(newRes.Values, newVal) + } + return newRes, nil +} + +// floor returns the rounded down value for each result in NumberSet, SeriesSet, or Scalar +func floor(e *State, varSet Results) (Results, error) { + newRes := Results{} + for _, res := range varSet.Values { + newVal, err := perFloat(e, res, math.Floor) + if err != nil { + return newRes, err + } + newRes.Values = append(newRes.Values, newVal) + } + return newRes, nil +} diff --git a/pkg/expr/mathexp/reduce.go b/pkg/expr/mathexp/reduce.go index f306cd24a3d..be8c7816c36 100644 --- a/pkg/expr/mathexp/reduce.go +++ b/pkg/expr/mathexp/reduce.go @@ -69,6 +69,17 @@ func Count(fv *Float64Field) *float64 { return &f } +func Last(fv *Float64Field) *float64 { + var f float64 + if fv.Len() == 0 { + f = math.NaN() + return &f + } + v := fv.GetValue(fv.Len() - 1) + f = *v + return &f +} + // Reduce turns the Series into a Number based on the given reduction function func (s Series) Reduce(refID, rFunc string) (Number, error) { var l data.Labels @@ -90,6 +101,8 @@ func (s Series) Reduce(refID, rFunc string) (Number, error) { f = Max(&floatField) case "count": f = Count(&floatField) + case "last": + f = Last(&floatField) default: return number, fmt.Errorf("reduction %v not implemented", rFunc) } diff --git a/pkg/expr/mathexp/reduce_test.go b/pkg/expr/mathexp/reduce_test.go index 7d83c123b71..2bd13780018 100644 --- a/pkg/expr/mathexp/reduce_test.go +++ b/pkg/expr/mathexp/reduce_test.go @@ -214,6 +214,19 @@ func TestSeriesReduce(t *testing.T) { }, }, }, + { + name: "last empty series", + red: "last", + varToReduce: "A", + vars: seriesEmpty, + errIs: require.NoError, + resultsIs: require.Equal, + results: Results{ + []Value{ + makeNumber("", nil, NaN), + }, + }, + }, } for _, tt := range tests { diff --git a/public/app/features/expressions/components/Math.tsx b/public/app/features/expressions/components/Math.tsx index 0ae6b4c8b0b..2d227321efd 100644 --- a/public/app/features/expressions/components/Math.tsx +++ b/public/app/features/expressions/components/Math.tsx @@ -12,7 +12,7 @@ interface Props { const mathPlaceholder = 'Math operations on one more queries, you reference the query by ${refId} ie. $A, $B, $C etc\n' + 'Example: $A + $B\n' + - 'Available functions: abs(), log(), is_number(), is_inf(), is_nan(), is_null()'; + 'Available functions: abs(), log(), is_number(), round(), ceil(), floor(), is_inf(), is_nan(), is_null(), '; export const Math: FC = ({ labelWidth, onChange, query }) => { const onExpressionChange = (event: ChangeEvent) => { diff --git a/public/app/features/expressions/types.ts b/public/app/features/expressions/types.ts index 5ec0c2e3bd6..b14ac983e0f 100644 --- a/public/app/features/expressions/types.ts +++ b/public/app/features/expressions/types.ts @@ -21,6 +21,7 @@ export const reducerTypes: Array> = [ { value: ReducerID.mean, label: 'Mean', description: 'Get the average value' }, { value: ReducerID.sum, label: 'Sum', description: 'Get the sum of all values' }, { value: ReducerID.count, label: 'Count', description: 'Get the number of values' }, + { value: ReducerID.last, label: 'Last', description: 'Get the last value' }, ]; export const downsamplingTypes: Array> = [