diff --git a/expr/functions/transformNull/function.go b/expr/functions/transformNull/function.go index 566aa433e..678151d98 100644 --- a/expr/functions/transformNull/function.go +++ b/expr/functions/transformNull/function.go @@ -10,6 +10,7 @@ import ( "github.com/go-graphite/carbonapi/expr/helper" "github.com/go-graphite/carbonapi/expr/interfaces" + "github.com/go-graphite/carbonapi/expr/tags" "github.com/go-graphite/carbonapi/expr/types" "github.com/go-graphite/carbonapi/pkg/parser" ) @@ -88,7 +89,8 @@ func (f *transformNull) Do(ctx context.Context, e parser.Expr, from, until int64 name = "transformNull(" + a.Name + ")" } - r := a.CopyName(name) + r := a.CopyLink() + r.Name = name r.Values = make([]float64, len(a.Values)) r.Tags["transformNull"] = defvStr @@ -109,15 +111,18 @@ func (f *transformNull) Do(ctx context.Context, e parser.Expr, from, until int64 if len(arg) == 0 && defaultOnAbsent { values := []float64{defv, defv} step := until - from + name := e.ToString() + tags := tags.ExtractTags(types.ExtractName(name)) + tags["transformNull"] = defvStr results = append(results, &types.MetricData{ FetchResponse: pbv3.FetchResponse{ - Name: e.ToString(), + Name: name, StartTime: from, StopTime: from + step*int64(len(values)), StepTime: step, Values: values, }, - Tags: map[string]string{"name": e.ToString()}, + Tags: tags, }) } return results, nil diff --git a/expr/functions/transformNull/function_test.go b/expr/functions/transformNull/function_test.go index 59a92fbe5..d548b5a7f 100644 --- a/expr/functions/transformNull/function_test.go +++ b/expr/functions/transformNull/function_test.go @@ -32,7 +32,7 @@ func TestTransformNull(t *testing.T) { {"metric1", 0, 1}: {types.MakeMetricData("metric1", []float64{1, math.NaN(), math.NaN(), 3, 4, 12}, 1, now)}, }, []*types.MetricData{types.MakeMetricData("transformNull(metric1)", - []float64{1, 0, 0, 3, 4, 12}, 1, now).SetTag("transformNull", "0").SetNameTag("transformNull(metric1)")}, + []float64{1, 0, 0, 3, 4, 12}, 1, now).SetTag("transformNull", "0").SetNameTag("metric1")}, }, { `transformNull(metric1, default=5)`, @@ -40,7 +40,7 @@ func TestTransformNull(t *testing.T) { {"metric1", 0, 1}: {types.MakeMetricData("metric1", []float64{1, math.NaN(), math.NaN(), 3, 4, 12}, 1, now)}, }, []*types.MetricData{types.MakeMetricData("transformNull(metric1,5)", - []float64{1, 5, 5, 3, 4, 12}, 1, now).SetTag("transformNull", "5").SetNameTag("transformNull(metric1,5)")}, + []float64{1, 5, 5, 3, 4, 12}, 1, now).SetTag("transformNull", "5").SetNameTag("metric1")}, }, { `transformNull(metric1, default=5, referenceSeries=metric2.*)`, @@ -51,13 +51,22 @@ func TestTransformNull(t *testing.T) { types.MakeMetricData("metric2.bar", []float64{1, math.NaN(), math.NaN(), 3, 4, 12}, 1, now)}, }, []*types.MetricData{types.MakeMetricData("transformNull(metric1,5)", - []float64{1, 5, math.NaN(), 5, 4, 12}, 1, now).SetTag("transformNull", "5").SetNameTag("transformNull(metric1,5)")}, + []float64{1, 5, math.NaN(), 5, 4, 12}, 1, now).SetTag("transformNull", "5").SetNameTag("metric1")}, }, { `transformNull(metric1, default=5, defaultOnAbsent=True)`, map[parser.MetricRequest][]*types.MetricData{}, []*types.MetricData{types.MakeMetricData("transformNull(metric1, default=5, defaultOnAbsent=True)", - []float64{5, 5}, 1, 0).SetNameTag(`transformNull(metric1, default=5, defaultOnAbsent=True)`)}, + []float64{5, 5}, 1, 0).SetTag("transformNull", "5").SetNameTag(`metric1`)}, + }, + // tagged metric + { + `transformNull(seriesByTag('name=metric1'), 0)`, + map[parser.MetricRequest][]*types.MetricData{ + {"seriesByTag('name=metric1')", 0, 1}: {types.MakeMetricData("metric1;env=prod", []float64{1, math.NaN(), math.NaN(), 3, 4, 12}, 1, now)}, + }, + []*types.MetricData{types.MakeMetricData("transformNull(metric1;env=prod,0)", + []float64{1, 0, 0, 3, 4, 12}, 1, now).SetTag("transformNull", "0").SetNameTag("metric1").SetTag("env", "prod")}, }, } diff --git a/expr/types/types.go b/expr/types/types.go index 9df402930..3f5fb764c 100644 --- a/expr/types/types.go +++ b/expr/types/types.go @@ -427,12 +427,17 @@ func (r *MetricData) Copy(includeValues bool) *MetricData { } } +func CopyLink(tags map[string]string) map[string]string { + newTags := make(map[string]string) + for k, v := range tags { + newTags[k] = v + } + return newTags +} + // CopyLink returns the copy of MetricData, Values not copied and link from parent. Tags map are copied func (r *MetricData) CopyLink() *MetricData { - tags := make(map[string]string) - for k, v := range r.Tags { - tags[k] = v - } + tags := CopyLink(r.Tags) return &MetricData{ FetchResponse: pb.FetchResponse{