diff --git a/lib/storage/index_db.go b/lib/storage/index_db.go index 61a8810bf5..fdb2a66003 100644 --- a/lib/storage/index_db.go +++ b/lib/storage/index_db.go @@ -1430,7 +1430,7 @@ func matchTagFilters(mn *MetricName, tfs []*tagFilter, kb *bytesutil.ByteBuffer) continue } - // Found the matching tag name. Match for the value. + // Found the matching tag name. Match the value. b := tag.Marshal(kb.B) kb.B = b[:len(kb.B)] ok, err := matchTagFilter(b, tf) @@ -1443,7 +1443,7 @@ func matchTagFilters(mn *MetricName, tfs []*tagFilter, kb *bytesutil.ByteBuffer) tagMatched = true break } - if !tagMatched { + if !tagMatched && !tf.isNegative { // Matching tag name wasn't found. return false, nil } diff --git a/lib/storage/index_db_test.go b/lib/storage/index_db_test.go index a0cd31cf08..6be5f63932 100644 --- a/lib/storage/index_db_test.go +++ b/lib/storage/index_db_test.go @@ -804,8 +804,8 @@ func TestMatchTagFilters(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %s", err) } - if ok { - t.Fatalf("Shouldn't match") + if !ok { + t.Fatalf("Should match") } tfs.Reset(mn.AccountID, mn.ProjectID) if err := tfs.Add([]byte("non-existing-tag"), []byte("foob.+metric"), true, true); err != nil { @@ -815,8 +815,19 @@ func TestMatchTagFilters(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %s", err) } - if ok { - t.Fatalf("Shouldn't match") + if !ok { + t.Fatalf("Should match") + } + tfs.Reset(mn.AccountID, mn.ProjectID) + if err := tfs.Add([]byte("non-existing-tag"), []byte(".+"), true, true); err != nil { + t.Fatalf("cannot add regexp, negative filter: %s", err) + } + ok, err = matchTagFilters(&mn, toTFPointers(tfs.tfs), &bb) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + if !ok { + t.Fatalf("Should match") } // Negative match by existing tag @@ -910,6 +921,17 @@ func TestMatchTagFilters(t *testing.T) { if !ok { t.Fatalf("Should match") } + tfs.Reset(mn.AccountID, mn.ProjectID) + if err := tfs.Add([]byte("key 3"), []byte(""), true, false); err != nil { + t.Fatalf("cannot add regexp, negative filter: %s", err) + } + ok, err = matchTagFilters(&mn, toTFPointers(tfs.tfs), &bb) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + if !ok { + t.Fatalf("Should match") + } // Positive match by multiple tags and MetricGroup tfs.Reset(mn.AccountID, mn.ProjectID) diff --git a/lib/storage/tag_filters_test.go b/lib/storage/tag_filters_test.go index 14d3594cca..016ed8c49c 100644 --- a/lib/storage/tag_filters_test.go +++ b/lib/storage/tag_filters_test.go @@ -308,6 +308,62 @@ func TestTagFilterMatchSuffix(t *testing.T) { mismatch("bar") match("xhttpbar") }) + t.Run("non-empty-string-regexp-negative-match", func(t *testing.T) { + value := ".+" + isNegative := true + isRegexp := true + expectedPrefix := tvNoTrailingTagSeparator("") + init(value, isNegative, isRegexp, expectedPrefix) + if len(tf.orSuffixes) != 0 { + t.Fatalf("unexpected non-zero number of or suffixes: %d; %q", len(tf.orSuffixes), tf.orSuffixes) + } + + match("") + mismatch("x") + mismatch("foo") + }) + t.Run("non-empty-string-regexp-match", func(t *testing.T) { + value := ".+" + isNegative := false + isRegexp := true + expectedPrefix := tvNoTrailingTagSeparator("") + init(value, isNegative, isRegexp, expectedPrefix) + if len(tf.orSuffixes) != 0 { + t.Fatalf("unexpected non-zero number of or suffixes: %d; %q", len(tf.orSuffixes), tf.orSuffixes) + } + + mismatch("") + match("x") + match("foo") + }) + t.Run("match-all-regexp-negative-match", func(t *testing.T) { + value := ".*" + isNegative := true + isRegexp := true + expectedPrefix := tvNoTrailingTagSeparator("") + init(value, isNegative, isRegexp, expectedPrefix) + if len(tf.orSuffixes) != 0 { + t.Fatalf("unexpected non-zero number of or suffixes: %d; %q", len(tf.orSuffixes), tf.orSuffixes) + } + + mismatch("") + mismatch("x") + mismatch("foo") + }) + t.Run("match-all-regexp-match", func(t *testing.T) { + value := ".*" + isNegative := false + isRegexp := true + expectedPrefix := tvNoTrailingTagSeparator("") + init(value, isNegative, isRegexp, expectedPrefix) + if len(tf.orSuffixes) != 0 { + t.Fatalf("unexpected non-zero number of or suffixes: %d; %q", len(tf.orSuffixes), tf.orSuffixes) + } + + match("") + match("x") + match("foo") + }) } func TestGetOrValues(t *testing.T) {