This commit is contained in:
Aliaksandr Valialkin 2024-05-27 14:22:18 +02:00
parent 9af6c63774
commit 401e79e0d8
No known key found for this signature in database
GPG key ID: 52C003EE2BCDB9EB
5 changed files with 34 additions and 13 deletions

View file

@ -19,6 +19,8 @@ according to [these docs](https://docs.victoriametrics.com/victorialogs/quicksta
## tip ## tip
* FEATURE: allow [`head` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#limit-pipe) without number. For example, `error | head`. In this case 10 last values are returned as `head` Unix command does by default.
## [v0.12.1](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v0.12.1-victorialogs) ## [v0.12.1](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v0.12.1-victorialogs)
Released at 2024-05-26 Released at 2024-05-26

View file

@ -1463,6 +1463,12 @@ _time:5m | limit 100
`head` keyword can be used instead of `limit` for convenience. For example, `_time:5m | head 100` is equivalent to `_time:5m | limit 100`. `head` keyword can be used instead of `limit` for convenience. For example, `_time:5m | head 100` is equivalent to `_time:5m | limit 100`.
The `N` in `head N` can be omitted - in this case up to 10 matching logs are returned:
```logsql
error | head
```
By default rows are selected in arbitrary order because of performance reasons, so the query above can return different sets of logs every time it is executed. By default rows are selected in arbitrary order because of performance reasons, so the query above can return different sets of logs every time it is executed.
[`sort` pipe](#sort-pipe) can be used for making sure the logs are in the same order before applying `limit ...` to them. [`sort` pipe](#sort-pipe) can be used for making sure the logs are in the same order before applying `limit ...` to them.

View file

@ -869,8 +869,10 @@ func TestParseQuerySuccess(t *testing.T) {
f(`* | DELETE foo, bar`, `* | delete foo, bar`) f(`* | DELETE foo, bar`, `* | delete foo, bar`)
// limit and head pipe // limit and head pipe
f(`foo | limit 10`, `foo | limit 10`) f(`foo | limit`, `foo | limit 10`)
f(`foo | head 10`, `foo | limit 10`) f(`foo | head`, `foo | limit 10`)
f(`foo | limit 20`, `foo | limit 20`)
f(`foo | head 20`, `foo | limit 20`)
f(`foo | HEAD 1_123_432`, `foo | limit 1123432`) f(`foo | HEAD 1_123_432`, `foo | limit 1123432`)
f(`foo | head 10K`, `foo | limit 10000`) f(`foo | head 10K`, `foo | limit 10000`)
@ -1313,10 +1315,6 @@ func TestParseQueryFailure(t *testing.T) {
f(`foo | delete foo,`) f(`foo | delete foo,`)
f(`foo | delete foo,,`) f(`foo | delete foo,,`)
// missing limit and head pipe value
f(`foo | limit`)
f(`foo | head`)
// invalid limit pipe value // invalid limit pipe value
f(`foo | limit bar`) f(`foo | limit bar`)
f(`foo | limit -123`) f(`foo | limit -123`)

View file

@ -88,15 +88,20 @@ func parsePipeLimit(lex *lexer) (*pipeLimit, error) {
if !lex.isKeyword("limit", "head") { if !lex.isKeyword("limit", "head") {
return nil, fmt.Errorf("expecting 'limit' or 'head'; got %q", lex.token) return nil, fmt.Errorf("expecting 'limit' or 'head'; got %q", lex.token)
} }
lex.nextToken()
lex.nextToken() limit := uint64(10)
n, err := parseUint(lex.token) if !lex.isKeyword("|", ")", "") {
if err != nil { n, err := parseUint(lex.token)
return nil, fmt.Errorf("cannot parse rows limit from %q: %w", lex.token, err) if err != nil {
return nil, fmt.Errorf("cannot parse rows limit from %q: %w", lex.token, err)
}
lex.nextToken()
limit = n
} }
lex.nextToken()
pl := &pipeLimit{ pl := &pipeLimit{
limit: n, limit: limit,
} }
return pl, nil return pl, nil
} }

View file

@ -20,7 +20,6 @@ func TestParsePipeLimitFailure(t *testing.T) {
expectParsePipeFailure(t, pipeStr) expectParsePipeFailure(t, pipeStr)
} }
f(`limit`)
f(`limit -10`) f(`limit -10`)
f(`limit foo`) f(`limit foo`)
} }
@ -30,6 +29,17 @@ func TestPipeLimit(t *testing.T) {
t.Helper() t.Helper()
expectPipeResults(t, pipeStr, rows, rowsExpected) expectPipeResults(t, pipeStr, rows, rowsExpected)
} }
f("limit", [][]Field{
{
{"_msg", `{"foo":"bar"}`},
{"a", `test`},
},
}, [][]Field{
{
{"_msg", `{"foo":"bar"}`},
{"a", `test`},
},
})
f("limit 100", [][]Field{ f("limit 100", [][]Field{
{ {