Label matchers (equal, not-equal, regexp, not-regexp) in labels/, postings intersection/union/difference in internal/postings/, LabelValues and AllPostings on index, block, and head readers. ingot.go wired to real DB backed by head + block readers. Querier snapshots overlapping blocks, Select resolves matchers to postings, merged iterator chains blocks then head with block-wins dedup. Oracle tests compare every query against a naive []sample reference across head-only, block-only, head+block merge seam, multiple blocks, all four matcher types, and time range filtering.
96 lines
2.1 KiB
Go
96 lines
2.1 KiB
Go
package labels
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
)
|
|
|
|
// MatchType identifies the type of a label matcher.
|
|
type MatchType int
|
|
|
|
const (
|
|
MatchEqual MatchType = iota
|
|
MatchNotEqual
|
|
MatchRegexp
|
|
MatchNotRegexp
|
|
)
|
|
|
|
func (m MatchType) String() string {
|
|
switch m {
|
|
case MatchEqual:
|
|
return "="
|
|
case MatchNotEqual:
|
|
return "!="
|
|
case MatchRegexp:
|
|
return "=~"
|
|
case MatchNotRegexp:
|
|
return "!~"
|
|
default:
|
|
return "??"
|
|
}
|
|
}
|
|
|
|
// Matcher matches label values against a pattern.
|
|
type Matcher struct {
|
|
Type MatchType
|
|
Name string
|
|
Value string
|
|
re *regexp.Regexp // compiled for MatchRegexp/MatchNotRegexp
|
|
}
|
|
|
|
// NewMatcher creates a new Matcher. For regex types, the value is compiled
|
|
// as a full-match regular expression (anchored with ^(?:...)$).
|
|
func NewMatcher(typ MatchType, name, value string) (*Matcher, error) {
|
|
m := &Matcher{Type: typ, Name: name, Value: value}
|
|
if typ == MatchRegexp || typ == MatchNotRegexp {
|
|
re, err := regexp.Compile("^(?:" + value + ")$")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("labels: bad matcher regex %q: %w", value, err)
|
|
}
|
|
m.re = re
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
// MustNewMatcher is like NewMatcher but panics on error.
|
|
func MustNewMatcher(typ MatchType, name, value string) *Matcher {
|
|
m, err := NewMatcher(typ, name, value)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return m
|
|
}
|
|
|
|
// Matches reports whether the given label value satisfies this matcher.
|
|
func (m *Matcher) Matches(v string) bool {
|
|
switch m.Type {
|
|
case MatchEqual:
|
|
return v == m.Value
|
|
case MatchNotEqual:
|
|
return v != m.Value
|
|
case MatchRegexp:
|
|
return m.re.MatchString(v)
|
|
case MatchNotRegexp:
|
|
return !m.re.MatchString(v)
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (m *Matcher) String() string {
|
|
return fmt.Sprintf("%s%s%q", m.Name, m.Type, m.Value)
|
|
}
|
|
|
|
// FromStrings creates a sorted label set from alternating name/value pairs.
|
|
// Panics if an odd number of strings is provided.
|
|
func FromStrings(ss ...string) []Label {
|
|
if len(ss)%2 != 0 {
|
|
panic("labels.FromStrings: odd number of arguments")
|
|
}
|
|
ls := make([]Label, len(ss)/2)
|
|
for i := 0; i < len(ss); i += 2 {
|
|
ls[i/2] = Label{Name: ss[i], Value: ss[i+1]}
|
|
}
|
|
return Sort(ls)
|
|
}
|