diff --git a/tsdb/querier.go b/tsdb/querier.go index aed4952ac0..a8a18164aa 100644 --- a/tsdb/querier.go +++ b/tsdb/querier.go @@ -338,6 +338,24 @@ func labelValuesWithMatchers(r IndexReader, name string, matchers ...*labels.Mat if err != nil { return nil, errors.Wrapf(err, "fetching values of label %s", name) } + + // If we have a matcher for the label name, we can filter out values that don't match + // before we fetch postings. This is especially useful for labels with many values. + // e.g. __name__ with a selector like {__name__="xyz"} + for _, m := range matchers { + if m.Name != name { + continue + } + + for i := len(allValues) - 1; i >= 0; i-- { + if m.Matches(allValues[i]) { + continue + } + + allValues = append(allValues[:i], allValues[i+1:]...) + } + } + valuesPostings := make([]index.Postings, len(allValues)) for i, value := range allValues { valuesPostings[i], err = r.Postings(name, value) diff --git a/tsdb/querier_bench_test.go b/tsdb/querier_bench_test.go index bd8f7131cf..741301dca6 100644 --- a/tsdb/querier_bench_test.go +++ b/tsdb/querier_bench_test.go @@ -182,6 +182,7 @@ func benchmarkLabelValuesWithMatchers(b *testing.B, ir IndexReader) { labelName string matchers []*labels.Matcher }{ + {`i with i="1"`, "i", []*labels.Matcher{i1}}, // i has 100k values. {`i with n="1"`, "i", []*labels.Matcher{n1}}, {`i with n="^.+$"`, "i", []*labels.Matcher{nPlus}},