diff --git a/app/vmselect/promql/aggr.go b/app/vmselect/promql/aggr.go index a2bfdaad04..c4afc0af9e 100644 --- a/app/vmselect/promql/aggr.go +++ b/app/vmselect/promql/aggr.go @@ -353,6 +353,25 @@ func aggrFuncCountValues(afa *aggrFuncArg) ([]*timeseries, error) { if err != nil { return nil, err } + + // Remove dstLabel from grouping like Prometheus does. + modifier := &afa.ae.Modifier + switch strings.ToLower(modifier.Op) { + case "without": + modifier.Args = append(modifier.Args, dstLabel) + case "by": + dstArgs := modifier.Args[:0] + for _, arg := range modifier.Args { + if arg == dstLabel { + continue + } + dstArgs = append(dstArgs, arg) + } + modifier.Args = dstArgs + default: + // Do nothing + } + afe := func(tss []*timeseries) []*timeseries { m := make(map[float64]bool) for _, ts := range tss { diff --git a/app/vmselect/promql/exec_test.go b/app/vmselect/promql/exec_test.go index 5929084cd5..92f3a8f6de 100644 --- a/app/vmselect/promql/exec_test.go +++ b/app/vmselect/promql/exec_test.go @@ -3900,6 +3900,107 @@ func TestExecSuccess(t *testing.T) { resultExpected := []netstorage.Result{r1, r2, r3, r4, r5, r6} f(q, resultExpected) }) + t.Run(`count_values by (xxx)`, func(t *testing.T) { + t.Parallel() + q := `count_values("xxx", label_set(10, "foo", "bar", "xxx", "aaa") or label_set(floor(time()/600), "foo", "bar", "baz", "xx")) by (xxx)` + r1 := netstorage.Result{ + MetricName: metricNameExpected, + Values: []float64{1, nan, nan, nan, nan, nan}, + Timestamps: timestampsExpected, + } + r1.MetricName.Tags = []storage.Tag{ + { + Key: []byte("xxx"), + Value: []byte("1"), + }, + } + r2 := netstorage.Result{ + MetricName: metricNameExpected, + Values: []float64{nan, 1, 1, 1, nan, nan}, + Timestamps: timestampsExpected, + } + r2.MetricName.Tags = []storage.Tag{ + { + Key: []byte("xxx"), + Value: []byte("2"), + }, + } + r3 := netstorage.Result{ + MetricName: metricNameExpected, + Values: []float64{nan, nan, nan, nan, 1, 1}, + Timestamps: timestampsExpected, + } + r3.MetricName.Tags = []storage.Tag{ + { + Key: []byte("xxx"), + Value: []byte("3"), + }, + } + r4 := netstorage.Result{ + MetricName: metricNameExpected, + Values: []float64{1, 1, 1, 1, 1, 1}, + Timestamps: timestampsExpected, + } + r4.MetricName.Tags = []storage.Tag{ + { + Key: []byte("xxx"), + Value: []byte("10"), + }, + } + resultExpected := []netstorage.Result{r1, r2, r3, r4} + f(q, resultExpected) + }) + t.Run(`count_values without (baz)`, func(t *testing.T) { + t.Parallel() + q := `count_values("xxx", label_set(floor(time()/600), "foo", "bar")) without (baz)` + r1 := netstorage.Result{ + MetricName: metricNameExpected, + Values: []float64{1, nan, nan, nan, nan, nan}, + Timestamps: timestampsExpected, + } + r1.MetricName.Tags = []storage.Tag{ + { + Key: []byte("foo"), + Value: []byte("bar"), + }, + { + Key: []byte("xxx"), + Value: []byte("1"), + }, + } + r2 := netstorage.Result{ + MetricName: metricNameExpected, + Values: []float64{nan, 1, 1, 1, nan, nan}, + Timestamps: timestampsExpected, + } + r2.MetricName.Tags = []storage.Tag{ + { + Key: []byte("foo"), + Value: []byte("bar"), + }, + { + Key: []byte("xxx"), + Value: []byte("2"), + }, + } + r3 := netstorage.Result{ + MetricName: metricNameExpected, + Values: []float64{nan, nan, nan, nan, 1, 1}, + Timestamps: timestampsExpected, + } + r3.MetricName.Tags = []storage.Tag{ + { + Key: []byte("foo"), + Value: []byte("bar"), + }, + { + Key: []byte("xxx"), + Value: []byte("3"), + }, + } + resultExpected := []netstorage.Result{r1, r2, r3} + f(q, resultExpected) + }) } func TestExecError(t *testing.T) {