This commit is contained in:
Aliaksandr Valialkin 2024-05-22 19:14:24 +02:00
parent 1e2f5e7294
commit b60cbd5c54
No known key found for this signature in database
GPG key ID: 52C003EE2BCDB9EB
6 changed files with 50 additions and 13 deletions

View file

@ -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)

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {

View file

@ -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)`)

View file

@ -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"},