mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-31 15:06:26 +00:00
wip
This commit is contained in:
parent
93c5f2f9bc
commit
89235a3489
6 changed files with 120 additions and 114 deletions
|
@ -24,32 +24,6 @@ type filter interface {
|
||||||
apply(bs *blockSearch, bm *bitmap)
|
apply(bs *blockSearch, bm *bitmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// notFilter negates the filter.
|
|
||||||
//
|
|
||||||
// It is expressed as `NOT f` or `!f` in LogsQL.
|
|
||||||
type notFilter struct {
|
|
||||||
f filter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fn *notFilter) String() string {
|
|
||||||
s := fn.f.String()
|
|
||||||
switch fn.f.(type) {
|
|
||||||
case *filterAnd, *filterOr:
|
|
||||||
s = "(" + s + ")"
|
|
||||||
}
|
|
||||||
return "!" + s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fn *notFilter) apply(bs *blockSearch, bm *bitmap) {
|
|
||||||
// Minimize the number of rows to check by the filter by applying it
|
|
||||||
// only to the rows, which match the bm, e.g. they may change the bm result.
|
|
||||||
bmTmp := getBitmap(bm.bitsLen)
|
|
||||||
bmTmp.copyFrom(bm)
|
|
||||||
fn.f.apply(bs, bmTmp)
|
|
||||||
bm.andNot(bmTmp)
|
|
||||||
putBitmap(bmTmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// streamFilter is the filter for `_stream:{...}`
|
// streamFilter is the filter for `_stream:{...}`
|
||||||
type streamFilter struct {
|
type streamFilter struct {
|
||||||
// f is the filter to apply
|
// f is the filter to apply
|
||||||
|
|
27
lib/logstorage/filter_not.go
Normal file
27
lib/logstorage/filter_not.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package logstorage
|
||||||
|
|
||||||
|
// filterNot negates the filter.
|
||||||
|
//
|
||||||
|
// It is expressed as `NOT f` or `!f` in LogsQL.
|
||||||
|
type filterNot struct {
|
||||||
|
f filter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fn *filterNot) String() string {
|
||||||
|
s := fn.f.String()
|
||||||
|
switch fn.f.(type) {
|
||||||
|
case *filterAnd, *filterOr:
|
||||||
|
s = "(" + s + ")"
|
||||||
|
}
|
||||||
|
return "!" + s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fn *filterNot) apply(bs *blockSearch, bm *bitmap) {
|
||||||
|
// Minimize the number of rows to check by the filter by applying it
|
||||||
|
// only to the rows, which match the bm, e.g. they may change the bm result.
|
||||||
|
bmTmp := getBitmap(bm.bitsLen)
|
||||||
|
bmTmp.copyFrom(bm)
|
||||||
|
fn.f.apply(bs, bmTmp)
|
||||||
|
bm.andNot(bmTmp)
|
||||||
|
putBitmap(bmTmp)
|
||||||
|
}
|
75
lib/logstorage/filter_not_test.go
Normal file
75
lib/logstorage/filter_not_test.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package logstorage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFilterNot(t *testing.T) {
|
||||||
|
columns := []column{
|
||||||
|
{
|
||||||
|
name: "foo",
|
||||||
|
values: []string{
|
||||||
|
"a foo",
|
||||||
|
"a foobar",
|
||||||
|
"aa abc a",
|
||||||
|
"ca afdf a,foobar baz",
|
||||||
|
"a fddf foobarbaz",
|
||||||
|
"",
|
||||||
|
"a foobar",
|
||||||
|
"a kjlkjf dfff",
|
||||||
|
"a ТЕСТЙЦУК НГКШ ",
|
||||||
|
"a !!,23.(!1)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// match
|
||||||
|
fn := &filterNot{
|
||||||
|
f: &phraseFilter{
|
||||||
|
fieldName: "foo",
|
||||||
|
phrase: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testFilterMatchForColumns(t, columns, fn, "foo", []int{0, 1, 2, 3, 4, 6, 7, 8, 9})
|
||||||
|
|
||||||
|
fn = &filterNot{
|
||||||
|
f: &phraseFilter{
|
||||||
|
fieldName: "foo",
|
||||||
|
phrase: "a",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testFilterMatchForColumns(t, columns, fn, "foo", []int{5})
|
||||||
|
|
||||||
|
fn = &filterNot{
|
||||||
|
f: &phraseFilter{
|
||||||
|
fieldName: "non-existing-field",
|
||||||
|
phrase: "foobar",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testFilterMatchForColumns(t, columns, fn, "foo", []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||||
|
|
||||||
|
fn = &filterNot{
|
||||||
|
f: &prefixFilter{
|
||||||
|
fieldName: "non-existing-field",
|
||||||
|
prefix: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testFilterMatchForColumns(t, columns, fn, "foo", []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||||
|
|
||||||
|
fn = &filterNot{
|
||||||
|
f: &prefixFilter{
|
||||||
|
fieldName: "foo",
|
||||||
|
prefix: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testFilterMatchForColumns(t, columns, fn, "foo", []int{5})
|
||||||
|
|
||||||
|
// mismatch
|
||||||
|
fn = &filterNot{
|
||||||
|
f: &phraseFilter{
|
||||||
|
fieldName: "non-existing-field",
|
||||||
|
phrase: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testFilterMatchForColumns(t, columns, fn, "foo", nil)
|
||||||
|
}
|
|
@ -362,7 +362,7 @@ func TestComplexFilters(t *testing.T) {
|
||||||
fieldName: "foo",
|
fieldName: "foo",
|
||||||
phrase: "foobar",
|
phrase: "foobar",
|
||||||
},
|
},
|
||||||
¬Filter{
|
&filterNot{
|
||||||
f: &phraseFilter{
|
f: &phraseFilter{
|
||||||
fieldName: "foo",
|
fieldName: "foo",
|
||||||
phrase: "baz",
|
phrase: "baz",
|
||||||
|
@ -391,7 +391,7 @@ func TestComplexFilters(t *testing.T) {
|
||||||
fieldName: "foo",
|
fieldName: "foo",
|
||||||
phrase: "foobaz",
|
phrase: "foobaz",
|
||||||
},
|
},
|
||||||
¬Filter{
|
&filterNot{
|
||||||
f: &phraseFilter{
|
f: &phraseFilter{
|
||||||
fieldName: "foo",
|
fieldName: "foo",
|
||||||
phrase: "baz",
|
phrase: "baz",
|
||||||
|
@ -420,7 +420,7 @@ func TestComplexFilters(t *testing.T) {
|
||||||
fieldName: "foo",
|
fieldName: "foo",
|
||||||
phrase: "foobar",
|
phrase: "foobar",
|
||||||
},
|
},
|
||||||
¬Filter{
|
&filterNot{
|
||||||
f: &phraseFilter{
|
f: &phraseFilter{
|
||||||
fieldName: "foo",
|
fieldName: "foo",
|
||||||
phrase: "baz",
|
phrase: "baz",
|
||||||
|
@ -453,7 +453,7 @@ func TestComplexFilters(t *testing.T) {
|
||||||
fieldName: "foo",
|
fieldName: "foo",
|
||||||
phrase: "foobar",
|
phrase: "foobar",
|
||||||
},
|
},
|
||||||
¬Filter{
|
&filterNot{
|
||||||
f: &phraseFilter{
|
f: &phraseFilter{
|
||||||
fieldName: "foo",
|
fieldName: "foo",
|
||||||
phrase: "qwert",
|
phrase: "qwert",
|
||||||
|
@ -480,76 +480,6 @@ func TestComplexFilters(t *testing.T) {
|
||||||
testFilterMatchForColumns(t, columns, f, "foo", []int{1, 3, 6})
|
testFilterMatchForColumns(t, columns, f, "foo", []int{1, 3, 6})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNotFilter(t *testing.T) {
|
|
||||||
columns := []column{
|
|
||||||
{
|
|
||||||
name: "foo",
|
|
||||||
values: []string{
|
|
||||||
"a foo",
|
|
||||||
"a foobar",
|
|
||||||
"aa abc a",
|
|
||||||
"ca afdf a,foobar baz",
|
|
||||||
"a fddf foobarbaz",
|
|
||||||
"",
|
|
||||||
"a foobar",
|
|
||||||
"a kjlkjf dfff",
|
|
||||||
"a ТЕСТЙЦУК НГКШ ",
|
|
||||||
"a !!,23.(!1)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// match
|
|
||||||
nf := ¬Filter{
|
|
||||||
f: &phraseFilter{
|
|
||||||
fieldName: "foo",
|
|
||||||
phrase: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
testFilterMatchForColumns(t, columns, nf, "foo", []int{0, 1, 2, 3, 4, 6, 7, 8, 9})
|
|
||||||
|
|
||||||
nf = ¬Filter{
|
|
||||||
f: &phraseFilter{
|
|
||||||
fieldName: "foo",
|
|
||||||
phrase: "a",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
testFilterMatchForColumns(t, columns, nf, "foo", []int{5})
|
|
||||||
|
|
||||||
nf = ¬Filter{
|
|
||||||
f: &phraseFilter{
|
|
||||||
fieldName: "non-existing-field",
|
|
||||||
phrase: "foobar",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
testFilterMatchForColumns(t, columns, nf, "foo", []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
|
|
||||||
|
|
||||||
nf = ¬Filter{
|
|
||||||
f: &prefixFilter{
|
|
||||||
fieldName: "non-existing-field",
|
|
||||||
prefix: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
testFilterMatchForColumns(t, columns, nf, "foo", []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
|
|
||||||
|
|
||||||
nf = ¬Filter{
|
|
||||||
f: &prefixFilter{
|
|
||||||
fieldName: "foo",
|
|
||||||
prefix: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
testFilterMatchForColumns(t, columns, nf, "foo", []int{5})
|
|
||||||
|
|
||||||
// mismatch
|
|
||||||
nf = ¬Filter{
|
|
||||||
f: &phraseFilter{
|
|
||||||
fieldName: "non-existing-field",
|
|
||||||
phrase: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
testFilterMatchForColumns(t, columns, nf, "foo", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTimeFilter(t *testing.T) {
|
func TestTimeFilter(t *testing.T) {
|
||||||
timestamps := []int64{
|
timestamps := []int64{
|
||||||
1,
|
1,
|
||||||
|
|
|
@ -242,17 +242,17 @@ func parseFilter(lex *lexer) (filter, error) {
|
||||||
if !lex.mustNextToken() || lex.isKeyword("|") {
|
if !lex.mustNextToken() || lex.isKeyword("|") {
|
||||||
return nil, fmt.Errorf("missing query")
|
return nil, fmt.Errorf("missing query")
|
||||||
}
|
}
|
||||||
fo, err := parseOrFilter(lex, "")
|
fo, err := parseFilterOr(lex, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return fo, nil
|
return fo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseOrFilter(lex *lexer, fieldName string) (filter, error) {
|
func parseFilterOr(lex *lexer, fieldName string) (filter, error) {
|
||||||
var filters []filter
|
var filters []filter
|
||||||
for {
|
for {
|
||||||
f, err := parseAndFilter(lex, fieldName)
|
f, err := parseFilterAnd(lex, fieldName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -274,7 +274,7 @@ func parseOrFilter(lex *lexer, fieldName string) (filter, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAndFilter(lex *lexer, fieldName string) (filter, error) {
|
func parseFilterAnd(lex *lexer, fieldName string) (filter, error) {
|
||||||
var filters []filter
|
var filters []filter
|
||||||
for {
|
for {
|
||||||
f, err := parseGenericFilter(lex, fieldName)
|
f, err := parseGenericFilter(lex, fieldName)
|
||||||
|
@ -320,7 +320,7 @@ func parseGenericFilter(lex *lexer, fieldName string) (filter, error) {
|
||||||
}
|
}
|
||||||
return parseParensFilter(lex, fieldName)
|
return parseParensFilter(lex, fieldName)
|
||||||
case lex.isKeyword("not", "!"):
|
case lex.isKeyword("not", "!"):
|
||||||
return parseNotFilter(lex, fieldName)
|
return parseFilterNot(lex, fieldName)
|
||||||
case lex.isKeyword("exact"):
|
case lex.isKeyword("exact"):
|
||||||
return parseExactFilter(lex, fieldName)
|
return parseExactFilter(lex, fieldName)
|
||||||
case lex.isKeyword("i"):
|
case lex.isKeyword("i"):
|
||||||
|
@ -444,7 +444,7 @@ func parseParensFilter(lex *lexer, fieldName string) (filter, error) {
|
||||||
if !lex.mustNextToken() {
|
if !lex.mustNextToken() {
|
||||||
return nil, fmt.Errorf("missing filter after '('")
|
return nil, fmt.Errorf("missing filter after '('")
|
||||||
}
|
}
|
||||||
f, err := parseOrFilter(lex, fieldName)
|
f, err := parseFilterOr(lex, fieldName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -455,7 +455,7 @@ func parseParensFilter(lex *lexer, fieldName string) (filter, error) {
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseNotFilter(lex *lexer, fieldName string) (filter, error) {
|
func parseFilterNot(lex *lexer, fieldName string) (filter, error) {
|
||||||
notKeyword := lex.token
|
notKeyword := lex.token
|
||||||
if !lex.mustNextToken() {
|
if !lex.mustNextToken() {
|
||||||
return nil, fmt.Errorf("missing filters after '%s'", notKeyword)
|
return nil, fmt.Errorf("missing filters after '%s'", notKeyword)
|
||||||
|
@ -464,14 +464,14 @@ func parseNotFilter(lex *lexer, fieldName string) (filter, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
nf, ok := f.(*notFilter)
|
fn, ok := f.(*filterNot)
|
||||||
if ok {
|
if ok {
|
||||||
return nf.f, nil
|
return fn.f, nil
|
||||||
}
|
}
|
||||||
nf = ¬Filter{
|
fn = &filterNot{
|
||||||
f: f,
|
f: f,
|
||||||
}
|
}
|
||||||
return nf, nil
|
return fn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAnyCaseFilter(lex *lexer, fieldName string) (filter, error) {
|
func parseAnyCaseFilter(lex *lexer, fieldName string) (filter, error) {
|
||||||
|
|
|
@ -343,7 +343,7 @@ func hasStreamFilters(f filter) bool {
|
||||||
return hasStreamFiltersInList(t.filters)
|
return hasStreamFiltersInList(t.filters)
|
||||||
case *filterOr:
|
case *filterOr:
|
||||||
return hasStreamFiltersInList(t.filters)
|
return hasStreamFiltersInList(t.filters)
|
||||||
case *notFilter:
|
case *filterNot:
|
||||||
return hasStreamFilters(t.f)
|
return hasStreamFilters(t.f)
|
||||||
case *streamFilter:
|
case *streamFilter:
|
||||||
return true
|
return true
|
||||||
|
@ -371,8 +371,8 @@ func initStreamFilters(tenantIDs []TenantID, idb *indexdb, f filter) filter {
|
||||||
return &filterOr{
|
return &filterOr{
|
||||||
filters: initStreamFiltersList(tenantIDs, idb, t.filters),
|
filters: initStreamFiltersList(tenantIDs, idb, t.filters),
|
||||||
}
|
}
|
||||||
case *notFilter:
|
case *filterNot:
|
||||||
return ¬Filter{
|
return &filterNot{
|
||||||
f: initStreamFilters(tenantIDs, idb, t.f),
|
f: initStreamFilters(tenantIDs, idb, t.f),
|
||||||
}
|
}
|
||||||
case *streamFilter:
|
case *streamFilter:
|
||||||
|
|
Loading…
Reference in a new issue