mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-01-20 15:16:42 +00:00
wip
This commit is contained in:
parent
01fc253eb7
commit
d95b491809
3 changed files with 100 additions and 8 deletions
|
@ -11,6 +11,7 @@ import (
|
|||
// Example LogsQL: `fieldName:range(minValue, maxValue]`
|
||||
type filterRange struct {
|
||||
fieldName string
|
||||
|
||||
minValue float64
|
||||
maxValue float64
|
||||
|
||||
|
@ -18,7 +19,7 @@ type filterRange struct {
|
|||
}
|
||||
|
||||
func (fr *filterRange) String() string {
|
||||
return quoteFieldNameIfNeeded(fr.fieldName) + "range" + fr.stringRepr
|
||||
return quoteFieldNameIfNeeded(fr.fieldName) + fr.stringRepr
|
||||
}
|
||||
|
||||
func (fr *filterRange) updateNeededFields(neededFields fieldsSet) {
|
||||
|
|
|
@ -406,6 +406,10 @@ func parseGenericFilter(lex *lexer, fieldName string) (filter, error) {
|
|||
return nil, fmt.Errorf("missing whitespace before the search word %q", lex.prevToken)
|
||||
}
|
||||
return parseParensFilter(lex, fieldName)
|
||||
case lex.isKeyword(">"):
|
||||
return parseFilterGT(lex, fieldName)
|
||||
case lex.isKeyword("<"):
|
||||
return parseFilterLT(lex, fieldName)
|
||||
case lex.isKeyword("not", "!"):
|
||||
return parseFilterNot(lex, fieldName)
|
||||
case lex.isKeyword("exact"):
|
||||
|
@ -754,6 +758,70 @@ func parseFilterRegexp(lex *lexer, fieldName string) (filter, error) {
|
|||
})
|
||||
}
|
||||
|
||||
func parseFilterGT(lex *lexer, fieldName string) (filter, error) {
|
||||
if fieldName == "" {
|
||||
return nil, fmt.Errorf("'>' and '>=' must be prefixed with the field name")
|
||||
}
|
||||
lex.nextToken()
|
||||
|
||||
includeMinValue := false
|
||||
op := ">"
|
||||
if lex.isKeyword("=") {
|
||||
lex.nextToken()
|
||||
includeMinValue = true
|
||||
op = ">="
|
||||
}
|
||||
|
||||
minValue, fStr, err := parseFloat64(lex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse number after '%s': %w", op, err)
|
||||
}
|
||||
|
||||
if !includeMinValue {
|
||||
minValue = nextafter(minValue, inf)
|
||||
}
|
||||
fr := &filterRange{
|
||||
fieldName: fieldName,
|
||||
minValue: minValue,
|
||||
maxValue: inf,
|
||||
|
||||
stringRepr: op + fStr,
|
||||
}
|
||||
return fr, nil
|
||||
}
|
||||
|
||||
func parseFilterLT(lex *lexer, fieldName string) (filter, error) {
|
||||
if fieldName == "" {
|
||||
return nil, fmt.Errorf("'<' and '<=' must be prefixed with the field name")
|
||||
}
|
||||
lex.nextToken()
|
||||
|
||||
includeMaxValue := false
|
||||
op := "<"
|
||||
if lex.isKeyword("=") {
|
||||
lex.nextToken()
|
||||
includeMaxValue = true
|
||||
op = "<="
|
||||
}
|
||||
|
||||
maxValue, fStr, err := parseFloat64(lex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse number after '%s': %w", op, err)
|
||||
}
|
||||
|
||||
if !includeMaxValue {
|
||||
maxValue = nextafter(maxValue, -inf)
|
||||
}
|
||||
fr := &filterRange{
|
||||
fieldName: fieldName,
|
||||
minValue: -inf,
|
||||
maxValue: maxValue,
|
||||
|
||||
stringRepr: op + fStr,
|
||||
}
|
||||
return fr, nil
|
||||
}
|
||||
|
||||
func parseFilterRange(lex *lexer, fieldName string) (filter, error) {
|
||||
funcName := lex.token
|
||||
lex.nextToken()
|
||||
|
@ -801,19 +869,19 @@ func parseFilterRange(lex *lexer, fieldName string) (filter, error) {
|
|||
}
|
||||
lex.nextToken()
|
||||
|
||||
stringRepr := ""
|
||||
stringRepr := "range"
|
||||
if includeMinValue {
|
||||
stringRepr += "["
|
||||
} else {
|
||||
stringRepr += "("
|
||||
minValue = math.Nextafter(minValue, inf)
|
||||
minValue = nextafter(minValue, inf)
|
||||
}
|
||||
stringRepr += minValueStr + ", " + maxValueStr
|
||||
if includeMaxValue {
|
||||
stringRepr += "]"
|
||||
} else {
|
||||
stringRepr += ")"
|
||||
maxValue = math.Nextafter(maxValue, -inf)
|
||||
maxValue = nextafter(maxValue, -inf)
|
||||
}
|
||||
|
||||
fr := &filterRange{
|
||||
|
@ -1216,3 +1284,10 @@ func parseInt(s string) (int64, error) {
|
|||
}
|
||||
return nn, nil
|
||||
}
|
||||
|
||||
func nextafter(f, xInf float64) float64 {
|
||||
if math.IsInf(f, 0) {
|
||||
return f
|
||||
}
|
||||
return math.Nextafter(f, xInf)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package logstorage
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -492,15 +491,25 @@ func TestParseRangeFilter(t *testing.T) {
|
|||
f(`range:range["-1.234e5", "-2e-5"]`, `range`, -1.234e5, -2e-5)
|
||||
|
||||
f(`_msg:range[1, 2]`, `_msg`, 1, 2)
|
||||
f(`:range(1, 2)`, ``, math.Nextafter(1, inf), math.Nextafter(2, -inf))
|
||||
f(`range[1, 2)`, ``, 1, math.Nextafter(2, -inf))
|
||||
f(`range("1", 2]`, ``, math.Nextafter(1, inf), 2)
|
||||
f(`:range(1, 2)`, ``, nextafter(1, inf), nextafter(2, -inf))
|
||||
f(`range[1, 2)`, ``, 1, nextafter(2, -inf))
|
||||
f(`range("1", 2]`, ``, nextafter(1, inf), 2)
|
||||
|
||||
f(`response_size:range[1KB, 10MiB]`, `response_size`, 1_000, 10*(1<<20))
|
||||
f(`response_size:range[1G, 10Ti]`, `response_size`, 1_000_000_000, 10*(1<<40))
|
||||
f(`response_size:range[10, inf]`, `response_size`, 10, inf)
|
||||
|
||||
f(`duration:range[100ns, 1y2w2.5m3s5ms]`, `duration`, 100, 1*nsecsPerYear+2*nsecsPerWeek+2.5*nsecsPerMinute+3*nsecsPerSecond+5*nsecsPerMillisecond)
|
||||
|
||||
f(`foo:>10.43`, `foo`, nextafter(10.43, inf), inf)
|
||||
f(`foo: > -10.43`, `foo`, nextafter(-10.43, inf), inf)
|
||||
f(`foo:>=10.43`, `foo`, 10.43, inf)
|
||||
f(`foo: >= -10.43`, `foo`, -10.43, inf)
|
||||
|
||||
f(`foo:<10.43`, `foo`, -inf, nextafter(10.43, -inf))
|
||||
f(`foo: < -10.43`, `foo`, -inf, nextafter(-10.43, -inf))
|
||||
f(`foo:<=10.43`, `foo`, -inf, 10.43)
|
||||
f(`foo: <= 10.43`, `foo`, -inf, 10.43)
|
||||
}
|
||||
|
||||
func TestParseQuerySuccess(t *testing.T) {
|
||||
|
@ -723,6 +732,13 @@ func TestParseQuerySuccess(t *testing.T) {
|
|||
f(`range(0x1ff, inf)`, `range(0x1ff, inf)`)
|
||||
f(`range(-INF,+inF)`, `range(-INF, +inF)`)
|
||||
f(`range(1.5K, 22.5GiB)`, `range(1.5K, 22.5GiB)`)
|
||||
f(`foo:range(5,inf)`, `foo:range(5, inf)`)
|
||||
|
||||
// >, >=, < and <= filter
|
||||
f(`foo: > 10.5M`, `foo:>10.5M`)
|
||||
f(`foo: >= 10.5M`, `foo:>=10.5M`)
|
||||
f(`foo: < 10.5M`, `foo:<10.5M`)
|
||||
f(`foo: <= 10.5M`, `foo:<=10.5M`)
|
||||
|
||||
// re filter
|
||||
f("re('foo|ba(r.+)')", `re("foo|ba(r.+)")`)
|
||||
|
|
Loading…
Reference in a new issue