mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-25 21:54:10 -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
|
times its value has changed within the provided time range as an instant
|
||||||
vector.
|
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()`
|
||||||
|
|
||||||
`clamp_max(v instant-vector, max scalar)` clamps the sample values of all
|
`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
|
sample value of that single element as a scalar. If the input vector does not
|
||||||
have exactly one element, `scalar` will return `NaN`.
|
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()`
|
||||||
|
|
||||||
`sort(v instant-vector)` returns vector elements sorted by their sample values,
|
`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.
|
* `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.
|
* `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.
|
* `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
|
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.
|
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)
|
ev.currentSamples -= len(points)
|
||||||
points = points[:0]
|
points = points[:0]
|
||||||
it.Reset(s.Iterator())
|
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{
|
ss := Series{
|
||||||
// For all range vector functions, the only change to the
|
Metric: metric,
|
||||||
// output labels is dropping the metric name so just do
|
|
||||||
// it once here.
|
|
||||||
Metric: dropMetricName(selVS.Series[i].Labels()),
|
|
||||||
Points: getPointSlice(numSteps),
|
Points: getPointSlice(numSteps),
|
||||||
}
|
}
|
||||||
inMatrix[0].Metric = selVS.Series[i].Labels()
|
inMatrix[0].Metric = selVS.Series[i].Labels()
|
||||||
|
|
|
@ -278,6 +278,23 @@ func funcSortDesc(vals []parser.Value, args parser.Expressions, enh *EvalNodeHel
|
||||||
return Vector(byValueSorter)
|
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 ===
|
// === clamp_max(Vector parser.ValueTypeVector, max Scalar) Vector ===
|
||||||
func funcClampMax(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
|
func funcClampMax(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
|
||||||
vec := vals[0].(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 ===
|
// === max_over_time(Matrix parser.ValueTypeMatrix) Vector ===
|
||||||
func funcMaxOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
|
func funcMaxOverTime(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
|
||||||
return aggrOverTime(vals, enh, func(values []Point) float64 {
|
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)
|
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 ===
|
// === timestamp(Vector parser.ValueTypeVector) Vector ===
|
||||||
func funcTimestamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
|
func funcTimestamp(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector {
|
||||||
vec := vals[0].(Vector)
|
vec := vals[0].(Vector)
|
||||||
|
@ -893,6 +931,7 @@ var FunctionCalls = map[string]FunctionCall{
|
||||||
"avg_over_time": funcAvgOverTime,
|
"avg_over_time": funcAvgOverTime,
|
||||||
"ceil": funcCeil,
|
"ceil": funcCeil,
|
||||||
"changes": funcChanges,
|
"changes": funcChanges,
|
||||||
|
"clamp": funcClamp,
|
||||||
"clamp_max": funcClampMax,
|
"clamp_max": funcClampMax,
|
||||||
"clamp_min": funcClampMin,
|
"clamp_min": funcClampMin,
|
||||||
"count_over_time": funcCountOverTime,
|
"count_over_time": funcCountOverTime,
|
||||||
|
@ -914,6 +953,7 @@ var FunctionCalls = map[string]FunctionCall{
|
||||||
"ln": funcLn,
|
"ln": funcLn,
|
||||||
"log10": funcLog10,
|
"log10": funcLog10,
|
||||||
"log2": funcLog2,
|
"log2": funcLog2,
|
||||||
|
"last_over_time": funcLastOverTime,
|
||||||
"max_over_time": funcMaxOverTime,
|
"max_over_time": funcMaxOverTime,
|
||||||
"min_over_time": funcMinOverTime,
|
"min_over_time": funcMinOverTime,
|
||||||
"minute": funcMinute,
|
"minute": funcMinute,
|
||||||
|
@ -924,6 +964,7 @@ var FunctionCalls = map[string]FunctionCall{
|
||||||
"resets": funcResets,
|
"resets": funcResets,
|
||||||
"round": funcRound,
|
"round": funcRound,
|
||||||
"scalar": funcScalar,
|
"scalar": funcScalar,
|
||||||
|
"sgn": funcSgn,
|
||||||
"sort": funcSort,
|
"sort": funcSort,
|
||||||
"sort_desc": funcSortDesc,
|
"sort_desc": funcSortDesc,
|
||||||
"sqrt": funcSqrt,
|
"sqrt": funcSqrt,
|
||||||
|
|
|
@ -54,6 +54,11 @@ var Functions = map[string]*Function{
|
||||||
ArgTypes: []ValueType{ValueTypeMatrix},
|
ArgTypes: []ValueType{ValueTypeMatrix},
|
||||||
ReturnType: ValueTypeVector,
|
ReturnType: ValueTypeVector,
|
||||||
},
|
},
|
||||||
|
"clamp": {
|
||||||
|
Name: "clamp",
|
||||||
|
ArgTypes: []ValueType{ValueTypeVector, ValueTypeScalar, ValueTypeScalar},
|
||||||
|
ReturnType: ValueTypeVector,
|
||||||
|
},
|
||||||
"clamp_max": {
|
"clamp_max": {
|
||||||
Name: "clamp_max",
|
Name: "clamp_max",
|
||||||
ArgTypes: []ValueType{ValueTypeVector, ValueTypeScalar},
|
ArgTypes: []ValueType{ValueTypeVector, ValueTypeScalar},
|
||||||
|
@ -149,6 +154,11 @@ var Functions = map[string]*Function{
|
||||||
Variadic: -1,
|
Variadic: -1,
|
||||||
ReturnType: ValueTypeVector,
|
ReturnType: ValueTypeVector,
|
||||||
},
|
},
|
||||||
|
"last_over_time": {
|
||||||
|
Name: "last_over_time",
|
||||||
|
ArgTypes: []ValueType{ValueTypeMatrix},
|
||||||
|
ReturnType: ValueTypeVector,
|
||||||
|
},
|
||||||
"ln": {
|
"ln": {
|
||||||
Name: "ln",
|
Name: "ln",
|
||||||
ArgTypes: []ValueType{ValueTypeVector},
|
ArgTypes: []ValueType{ValueTypeVector},
|
||||||
|
@ -217,6 +227,11 @@ var Functions = map[string]*Function{
|
||||||
ArgTypes: []ValueType{ValueTypeVector},
|
ArgTypes: []ValueType{ValueTypeVector},
|
||||||
ReturnType: ValueTypeScalar,
|
ReturnType: ValueTypeScalar,
|
||||||
},
|
},
|
||||||
|
"sgn": {
|
||||||
|
Name: "sgn",
|
||||||
|
ArgTypes: []ValueType{ValueTypeVector},
|
||||||
|
ReturnType: ValueTypeVector,
|
||||||
|
},
|
||||||
"sort": {
|
"sort": {
|
||||||
Name: "sort",
|
Name: "sort",
|
||||||
ArgTypes: []ValueType{ValueTypeVector},
|
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
|
{} 3600
|
||||||
|
|
||||||
|
|
||||||
# Tests for clamp_max and clamp_min().
|
# Tests for clamp_max, clamp_min(), and clamp().
|
||||||
load 5m
|
load 5m
|
||||||
test_clamp{src="clamp-a"} -50
|
test_clamp{src="clamp-a"} -50
|
||||||
test_clamp{src="clamp-b"} 0
|
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-b"} 0
|
||||||
{src="clamp-c"} 100
|
{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)
|
eval instant at 0m clamp_max(clamp_min(test_clamp, -20), 70)
|
||||||
{src="clamp-a"} -20
|
{src="clamp-a"} -20
|
||||||
{src="clamp-b"} 0
|
{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-b"} 0
|
||||||
{src="clamp-c"} 70
|
{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.
|
# Tests for sort/sort_desc.
|
||||||
clear
|
clear
|
||||||
|
@ -745,6 +780,13 @@ eval instant at 1m max_over_time(data[1m])
|
||||||
{type="some_nan3"} 1
|
{type="some_nan3"} 1
|
||||||
{type="only_nan"} NaN
|
{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
|
clear
|
||||||
|
|
||||||
# Test for absent()
|
# Test for absent()
|
||||||
|
|
Loading…
Reference in a new issue