mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-25 05:34:05 -08:00
promql: Add sgn, clamp and last_over_time functions (#8457)
* Add sgn, clamp and last_over_time functions Signed-off-by: schou <pschou@users.noreply.github.com>
This commit is contained in:
parent
d35cf369f2
commit
aff3c702ab
|
@ -73,6 +73,15 @@ For each input time series, `changes(v range-vector)` returns the number of
|
|||
times its value has changed within the provided time range as an instant
|
||||
vector.
|
||||
|
||||
## `clamp()`
|
||||
|
||||
`clamp(v instant-vector, min scalar, max scalar)`
|
||||
clamps the sample values of all elements in `v` to have a lower limit of `min` and an upper limit of `max`.
|
||||
|
||||
Special cases:
|
||||
- Return an empty vector if `min > max`
|
||||
- Return `NaN` if `min` or `max` is `NaN`
|
||||
|
||||
## `clamp_max()`
|
||||
|
||||
`clamp_max(v instant-vector, max scalar)` clamps the sample values of all
|
||||
|
@ -370,6 +379,10 @@ Given a single-element input vector, `scalar(v instant-vector)` returns the
|
|||
sample value of that single element as a scalar. If the input vector does not
|
||||
have exactly one element, `scalar` will return `NaN`.
|
||||
|
||||
## `sgn()`
|
||||
|
||||
`sgn(v instant-vector)` returns a vector with all sample values converted to their sign, defined as this: 1 if v is positive, -1 if v is negative and 0 if v is equal to zero.
|
||||
|
||||
## `sort()`
|
||||
|
||||
`sort(v instant-vector)` returns vector elements sorted by their sample values,
|
||||
|
@ -418,6 +431,7 @@ over time and return an instant vector with per-series aggregation results:
|
|||
* `quantile_over_time(scalar, range-vector)`: the φ-quantile (0 ≤ φ ≤ 1) of the values in the specified interval.
|
||||
* `stddev_over_time(range-vector)`: the population standard deviation of the values in the specified interval.
|
||||
* `stdvar_over_time(range-vector)`: the population standard variance of the values in the specified interval.
|
||||
* `last_over_time(range-vector)`: the most recent point value in specified interval.
|
||||
|
||||
Note that all values in the specified interval have the same weight in the
|
||||
aggregation even if the values are not equally spaced throughout the interval.
|
||||
|
|
|
@ -1216,11 +1216,16 @@ func (ev *evaluator) eval(expr parser.Expr) (parser.Value, storage.Warnings) {
|
|||
ev.currentSamples -= len(points)
|
||||
points = points[:0]
|
||||
it.Reset(s.Iterator())
|
||||
metric := selVS.Series[i].Labels()
|
||||
// The last_over_time function acts like offset; thus, it
|
||||
// should keep the metric name. For all the other range
|
||||
// vector functions, the only change needed is to drop the
|
||||
// metric name in the output.
|
||||
if e.Func.Name != "last_over_time" {
|
||||
metric = dropMetricName(metric)
|
||||
}
|
||||
ss := Series{
|
||||
// For all range vector functions, the only change to the
|
||||
// output labels is dropping the metric name so just do
|
||||
// it once here.
|
||||
Metric: dropMetricName(selVS.Series[i].Labels()),
|
||||
Metric: metric,
|
||||
Points: getPointSlice(numSteps),
|
||||
}
|
||||
inMatrix[0].Metric = selVS.Series[i].Labels()
|
||||
|
|
|
@ -278,6 +278,23 @@ func funcSortDesc(vals []parser.Value, args parser.Expressions, enh *EvalNodeHel
|
|||
return Vector(byValueSorter)
|
||||
}
|
||||
|
||||
// === clamp(Vector parser.ValueTypeVector, min, max Scalar) Vector ===
|
||||
func funcClamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
|
||||
vec := vals[0].(Vector)
|
||||
min := vals[1].(Vector)[0].Point.V
|
||||
max := vals[2].(Vector)[0].Point.V
|
||||
if max < min {
|
||||
return enh.Out
|
||||
}
|
||||
for _, el := range vec {
|
||||
enh.Out = append(enh.Out, Sample{
|
||||
Metric: enh.DropMetricName(el.Metric),
|
||||
Point: Point{V: math.Max(min, math.Min(max, el.V))},
|
||||
})
|
||||
}
|
||||
return enh.Out
|
||||
}
|
||||
|
||||
// === clamp_max(Vector parser.ValueTypeVector, max Scalar) Vector ===
|
||||
func funcClampMax(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
|
||||
vec := vals[0].(Vector)
|
||||
|
@ -383,7 +400,16 @@ func funcCountOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNo
|
|||
})
|
||||
}
|
||||
|
||||
// === floor(Vector parser.ValueTypeVector) Vector ===
|
||||
// === last_over_time(Matrix parser.ValueTypeMatrix) Vector ===
|
||||
func funcLastOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
|
||||
el := vals[0].(Matrix)[0]
|
||||
|
||||
return append(enh.Out, Sample{
|
||||
Metric: el.Metric,
|
||||
Point: Point{V: el.Points[len(el.Points)-1].V},
|
||||
})
|
||||
}
|
||||
|
||||
// === max_over_time(Matrix parser.ValueTypeMatrix) Vector ===
|
||||
func funcMaxOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
|
||||
return aggrOverTime(vals, enh, func(values []Point) float64 {
|
||||
|
@ -537,6 +563,18 @@ func funcLog10(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper
|
|||
return simpleFunc(vals, enh, math.Log10)
|
||||
}
|
||||
|
||||
// === sgn(Vector parser.ValueTypeVector) Vector ===
|
||||
func funcSgn(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
|
||||
return simpleFunc(vals, enh, func(v float64) float64 {
|
||||
if v < 0 {
|
||||
return -1
|
||||
} else if v > 0 {
|
||||
return 1
|
||||
}
|
||||
return v
|
||||
})
|
||||
}
|
||||
|
||||
// === timestamp(Vector parser.ValueTypeVector) Vector ===
|
||||
func funcTimestamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
|
||||
vec := vals[0].(Vector)
|
||||
|
@ -893,6 +931,7 @@ var FunctionCalls = map[string]FunctionCall{
|
|||
"avg_over_time": funcAvgOverTime,
|
||||
"ceil": funcCeil,
|
||||
"changes": funcChanges,
|
||||
"clamp": funcClamp,
|
||||
"clamp_max": funcClampMax,
|
||||
"clamp_min": funcClampMin,
|
||||
"count_over_time": funcCountOverTime,
|
||||
|
@ -914,6 +953,7 @@ var FunctionCalls = map[string]FunctionCall{
|
|||
"ln": funcLn,
|
||||
"log10": funcLog10,
|
||||
"log2": funcLog2,
|
||||
"last_over_time": funcLastOverTime,
|
||||
"max_over_time": funcMaxOverTime,
|
||||
"min_over_time": funcMinOverTime,
|
||||
"minute": funcMinute,
|
||||
|
@ -924,6 +964,7 @@ var FunctionCalls = map[string]FunctionCall{
|
|||
"resets": funcResets,
|
||||
"round": funcRound,
|
||||
"scalar": funcScalar,
|
||||
"sgn": funcSgn,
|
||||
"sort": funcSort,
|
||||
"sort_desc": funcSortDesc,
|
||||
"sqrt": funcSqrt,
|
||||
|
|
|
@ -54,6 +54,11 @@ var Functions = map[string]*Function{
|
|||
ArgTypes: []ValueType{ValueTypeMatrix},
|
||||
ReturnType: ValueTypeVector,
|
||||
},
|
||||
"clamp": {
|
||||
Name: "clamp",
|
||||
ArgTypes: []ValueType{ValueTypeVector, ValueTypeScalar, ValueTypeScalar},
|
||||
ReturnType: ValueTypeVector,
|
||||
},
|
||||
"clamp_max": {
|
||||
Name: "clamp_max",
|
||||
ArgTypes: []ValueType{ValueTypeVector, ValueTypeScalar},
|
||||
|
@ -149,6 +154,11 @@ var Functions = map[string]*Function{
|
|||
Variadic: -1,
|
||||
ReturnType: ValueTypeVector,
|
||||
},
|
||||
"last_over_time": {
|
||||
Name: "last_over_time",
|
||||
ArgTypes: []ValueType{ValueTypeMatrix},
|
||||
ReturnType: ValueTypeVector,
|
||||
},
|
||||
"ln": {
|
||||
Name: "ln",
|
||||
ArgTypes: []ValueType{ValueTypeVector},
|
||||
|
@ -217,6 +227,11 @@ var Functions = map[string]*Function{
|
|||
ArgTypes: []ValueType{ValueTypeVector},
|
||||
ReturnType: ValueTypeScalar,
|
||||
},
|
||||
"sgn": {
|
||||
Name: "sgn",
|
||||
ArgTypes: []ValueType{ValueTypeVector},
|
||||
ReturnType: ValueTypeVector,
|
||||
},
|
||||
"sort": {
|
||||
Name: "sort",
|
||||
ArgTypes: []ValueType{ValueTypeVector},
|
||||
|
|
44
promql/testdata/functions.test
vendored
44
promql/testdata/functions.test
vendored
|
@ -372,7 +372,7 @@ eval instant at 60m vector(time())
|
|||
{} 3600
|
||||
|
||||
|
||||
# Tests for clamp_max and clamp_min().
|
||||
# Tests for clamp_max, clamp_min(), and clamp().
|
||||
load 5m
|
||||
test_clamp{src="clamp-a"} -50
|
||||
test_clamp{src="clamp-b"} 0
|
||||
|
@ -388,6 +388,11 @@ eval instant at 0m clamp_min(test_clamp, -25)
|
|||
{src="clamp-b"} 0
|
||||
{src="clamp-c"} 100
|
||||
|
||||
eval instant at 0m clamp(test_clamp, -25, 75)
|
||||
{src="clamp-a"} -25
|
||||
{src="clamp-b"} 0
|
||||
{src="clamp-c"} 75
|
||||
|
||||
eval instant at 0m clamp_max(clamp_min(test_clamp, -20), 70)
|
||||
{src="clamp-a"} -20
|
||||
{src="clamp-b"} 0
|
||||
|
@ -398,6 +403,36 @@ eval instant at 0m clamp_max((clamp_min(test_clamp, (-20))), (70))
|
|||
{src="clamp-b"} 0
|
||||
{src="clamp-c"} 70
|
||||
|
||||
eval instant at 0m clamp(test_clamp, 0, NaN)
|
||||
{src="clamp-a"} NaN
|
||||
{src="clamp-b"} NaN
|
||||
{src="clamp-c"} NaN
|
||||
|
||||
eval instant at 0m clamp(test_clamp, NaN, 0)
|
||||
{src="clamp-a"} NaN
|
||||
{src="clamp-b"} NaN
|
||||
{src="clamp-c"} NaN
|
||||
|
||||
eval instant at 0m clamp(test_clamp, 5, -5)
|
||||
|
||||
# Test cases for sgn.
|
||||
clear
|
||||
load 5m
|
||||
test_sgn{src="sgn-a"} -Inf
|
||||
test_sgn{src="sgn-b"} Inf
|
||||
test_sgn{src="sgn-c"} NaN
|
||||
test_sgn{src="sgn-d"} -50
|
||||
test_sgn{src="sgn-e"} 0
|
||||
test_sgn{src="sgn-f"} 100
|
||||
|
||||
eval instant at 0m sgn(test_sgn)
|
||||
{src="sgn-a"} -1
|
||||
{src="sgn-b"} 1
|
||||
{src="sgn-c"} NaN
|
||||
{src="sgn-d"} -1
|
||||
{src="sgn-e"} 0
|
||||
{src="sgn-f"} 1
|
||||
|
||||
|
||||
# Tests for sort/sort_desc.
|
||||
clear
|
||||
|
@ -745,6 +780,13 @@ eval instant at 1m max_over_time(data[1m])
|
|||
{type="some_nan3"} 1
|
||||
{type="only_nan"} NaN
|
||||
|
||||
eval instant at 1m last_over_time(data[1m])
|
||||
data{type="numbers"} 3
|
||||
data{type="some_nan"} NaN
|
||||
data{type="some_nan2"} 1
|
||||
data{type="some_nan3"} 1
|
||||
data{type="only_nan"} NaN
|
||||
|
||||
clear
|
||||
|
||||
# Test for absent()
|
||||
|
|
Loading…
Reference in a new issue