Repository: prometheus/prometheus
File: tsdb/querier.go (Go)

Code before:
	}

	// Fast-path for MatchNotEqual matching.
	// Inverse of a MatchNotEqual is MatchEqual (double negation).
	if m.Type == labels.MatchNotEqual {
		return ix.Postings(ctx, m.Name, m.Value)
	}

	// If the matcher being inverted is =~"" or ="", we just want all the values.
	if m.Value == "" && (m.Type == labels.MatchRegexp || m.Type == labels.MatchEqual) {
		it := ix.PostingsForAllLabelValues(ctx, m.Name)
		return it, it.Err()
	}

	it := ix.PostingsForLabelMatching(ctx, m.Name, func(s string) bool {
		return !m.Matches(s)
	})
	return it, it.Err()
}

func labelValuesWithMatchers(ctx context.Context, r IndexReader, name string, hints *storage.LabelHints, matchers ...*labels.Matcher) ([]string, error) {
	if hints == nil {
		hints = &storage.LabelHints{}
	}

	// Do not apply limits here. We need all values.
	allValues, err := r.LabelValues(ctx, name, &storage.LabelHints{})
	if err != nil {
		return nil, fmt.Errorf("fetching values of label %s: %w", name, err)
	}

	// 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"}
	hasMatchersForOtherLabels := false
	for _, m := range matchers {
		if m.Name != name {
			hasMatchersForOtherLabels = true
			continue
		}

		// re-use the allValues slice to avoid allocations
		// this is safe because the iteration is always ahead of the append
		filteredValues := allValues[:0]
		count := 1
		for _, v := range allValues {
			if count%checkContextEveryNIterations == 0 && ctx.Err() != nil {
				return nil, ctx.Err()
			}
			count++
			if m.Matches(v) {

Review comment (suggestion):
```suggestion
	// Limit is applied at the end, after filtering.
```

Code after:
	}

	// Fast-path for MatchNotEqual matching.
	// Inverse of a MatchNotEqual is MatchEqual (double negation).
	if m.Type == labels.MatchNotEqual {
		return ix.Postings(ctx, m.Name, m.Value)
	}

	// If the matcher being inverted is =~"" or ="", we just want all the values.
	if m.Value == "" && (m.Type == labels.MatchRegexp || m.Type == labels.MatchEqual) {
		it := ix.PostingsForAllLabelValues(ctx, m.Name)
		return it, it.Err()
	}

	it := ix.PostingsForLabelMatching(ctx, m.Name, func(s string) bool {
		return !m.Matches(s)
	})
	return it, it.Err()
}

func labelValuesWithMatchers(ctx context.Context, r IndexReader, name string, hints *storage.LabelHints, matchers ...*labels.Matcher) ([]string, error) {
	// Limit is applied at the end, after filtering.
	allValues, err := r.LabelValues(ctx, name, nil)
	if err != nil {
		return nil, fmt.Errorf("fetching values of label %s: %w", name, err)
	}

	// 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"}
	hasMatchersForOtherLabels := false
	for _, m := range matchers {
		if m.Name != name {
			hasMatchersForOtherLabels = true
			continue
		}

		// re-use the allValues slice to avoid allocations
		// this is safe because the iteration is always ahead of the append
		filteredValues := allValues[:0]
		count := 1
		for _, v := range allValues {
			if count%checkContextEveryNIterations == 0 && ctx.Err() != nil {
				return nil, ctx.Err()
			}
			count++
			if m.Matches(v) {
				filteredValues = append(filteredValues, v)
			}
		}
		allValues = filteredValues