diff --git a/rules/ast/functions.go b/rules/ast/functions.go index 6cb111635d..06d389a176 100644 --- a/rules/ast/functions.go +++ b/rules/ast/functions.go @@ -265,7 +265,18 @@ func scalarImpl(timestamp clientmodel.Timestamp, view *viewAdapter, args []Node) return clientmodel.SampleValue(v[0].Value) } +// === count_scalar(vector VectorNode) model.SampleValue === +func countScalarImpl(timestamp clientmodel.Timestamp, view *viewAdapter, args []Node) interface{} { + return clientmodel.SampleValue(len(args[0].(VectorNode).Eval(timestamp, view))) +} + var functions = map[string]*Function{ + "count_scalar": { + name: "count_scalar", + argTypes: []ExprType{VECTOR}, + returnType: SCALAR, + callFn: countScalarImpl, + }, "delta": { name: "delta", argTypes: []ExprType{MATRIX, SCALAR}, diff --git a/rules/ast/printer.go b/rules/ast/printer.go index ad53816f0c..6c0e9b7685 100644 --- a/rules/ast/printer.go +++ b/rules/ast/printer.go @@ -159,7 +159,7 @@ func EvalToString(node Node, timestamp clientmodel.Timestamp, format OutputForma evalTimer.Stop() switch format { case TEXT: - return fmt.Sprintf("scalar: %v", scalar) + return fmt.Sprintf("scalar: %v @[%v]", scalar, timestamp) case JSON: return TypedValueToJSON(scalar, "scalar") } diff --git a/rules/rules_test.go b/rules/rules_test.go index b09d0e5ac3..4c8c158642 100644 --- a/rules/rules_test.go +++ b/rules/rules_test.go @@ -382,6 +382,18 @@ func TestExpressions(t *testing.T) { output: []string{`testcounter_reset_end => -90 @[%v]`}, fullRanges: 1, intervalRanges: 0, + }, { + // count_scalar for a non-empty vector should return scalar element count. + expr: `count_scalar(http_requests)`, + output: []string{`scalar: 8 @[%v]`}, + fullRanges: 0, + intervalRanges: 8, + }, { + // count_scalar for an empty vector should return scalar 0. + expr: `count_scalar(nonexistent)`, + output: []string{`scalar: 0 @[%v]`}, + fullRanges: 0, + intervalRanges: 0, }, { // Empty expressions shouldn"t parse. expr: ``,