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")
```
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:
- [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"*)
```
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:
- [Exact filter](#exact-filter)

View file

@ -11,7 +11,7 @@ import (
// filterExact matches the exact value.
//
// Example LogsQL: `fieldName:exact("foo bar")`
// Example LogsQL: `fieldName:exact("foo bar")` of `fieldName:="foo bar"
type filterExact struct {
fieldName string
value string
@ -21,7 +21,7 @@ type filterExact struct {
}
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) {

View file

@ -20,7 +20,7 @@ type filterExactPrefix struct {
}
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) {

View file

@ -595,6 +595,8 @@ func parseGenericFilter(lex *lexer, fieldName string) (filter, error) {
return parseFilterGT(lex, fieldName)
case lex.isKeyword("<"):
return parseFilterLT(lex, fieldName)
case lex.isKeyword("="):
return parseFilterEQ(lex, fieldName)
case lex.isKeyword("not", "!"):
return parseFilterNot(lex, fieldName)
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) {
lex.nextToken()
@ -1148,7 +1168,7 @@ func parseFilterRange(lex *lexer, fieldName string) (filter, error) {
func parseFloat64(lex *lexer) (float64, string, error) {
s, err := getCompoundToken(lex)
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)
if err == nil {
@ -1161,7 +1181,7 @@ func parseFloat64(lex *lexer) (float64, string, error) {
if err == 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) {

View file

@ -689,12 +689,17 @@ func TestParseQuerySuccess(t *testing.T) {
f("string_range-a:x", `string_range-a:x`)
// exact filter
f("exact(foo)", `exact(foo)`)
f("exact(foo*)", `exact(foo*)`)
f("exact('foo bar),|baz')", `exact("foo bar),|baz")`)
f("exact('foo bar),|baz'*)", `exact("foo bar),|baz"*)`)
f(`exact(foo/b:ar)`, `exact("foo/b:ar")`)
f(`foo:exact(foo/b:ar*)`, `foo:exact("foo/b:ar"*)`)
f("exact(foo)", `=foo`)
f("exact(foo*)", `=foo*`)
f("exact('foo bar),|baz')", `="foo bar),|baz"`)
f("exact('foo bar),|baz'*)", `="foo bar),|baz"*`)
f(`exact(foo/b:ar)`, `="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
f("i(foo)", `i(foo)`)

View file

@ -114,7 +114,7 @@ func TestPipeExtract(t *testing.T) {
})
// 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`},
{"bar", "abc"},
@ -128,7 +128,7 @@ func TestPipeExtract(t *testing.T) {
})
// 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`},
{"bar", "abc"},