mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-31 15:06:26 +00:00
wip
This commit is contained in:
parent
1e2f5e7294
commit
b60cbd5c54
6 changed files with 50 additions and 13 deletions
|
@ -573,6 +573,12 @@ For example, the following query matches the `error` value in the field `log:lev
|
||||||
"log:level":exact("error")
|
"log:level":exact("error")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The `exact(...)` filter can be replaced with `=...` for convenience. For example, the following query is equivalent to the previous one:
|
||||||
|
|
||||||
|
```logsql
|
||||||
|
"log:level":="error"
|
||||||
|
```
|
||||||
|
|
||||||
See also:
|
See also:
|
||||||
|
|
||||||
- [Exact prefix filter](#exact-prefix-filter)
|
- [Exact prefix filter](#exact-prefix-filter)
|
||||||
|
@ -625,6 +631,12 @@ For example, the following query matches `log:level` values starting with `err`
|
||||||
"log:level":exact("err"*)
|
"log:level":exact("err"*)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The `exact(...)` filter can be replaced with `=...` for convenience. For example, the following query is equivalent to the previous one:
|
||||||
|
|
||||||
|
```logsql
|
||||||
|
"log:level":="err"*
|
||||||
|
```
|
||||||
|
|
||||||
See also:
|
See also:
|
||||||
|
|
||||||
- [Exact filter](#exact-filter)
|
- [Exact filter](#exact-filter)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
// filterExact matches the exact value.
|
// filterExact matches the exact value.
|
||||||
//
|
//
|
||||||
// Example LogsQL: `fieldName:exact("foo bar")`
|
// Example LogsQL: `fieldName:exact("foo bar")` of `fieldName:="foo bar"
|
||||||
type filterExact struct {
|
type filterExact struct {
|
||||||
fieldName string
|
fieldName string
|
||||||
value string
|
value string
|
||||||
|
@ -21,7 +21,7 @@ type filterExact struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fe *filterExact) String() string {
|
func (fe *filterExact) String() string {
|
||||||
return fmt.Sprintf("%sexact(%s)", quoteFieldNameIfNeeded(fe.fieldName), quoteTokenIfNeeded(fe.value))
|
return fmt.Sprintf("%s=%s", quoteFieldNameIfNeeded(fe.fieldName), quoteTokenIfNeeded(fe.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fe *filterExact) updateNeededFields(neededFields fieldsSet) {
|
func (fe *filterExact) updateNeededFields(neededFields fieldsSet) {
|
||||||
|
|
|
@ -20,7 +20,7 @@ type filterExactPrefix struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fep *filterExactPrefix) String() string {
|
func (fep *filterExactPrefix) String() string {
|
||||||
return fmt.Sprintf("%sexact(%s*)", quoteFieldNameIfNeeded(fep.fieldName), quoteTokenIfNeeded(fep.prefix))
|
return fmt.Sprintf("%s=%s*", quoteFieldNameIfNeeded(fep.fieldName), quoteTokenIfNeeded(fep.prefix))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fep *filterExactPrefix) updateNeededFields(neededFields fieldsSet) {
|
func (fep *filterExactPrefix) updateNeededFields(neededFields fieldsSet) {
|
||||||
|
|
|
@ -595,6 +595,8 @@ func parseGenericFilter(lex *lexer, fieldName string) (filter, error) {
|
||||||
return parseFilterGT(lex, fieldName)
|
return parseFilterGT(lex, fieldName)
|
||||||
case lex.isKeyword("<"):
|
case lex.isKeyword("<"):
|
||||||
return parseFilterLT(lex, fieldName)
|
return parseFilterLT(lex, fieldName)
|
||||||
|
case lex.isKeyword("="):
|
||||||
|
return parseFilterEQ(lex, fieldName)
|
||||||
case lex.isKeyword("not", "!"):
|
case lex.isKeyword("not", "!"):
|
||||||
return parseFilterNot(lex, fieldName)
|
return parseFilterNot(lex, fieldName)
|
||||||
case lex.isKeyword("exact"):
|
case lex.isKeyword("exact"):
|
||||||
|
@ -1015,6 +1017,24 @@ func parseFilterRegexp(lex *lexer, fieldName string) (filter, error) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseFilterEQ(lex *lexer, fieldName string) (filter, error) {
|
||||||
|
lex.nextToken()
|
||||||
|
phrase := getCompoundFuncArg(lex)
|
||||||
|
if lex.isKeyword("*") && !lex.isSkippedSpace {
|
||||||
|
lex.nextToken()
|
||||||
|
f := &filterExactPrefix{
|
||||||
|
fieldName: fieldName,
|
||||||
|
prefix: phrase,
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
f := &filterExact{
|
||||||
|
fieldName: fieldName,
|
||||||
|
value: phrase,
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
func parseFilterGT(lex *lexer, fieldName string) (filter, error) {
|
func parseFilterGT(lex *lexer, fieldName string) (filter, error) {
|
||||||
lex.nextToken()
|
lex.nextToken()
|
||||||
|
|
||||||
|
@ -1148,7 +1168,7 @@ func parseFilterRange(lex *lexer, fieldName string) (filter, error) {
|
||||||
func parseFloat64(lex *lexer) (float64, string, error) {
|
func parseFloat64(lex *lexer) (float64, string, error) {
|
||||||
s, err := getCompoundToken(lex)
|
s, err := getCompoundToken(lex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, "", fmt.Errorf("cannot parse float64: %w", err)
|
return 0, "", fmt.Errorf("cannot parse float64 from %q: %w", s, err)
|
||||||
}
|
}
|
||||||
f, err := strconv.ParseFloat(s, 64)
|
f, err := strconv.ParseFloat(s, 64)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -1161,7 +1181,7 @@ func parseFloat64(lex *lexer) (float64, string, error) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return float64(n), s, nil
|
return float64(n), s, nil
|
||||||
}
|
}
|
||||||
return 0, "", fmt.Errorf("cannot parse %q as float64: %w", lex.token, err)
|
return 0, "", fmt.Errorf("cannot parse %q as float64: %w", s, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseFuncArg(lex *lexer, fieldName string, callback func(args string) (filter, error)) (filter, error) {
|
func parseFuncArg(lex *lexer, fieldName string, callback func(args string) (filter, error)) (filter, error) {
|
||||||
|
|
|
@ -689,12 +689,17 @@ func TestParseQuerySuccess(t *testing.T) {
|
||||||
f("string_range-a:x", `string_range-a:x`)
|
f("string_range-a:x", `string_range-a:x`)
|
||||||
|
|
||||||
// exact filter
|
// exact filter
|
||||||
f("exact(foo)", `exact(foo)`)
|
f("exact(foo)", `=foo`)
|
||||||
f("exact(foo*)", `exact(foo*)`)
|
f("exact(foo*)", `=foo*`)
|
||||||
f("exact('foo bar),|baz')", `exact("foo bar),|baz")`)
|
f("exact('foo bar),|baz')", `="foo bar),|baz"`)
|
||||||
f("exact('foo bar),|baz'*)", `exact("foo bar),|baz"*)`)
|
f("exact('foo bar),|baz'*)", `="foo bar),|baz"*`)
|
||||||
f(`exact(foo/b:ar)`, `exact("foo/b:ar")`)
|
f(`exact(foo/b:ar)`, `="foo/b:ar"`)
|
||||||
f(`foo:exact(foo/b:ar*)`, `foo:exact("foo/b:ar"*)`)
|
f(`foo:exact(foo/b:ar*)`, `foo:="foo/b:ar"*`)
|
||||||
|
f(`exact("foo/bar")`, `="foo/bar"`)
|
||||||
|
f(`exact('foo/bar')`, `="foo/bar"`)
|
||||||
|
f(`="foo/bar"`, `="foo/bar"`)
|
||||||
|
f("=foo=bar =b<=a>z ='abc'*", `="foo=bar" ="b<=a>z" =abc*`)
|
||||||
|
f("==foo =>=bar x : ( = =a<b*='c*' >=20)", `="=foo" =">=bar" x:="=a<b"* x:="c*" x:>=20`)
|
||||||
|
|
||||||
// i filter
|
// i filter
|
||||||
f("i(foo)", `i(foo)`)
|
f("i(foo)", `i(foo)`)
|
||||||
|
|
|
@ -114,7 +114,7 @@ func TestPipeExtract(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// single row, if match
|
// single row, if match
|
||||||
f(`extract if (x:baz) "foo=<bar> baz=<xx>" from x`, [][]Field{
|
f(`extract if (x:baz) "foo=<bar> baz=<xx>" from "x"`, [][]Field{
|
||||||
{
|
{
|
||||||
{"x", `a foo=cc baz=aa b`},
|
{"x", `a foo=cc baz=aa b`},
|
||||||
{"bar", "abc"},
|
{"bar", "abc"},
|
||||||
|
@ -128,7 +128,7 @@ func TestPipeExtract(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// single row, if mismatch
|
// single row, if mismatch
|
||||||
f(`extract if (bar:"") "foo=<bar> baz=<xx>" from x`, [][]Field{
|
f(`extract if (bar:"") "foo=<bar> baz=<xx>" from 'x'`, [][]Field{
|
||||||
{
|
{
|
||||||
{"x", `a foo=cc baz=aa b`},
|
{"x", `a foo=cc baz=aa b`},
|
||||||
{"bar", "abc"},
|
{"bar", "abc"},
|
||||||
|
|
Loading…
Reference in a new issue