diff --git a/filter/series_by_tag.go b/filter/series_by_tag.go index fc4f6b9a6..90b522964 100644 --- a/filter/series_by_tag.go +++ b/filter/series_by_tag.go @@ -170,6 +170,7 @@ func createMatchingHandlerForOneTag( compatibility *Compatibility, ) (MatchingHandler, error) { var matchingHandlerCondition func(string) bool + var err error allowMatchEmpty := false switch spec.Operator { @@ -182,28 +183,13 @@ func createMatchingHandlerForOneTag( matchingHandlerCondition = func(value string) bool { return value != spec.Value } - case MatchOperator: + case MatchOperator, NotMatchOperator: allowMatchEmpty = compatibility.AllowRegexMatchEmpty - matchRegex, err := newMatchRegex(spec.Value, compatibility) + matchingHandlerCondition, err = handleRegexMatch(spec, compatibility) if err != nil { return nil, err } - - matchingHandlerCondition = func(value string) bool { - return matchRegex.MatchString(value) - } - case NotMatchOperator: - allowMatchEmpty = compatibility.AllowRegexMatchEmpty - - matchRegex, err := newMatchRegex(spec.Value, compatibility) - if err != nil { - return nil, err - } - - matchingHandlerCondition = func(value string) bool { - return !matchRegex.MatchString(value) - } default: matchingHandlerCondition = func(_ string) bool { return false @@ -224,14 +210,37 @@ func createMatchingHandlerForOneTag( }, nil } +func handleRegexMatch( + spec TagSpec, + compatibility *Compatibility, +) (func(string) bool, error) { + isMatchOperator := spec.Operator == MatchOperator + + // We don't need to create a regular for the asterisk, because in such a tag + // it is the fact of the tag's presence that matters, not its value. + if spec.Value == "*" || spec.Value == ".*" { + return func(value string) bool { + return isMatchOperator + }, nil + } + + matchRegex, err := newMatchRegex(spec.Value, compatibility) + if err != nil { + return nil, fmt.Errorf("failed to create new match regex: %w", err) + } + + return func(value string) bool { + matchRes := matchRegex.MatchString(value) + + // Invert the result depending on the match operator. + return isMatchOperator == matchRes + }, nil +} + func newMatchRegex( tagValue string, compatibility *Compatibility, ) (*regexp.Regexp, error) { - if tagValue == "*" { - tagValue = ".*" - } - if !compatibility.AllowRegexLooseStartMatch { tagValue = "^" + tagValue } diff --git a/filter/series_by_tag_pattern_index_test.go b/filter/series_by_tag_pattern_index_test.go index 030ece80b..7f560735a 100644 --- a/filter/series_by_tag_pattern_index_test.go +++ b/filter/series_by_tag_pattern_index_test.go @@ -223,6 +223,15 @@ func TestSeriesByTagPatternIndex(t *testing.T) { "tag2=~.*": { {"tag2", MatchOperator, ".*"}, }, + "tag2!=~*": { + {"tag2", NotMatchOperator, "*"}, + }, + "tag2!=~.*": { + {"tag2", NotMatchOperator, ".*"}, + }, + "tag2!=~al2": { + {"tag2", NotMatchOperator, "al2"}, + }, "tag1=val1;tag2=val2": { {"tag1", EqualOperator, "val1"}, {"tag2", EqualOperator, "val2"}, @@ -293,6 +302,7 @@ func TestSeriesByTagPatternIndex(t *testing.T) { "name=~cpu;tag1=val1", "name=~test1", "tag1=~al1", + "tag2!=~al2", "tag2=~*", "tag2=~.*", }, @@ -322,6 +332,18 @@ func TestSeriesByTagPatternIndex(t *testing.T) { "tag2=~.*", }, }, + { + "cpu.test1.test3", + map[string]string{"tag2": "val3"}, + []string{ + "name=cpu.*.*", + "name=cpu.test1.*", + "name=~test1", + "tag2!=~al2", + "tag2=~*", + "tag2=~.*", + }, + }, { "cpu.test1.test2", map[string]string{"tag1": "val1", "tag2": "val2"},