(ctx context.Context, r IndexReader, name string, hints *storage.LabelHints, matchers ...*labels.Matcher)
| 462 | } |
| 463 | |
| 464 | func labelValuesWithMatchers(ctx context.Context, r IndexReader, name string, hints *storage.LabelHints, matchers ...*labels.Matcher) ([]string, error) { |
| 465 | // Limit is applied at the end, after filtering. |
| 466 | allValues, err := r.LabelValues(ctx, name, nil) |
| 467 | if err != nil { |
| 468 | return nil, fmt.Errorf("fetching values of label %s: %w", name, err) |
| 469 | } |
| 470 | |
| 471 | // If we have a matcher for the label name, we can filter out values that don't match |
| 472 | // before we fetch postings. This is especially useful for labels with many values. |
| 473 | // e.g. __name__ with a selector like {__name__="xyz"} |
| 474 | hasMatchersForOtherLabels := false |
| 475 | for _, m := range matchers { |
| 476 | if m.Name != name { |
| 477 | hasMatchersForOtherLabels = true |
| 478 | continue |
| 479 | } |
| 480 | |
| 481 | // re-use the allValues slice to avoid allocations |
| 482 | // this is safe because the iteration is always ahead of the append |
| 483 | filteredValues := allValues[:0] |
| 484 | count := 1 |
| 485 | for _, v := range allValues { |
| 486 | if count%checkContextEveryNIterations == 0 && ctx.Err() != nil { |
| 487 | return nil, ctx.Err() |
| 488 | } |
| 489 | count++ |
| 490 | if m.Matches(v) { |
| 491 | filteredValues = append(filteredValues, v) |
| 492 | } |
| 493 | } |
| 494 | allValues = filteredValues |
| 495 | } |
| 496 | |
| 497 | if len(allValues) == 0 { |
| 498 | return nil, nil |
| 499 | } |
| 500 | |
| 501 | // If we don't have any matchers for other labels, then we're done. |
| 502 | if !hasMatchersForOtherLabels { |
| 503 | if hints != nil && hints.Limit > 0 && len(allValues) > hints.Limit { |
| 504 | allValues = allValues[:hints.Limit] |
| 505 | } |
| 506 | return allValues, nil |
| 507 | } |
| 508 | |
| 509 | p, err := PostingsForMatchers(ctx, r, matchers...) |
| 510 | if err != nil { |
| 511 | return nil, fmt.Errorf("fetching postings for matchers: %w", err) |
| 512 | } |
| 513 | |
| 514 | valuesPostings := make([]index.Postings, len(allValues)) |
| 515 | for i, value := range allValues { |
| 516 | valuesPostings[i], err = r.Postings(ctx, name, value) |
| 517 | if err != nil { |
| 518 | return nil, fmt.Errorf("fetching postings for %s=%q: %w", name, value, err) |
| 519 | } |
| 520 | } |
| 521 | indexes, err := index.FindIntersectingPostings(p, valuesPostings) |
searching dependent graphs…