app/vmselect/promql: add label_uppercase(q, label1, ... labelN) and label_lowercase(q, label1, ... labelN) functions

Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/936
This commit is contained in:
Aliaksandr Valialkin 2020-12-03 21:10:32 +02:00
parent d0e1589ea9
commit 66379cc69f
9 changed files with 107 additions and 5 deletions

View file

@ -1090,6 +1090,62 @@ func TestExecSuccess(t *testing.T) {
resultExpected := []netstorage.Result{r1, r2, r3, r4, r5} resultExpected := []netstorage.Result{r1, r2, r3, r4, r5}
f(q, resultExpected) f(q, resultExpected)
}) })
t.Run(`label_uppercase`, func(t *testing.T) {
t.Parallel()
q := `label_uppercase(
label_set(time(), "foo", "bAr", "XXx", "yyy", "zzz", "abc"),
"foo", "XXx", "aaa"
)`
r := netstorage.Result{
MetricName: metricNameExpected,
Values: []float64{1000, 1200, 1400, 1600, 1800, 2000},
Timestamps: timestampsExpected,
}
r.MetricName.Tags = []storage.Tag{
{
Key: []byte("XXx"),
Value: []byte("YYY"),
},
{
Key: []byte("foo"),
Value: []byte("BAR"),
},
{
Key: []byte("zzz"),
Value: []byte("abc"),
},
}
resultExpected := []netstorage.Result{r}
f(q, resultExpected)
})
t.Run(`label_lowercase`, func(t *testing.T) {
t.Parallel()
q := `label_lowercase(
label_set(time(), "foo", "bAr", "XXx", "yyy", "zzz", "aBc"),
"foo", "XXx", "aaa"
)`
r := netstorage.Result{
MetricName: metricNameExpected,
Values: []float64{1000, 1200, 1400, 1600, 1800, 2000},
Timestamps: timestampsExpected,
}
r.MetricName.Tags = []storage.Tag{
{
Key: []byte("XXx"),
Value: []byte("yyy"),
},
{
Key: []byte("foo"),
Value: []byte("bar"),
},
{
Key: []byte("zzz"),
Value: []byte("aBc"),
},
}
resultExpected := []netstorage.Result{r}
f(q, resultExpected)
})
t.Run(`label_copy(new_tag)`, func(t *testing.T) { t.Run(`label_copy(new_tag)`, func(t *testing.T) {
t.Parallel() t.Parallel()
q := `label_copy( q := `label_copy(
@ -6217,6 +6273,8 @@ func TestExecError(t *testing.T) {
f(`label_transform(1, "foo", "invalid(regexp", "baz`) f(`label_transform(1, "foo", "invalid(regexp", "baz`)
f(`label_match(1, 2, 3)`) f(`label_match(1, 2, 3)`)
f(`label_mismatch(1, 2, 3)`) f(`label_mismatch(1, 2, 3)`)
f(`label_uppercase()`)
f(`label_lowercase()`)
f(`alias(1, 2)`) f(`alias(1, 2)`)
f(`aggr_over_time(1, 2)`) f(`aggr_over_time(1, 2)`)
f(`aggr_over_time(("foo", "bar"), 3)`) f(`aggr_over_time(("foo", "bar"), 3)`)

View file

@ -73,6 +73,8 @@ var transformFuncs = map[string]transformFunc{
// New funcs // New funcs
"label_set": transformLabelSet, "label_set": transformLabelSet,
"label_map": transformLabelMap, "label_map": transformLabelMap,
"label_uppercase": transformLabelUppercase,
"label_lowercase": transformLabelLowercase,
"label_del": transformLabelDel, "label_del": transformLabelDel,
"label_keep": transformLabelKeep, "label_keep": transformLabelKeep,
"label_copy": transformLabelCopy, "label_copy": transformLabelCopy,
@ -1196,6 +1198,42 @@ func transformLabelSet(tfa *transformFuncArg) ([]*timeseries, error) {
return rvs, nil return rvs, nil
} }
func transformLabelUppercase(tfa *transformFuncArg) ([]*timeseries, error) {
return transformLabelValueFunc(tfa, strings.ToUpper)
}
func transformLabelLowercase(tfa *transformFuncArg) ([]*timeseries, error) {
return transformLabelValueFunc(tfa, strings.ToLower)
}
func transformLabelValueFunc(tfa *transformFuncArg, f func(string) string) ([]*timeseries, error) {
args := tfa.args
if len(args) < 2 {
return nil, fmt.Errorf(`not enough args; got %d; want at least %d`, len(args), 2)
}
labels := make([]string, 0, len(args)-1)
for i := 1; i < len(args); i++ {
label, err := getString(args[i], i)
if err != nil {
return nil, err
}
labels = append(labels, label)
}
rvs := args[0]
for _, ts := range rvs {
mn := &ts.MetricName
for _, label := range labels {
dstValue := getDstValue(mn, label)
*dstValue = append((*dstValue)[:0], f(string(*dstValue))...)
if len(*dstValue) == 0 {
mn.RemoveTag(label)
}
}
}
return rvs, nil
}
func transformLabelMap(tfa *transformFuncArg) ([]*timeseries, error) { func transformLabelMap(tfa *transformFuncArg) ([]*timeseries, error) {
args := tfa.args args := tfa.args
if len(args) < 2 { if len(args) < 2 {

View file

@ -3,6 +3,8 @@
# tip # tip
* FEATURE: optimize Consul service discovery speed when discovering big number of services. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/574 * FEATURE: optimize Consul service discovery speed when discovering big number of services. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/574
* FEATURE: add `label_uppercase(q, label1, ... labelN)` and `label_lowercase(q, label1, ... labelN)` function to [MetricsQL](https://victoriametrics.github.io/MetricsQL.html)
for uppercasing and lowercasing values for the given labels. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/936
* BUGFIX: properly parse timestamps in OpenMetrics format - they are exposed as floating-point number in seconds instead of integer milliseconds * BUGFIX: properly parse timestamps in OpenMetrics format - they are exposed as floating-point number in seconds instead of integer milliseconds
unlike in Prometheus exposition format. See [the docs](https://github.com/OpenObservability/OpenMetrics/blob/master/specification/OpenMetrics.md#timestamps). unlike in Prometheus exposition format. See [the docs](https://github.com/OpenObservability/OpenMetrics/blob/master/specification/OpenMetrics.md#timestamps).

View file

@ -57,6 +57,8 @@ This functionality can be tried at [an editable Grafana dashboard](http://play-g
- `alias(q, name)` for setting metric name across all the time series `q`. - `alias(q, name)` for setting metric name across all the time series `q`.
- `label_set(q, label1, value1, ... labelN, valueN)` for setting the given values for the given labels on `q`. - `label_set(q, label1, value1, ... labelN, valueN)` for setting the given values for the given labels on `q`.
- `label_map(q, label, srcValue1, dstValue1, ... srcValueN, dstValueN)` for mapping `label` values from `src*` to `dst*`. - `label_map(q, label, srcValue1, dstValue1, ... srcValueN, dstValueN)` for mapping `label` values from `src*` to `dst*`.
- `label_uppercase(q, label1, ... labelN)` for uppercasing values for the given labels.
- `label_lowercase(q, label2, ... labelN)` for lowercasing value for the given labels.
- `label_del(q, label1, ... labelN)` for deleting the given labels from `q`. - `label_del(q, label1, ... labelN)` for deleting the given labels from `q`.
- `label_keep(q, label1, ... labelN)` for deleting all the labels except the given labels from `q`. - `label_keep(q, label1, ... labelN)` for deleting all the labels except the given labels from `q`.
- `label_copy(q, src_label1, dst_label1, ... src_labelN, dst_labelN)` for copying label values from `src_*` to `dst_*`. - `label_copy(q, src_label1, dst_label1, ... src_labelN, dst_labelN)` for copying label values from `src_*` to `dst_*`.

2
go.mod
View file

@ -8,7 +8,7 @@ require (
// like https://github.com/valyala/fasthttp/commit/996610f021ff45fdc98c2ce7884d5fa4e7f9199b // like https://github.com/valyala/fasthttp/commit/996610f021ff45fdc98c2ce7884d5fa4e7f9199b
github.com/VictoriaMetrics/fasthttp v1.0.9 github.com/VictoriaMetrics/fasthttp v1.0.9
github.com/VictoriaMetrics/metrics v1.12.3 github.com/VictoriaMetrics/metrics v1.12.3
github.com/VictoriaMetrics/metricsql v0.7.3 github.com/VictoriaMetrics/metricsql v0.8.0
github.com/aws/aws-sdk-go v1.36.0 github.com/aws/aws-sdk-go v1.36.0
github.com/cespare/xxhash/v2 v2.1.1 github.com/cespare/xxhash/v2 v2.1.1
github.com/golang/snappy v0.0.2 github.com/golang/snappy v0.0.2

4
go.sum
View file

@ -45,8 +45,8 @@ github.com/VictoriaMetrics/fasthttp v1.0.9/go.mod h1:3SeUL4zwB/p/a9aEeRc6gdlbrtN
github.com/VictoriaMetrics/metrics v1.12.2/go.mod h1:Z1tSfPfngDn12bTfZSCqArT3OPY3u88J12hSoOhuiRE= github.com/VictoriaMetrics/metrics v1.12.2/go.mod h1:Z1tSfPfngDn12bTfZSCqArT3OPY3u88J12hSoOhuiRE=
github.com/VictoriaMetrics/metrics v1.12.3 h1:Fe6JHC6MSEKa+BtLhPN8WIvS+HKPzMc2evEpNeCGy7I= github.com/VictoriaMetrics/metrics v1.12.3 h1:Fe6JHC6MSEKa+BtLhPN8WIvS+HKPzMc2evEpNeCGy7I=
github.com/VictoriaMetrics/metrics v1.12.3/go.mod h1:Z1tSfPfngDn12bTfZSCqArT3OPY3u88J12hSoOhuiRE= github.com/VictoriaMetrics/metrics v1.12.3/go.mod h1:Z1tSfPfngDn12bTfZSCqArT3OPY3u88J12hSoOhuiRE=
github.com/VictoriaMetrics/metricsql v0.7.3 h1:ReI+UBleCkGMmYDt69gsuGld71l1WpQDIlhgU1N5xQ8= github.com/VictoriaMetrics/metricsql v0.8.0 h1:fUZWNjXYJhiCKoEXr86pM4RaAxfA3SqkAhJLe1gr/mo=
github.com/VictoriaMetrics/metricsql v0.7.3/go.mod h1:ylO7YITho/Iw6P71oEaGyHbO94bGoGtzWfLGqFhMIg8= github.com/VictoriaMetrics/metricsql v0.8.0/go.mod h1:ylO7YITho/Iw6P71oEaGyHbO94bGoGtzWfLGqFhMIg8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=

View file

@ -110,7 +110,7 @@ func getMetricExprForOptimization(e Expr) *MetricExpr {
if IsTransformFunc(fe.Name) { if IsTransformFunc(fe.Name) {
switch strings.ToLower(fe.Name) { switch strings.ToLower(fe.Name) {
case "absent", "histogram_quantile", "label_join", "label_replace", "scalar", "vector", case "absent", "histogram_quantile", "label_join", "label_replace", "scalar", "vector",
"label_set", "label_map", "label_del", "label_keep", "label_copy", "label_set", "label_map", "label_uppercase", "label_lowercase", "label_del", "label_keep", "label_copy",
"label_move", "label_transform", "label_value", "label_match", "label_mismatch", "label_move", "label_transform", "label_value", "label_match", "label_mismatch",
"prometheus_buckets", "buckets_limit", "histogram_share", "union", "": "prometheus_buckets", "buckets_limit", "histogram_share", "union", "":
// metric expressions for these functions cannot be optimized. // metric expressions for these functions cannot be optimized.

View file

@ -39,6 +39,8 @@ var transformFuncs = map[string]bool{
// New funcs from MetricsQL // New funcs from MetricsQL
"label_set": true, "label_set": true,
"label_map": true, "label_map": true,
"label_uppercase": true,
"label_lowercase": true,
"label_del": true, "label_del": true,
"label_keep": true, "label_keep": true,
"label_copy": true, "label_copy": true,

2
vendor/modules.txt vendored
View file

@ -16,7 +16,7 @@ github.com/VictoriaMetrics/fasthttp/fasthttputil
github.com/VictoriaMetrics/fasthttp/stackless github.com/VictoriaMetrics/fasthttp/stackless
# github.com/VictoriaMetrics/metrics v1.12.3 # github.com/VictoriaMetrics/metrics v1.12.3
github.com/VictoriaMetrics/metrics github.com/VictoriaMetrics/metrics
# github.com/VictoriaMetrics/metricsql v0.7.3 # github.com/VictoriaMetrics/metricsql v0.8.0
github.com/VictoriaMetrics/metricsql github.com/VictoriaMetrics/metricsql
github.com/VictoriaMetrics/metricsql/binaryop github.com/VictoriaMetrics/metricsql/binaryop
# github.com/aws/aws-sdk-go v1.36.0 # github.com/aws/aws-sdk-go v1.36.0