From b021020e039933b395c1d7b5bb3454b1cc8b9529 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Thu, 30 May 2024 14:48:36 +0200 Subject: [PATCH] wip --- docs/VictoriaLogs/CHANGELOG.md | 1 + docs/VictoriaLogs/LogsQL.md | 22 +++++++++++++--------- lib/logstorage/pipe_math.go | 20 +++++++++++++------- lib/logstorage/pipe_math_test.go | 20 +++++++++++++++----- 4 files changed, 42 insertions(+), 21 deletions(-) diff --git a/docs/VictoriaLogs/CHANGELOG.md b/docs/VictoriaLogs/CHANGELOG.md index 06b091313..12ae5675e 100644 --- a/docs/VictoriaLogs/CHANGELOG.md +++ b/docs/VictoriaLogs/CHANGELOG.md @@ -20,6 +20,7 @@ according to [these docs](https://docs.victoriametrics.com/victorialogs/quicksta ## tip * FEATURE: add `default` operator to [`math` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#math-pipe). It allows setting `NaN` result to the given default value. +* FEATURE: allow omitting result name in [`math` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#math-pipe) expresions. In this case the result name is automatically set to string representation of the corresponding math expression. For example, `_time:5m | math duration / 1000` is equivalent to `_time:5m | math (duration / 1000) as "duration / 1000"`. * FEATURE: allow omitting result name in [`stats` pipe](https://docs.victoriametrics.com/victorialogs/logsql/#stats-pipe). In this case the result name is automatically set to string representation of the corresponding [stats function expression](https://docs.victoriametrics.com/victorialogs/logsql/#stats-pipe-functions). For example, `_time:5m | count(*)` is valid [LogsQL query](https://docs.victoriametrics.com/victorialogs/logsql/) now. It is equivalent to `_time:5m | stats count(*) as "count(*)"`. * BUGFIX: properly calculate the number of matching rows in `* | field_values x | stats count() rows` and in `* | unroll (x) | stats count() rows` queries. diff --git a/docs/VictoriaLogs/LogsQL.md b/docs/VictoriaLogs/LogsQL.md index 6a0672c68..d1ae2dae4 100644 --- a/docs/VictoriaLogs/LogsQL.md +++ b/docs/VictoriaLogs/LogsQL.md @@ -1594,6 +1594,19 @@ See also: ### math pipe `| math ...` [pipe](#pipes) performs mathematical calculations over numeric values stored in [log fields](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model). +It has the following format: + +``` +| math + expr1 as resultName1, + ... + exprN as resultNameN +``` + +Where `exprX` is one of the supported math expressions mentioned below, while `resultNameX` is the name of the field to store the calculated result to. +The `as` keyword is optional. The result name can be omitted. In this case the result is stored to a field with the name equal to string represenation +of the corresponding math expression. + For example, the following query divides `duration_msecs` field value by 1000, then rounds it to integer and stores the result in the `duration_secs` field: ```logsql @@ -1621,15 +1634,6 @@ Every `argX` argument in every mathematical operation can contain one of the fol - Any [supported numeric value](#numeric-values). For example, `response_size_bytes / 1MiB`. - Another mathematical expression. Optionally, it may be put inside `(...)`. For example, `(a + b) * c`. -Multiple distinct results can be calculated in a single `math ...` pipe - just separate them with `,`. For example, the following query calculates the error rate -and the number of successful requests from `errors`, `warnings` and `requests` [log fields](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model): - -```logsql -_time:5m | math - (errors / requests) as error_rate, - (requests - (errors + warnings)) as success_requests -``` - See also: - [`stats` pipe](#stats-pipe) diff --git a/lib/logstorage/pipe_math.go b/lib/logstorage/pipe_math.go index 5790ea07f..50702b372 100644 --- a/lib/logstorage/pipe_math.go +++ b/lib/logstorage/pipe_math.go @@ -384,14 +384,20 @@ func parseMathEntry(lex *lexer) (*mathEntry, error) { return nil, err } - // skip optional 'as' - if lex.isKeyword("as") { - lex.nextToken() - } + resultField := "" + if lex.isKeyword(",", "|", ")", "") { + resultField = me.String() + } else { + if lex.isKeyword("as") { + // skip optional 'as' + lex.nextToken() + } - resultField, err := parseFieldName(lex) - if err != nil { - return nil, fmt.Errorf("cannot parse result name for [%s]: %w", me, err) + fieldName, err := parseFieldName(lex) + if err != nil { + return nil, fmt.Errorf("cannot parse result name for [%s]: %w", me, err) + } + resultField = fieldName } e := &mathEntry{ diff --git a/lib/logstorage/pipe_math_test.go b/lib/logstorage/pipe_math_test.go index af070a1a8..4b1536268 100644 --- a/lib/logstorage/pipe_math_test.go +++ b/lib/logstorage/pipe_math_test.go @@ -32,7 +32,6 @@ func TestParsePipeMathFailure(t *testing.T) { } f(`math`) - f(`math x`) f(`math x as`) f(`math abs() as x`) f(`math abs(a, b) as x`) @@ -64,7 +63,7 @@ func TestPipeMath(t *testing.T) { }, }) - f("math a / b default 10 as c", [][]Field{ + f("math a / b default c", [][]Field{ { {"a", "v1"}, {"b", "2"}, @@ -79,21 +78,32 @@ func TestPipeMath(t *testing.T) { {"a", "3"}, {"b", "2"}, }, + { + {"a", "3"}, + {"b", "foo"}, + }, }, [][]Field{ { {"a", "v1"}, {"b", "2"}, - {"c", "10"}, + {"c", "3"}, + {"a / b default c", "3"}, }, { {"a", "0"}, {"b", "0"}, - {"c", "10"}, + {"c", "3"}, + {"a / b default c", "3"}, }, { {"a", "3"}, {"b", "2"}, - {"c", "1.5"}, + {"a / b default c", "1.5"}, + }, + { + {"a", "3"}, + {"b", "foo"}, + {"a / b default c", "NaN"}, }, })