Support + operator for sparse histograms (#9949)

Signed-off-by: Ganesh Vernekar <ganeshvern@gmail.com>
pull/10025/head
Ganesh Vernekar 2021-12-06 23:06:58 +05:30 committed by GitHub
parent 187a767292
commit f580248759
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 31 deletions

View File

@ -1960,10 +1960,12 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
// Account for potentially swapped sidedness. // Account for potentially swapped sidedness.
vl, vr := ls.V, rs.V vl, vr := ls.V, rs.V
hl, hr := ls.H, rs.H
if matching.Card == parser.CardOneToMany { if matching.Card == parser.CardOneToMany {
vl, vr = vr, vl vl, vr = vr, vl
hl, hr = hr, hl
} }
value, keep := vectorElemBinop(op, vl, vr) value, histogramValue, keep := vectorElemBinop(op, vl, vr, hl, hr)
if returnBool { if returnBool {
if keep { if keep {
value = 1.0 value = 1.0
@ -1998,11 +2000,14 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching *
insertedSigs[insertSig] = struct{}{} insertedSigs[insertSig] = struct{}{}
} }
if (hl != nil && hr != nil) || (hl == nil && hr == nil) {
// Both lhs and rhs are of same type.
enh.Out = append(enh.Out, Sample{ enh.Out = append(enh.Out, Sample{
Metric: metric, Metric: metric,
Point: Point{V: value}, Point: Point{V: value, H: histogramValue},
}) })
} }
}
return enh.Out return enh.Out
} }
@ -2085,7 +2090,7 @@ func (ev *evaluator) VectorscalarBinop(op parser.ItemType, lhs Vector, rhs Scala
if swap { if swap {
lv, rv = rv, lv lv, rv = rv, lv
} }
value, keep := vectorElemBinop(op, lv, rv) value, _, keep := vectorElemBinop(op, lv, rv, nil, nil)
// Catch cases where the scalar is the LHS in a scalar-vector comparison operation. // Catch cases where the scalar is the LHS in a scalar-vector comparison operation.
// We want to always keep the vector element value as the output value, even if it's on the RHS. // We want to always keep the vector element value as the output value, even if it's on the RHS.
if op.IsComparisonOperator() && swap { if op.IsComparisonOperator() && swap {
@ -2148,34 +2153,37 @@ func scalarBinop(op parser.ItemType, lhs, rhs float64) float64 {
} }
// vectorElemBinop evaluates a binary operation between two Vector elements. // vectorElemBinop evaluates a binary operation between two Vector elements.
func vectorElemBinop(op parser.ItemType, lhs, rhs float64) (float64, bool) { func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram.FloatHistogram) (float64, *histogram.FloatHistogram, bool) {
switch op { switch op {
case parser.ADD: case parser.ADD:
return lhs + rhs, true if hlhs != nil && hrhs != nil {
return 0, hlhs.Copy().Add(hrhs), true
}
return lhs + rhs, nil, true
case parser.SUB: case parser.SUB:
return lhs - rhs, true return lhs - rhs, nil, true
case parser.MUL: case parser.MUL:
return lhs * rhs, true return lhs * rhs, nil, true
case parser.DIV: case parser.DIV:
return lhs / rhs, true return lhs / rhs, nil, true
case parser.POW: case parser.POW:
return math.Pow(lhs, rhs), true return math.Pow(lhs, rhs), nil, true
case parser.MOD: case parser.MOD:
return math.Mod(lhs, rhs), true return math.Mod(lhs, rhs), nil, true
case parser.EQLC: case parser.EQLC:
return lhs, lhs == rhs return lhs, nil, lhs == rhs
case parser.NEQ: case parser.NEQ:
return lhs, lhs != rhs return lhs, nil, lhs != rhs
case parser.GTR: case parser.GTR:
return lhs, lhs > rhs return lhs, nil, lhs > rhs
case parser.LSS: case parser.LSS:
return lhs, lhs < rhs return lhs, nil, lhs < rhs
case parser.GTE: case parser.GTE:
return lhs, lhs >= rhs return lhs, nil, lhs >= rhs
case parser.LTE: case parser.LTE:
return lhs, lhs <= rhs return lhs, nil, lhs <= rhs
case parser.ATAN2: case parser.ATAN2:
return math.Atan2(lhs, rhs), true return math.Atan2(lhs, rhs), nil, true
} }
panic(errors.Errorf("operator %q not allowed for operations between Vectors", op)) panic(errors.Errorf("operator %q not allowed for operations between Vectors", op))
} }

View File

@ -2900,7 +2900,7 @@ func TestSparseHistogram_HistogramQuantile(t *testing.T) {
} }
} }
func TestSparseHistogram_Sum(t *testing.T) { func TestSparseHistogram_Sum_AddOperator(t *testing.T) {
// TODO(codesome): Integrate histograms into the PromQL testing framework // TODO(codesome): Integrate histograms into the PromQL testing framework
// and write more tests there. // and write more tests there.
cases := []struct { cases := []struct {
@ -3006,7 +3006,7 @@ func TestSparseHistogram_Sum(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, app.Commit()) require.NoError(t, app.Commit())
queryString := fmt.Sprintf("sum(%s)", seriesName) queryAndCheck := func(queryString string) {
qry, err := engine.NewInstantQuery(test.Queryable(), queryString, timestamp.Time(ts)) qry, err := engine.NewInstantQuery(test.Queryable(), queryString, timestamp.Time(ts))
require.NoError(t, err) require.NoError(t, err)
@ -3018,6 +3018,18 @@ func TestSparseHistogram_Sum(t *testing.T) {
require.Len(t, vector, 1) require.Len(t, vector, 1)
require.Equal(t, &c.expected, vector[0].H) require.Equal(t, &c.expected, vector[0].H)
}
// sum().
queryString := fmt.Sprintf("sum(%s)", seriesName)
queryAndCheck(queryString)
// + operator.
queryString = fmt.Sprintf(`%s{idx="0"}`, seriesName)
for idx := 1; idx < len(c.histograms); idx++ {
queryString += fmt.Sprintf(` + ignoring(idx) %s{idx="%d"}`, seriesName, idx)
}
queryAndCheck(queryString)
}) })
} }
} }