Skip to content

Commit a825e63

Browse files
authored
Merge pull request grafana#6515 from grafana/influxdb_alias_seriename
[Influxdb] Backend support for seriename alias
2 parents 6767bdd + ecba23e commit a825e63

File tree

8 files changed

+219
-59
lines changed

8 files changed

+219
-59
lines changed

pkg/tsdb/influxdb/influxdb.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,16 @@ func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice,
5151
return result.WithError(err)
5252
}
5353

54+
rawQuery, err := e.QueryBuilder.Build(query, context)
55+
if err != nil {
56+
return result.WithError(err)
57+
}
58+
5459
if setting.Env == setting.DEV {
55-
glog.Debug("Influxdb query", "raw query", query)
60+
glog.Debug("Influxdb query", "raw query", rawQuery)
5661
}
5762

58-
req, err := e.createRequest(query)
63+
req, err := e.createRequest(rawQuery)
5964
if err != nil {
6065
return result.WithError(err)
6166
}
@@ -80,28 +85,23 @@ func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice,
8085
}
8186

8287
result.QueryResults = make(map[string]*tsdb.QueryResult)
83-
result.QueryResults["A"] = e.ResponseParser.Parse(&response)
88+
result.QueryResults["A"] = e.ResponseParser.Parse(&response, query)
8489

8590
return result
8691
}
8792

88-
func (e *InfluxDBExecutor) getQuery(queries tsdb.QuerySlice, context *tsdb.QueryContext) (string, error) {
93+
func (e *InfluxDBExecutor) getQuery(queries tsdb.QuerySlice, context *tsdb.QueryContext) (*Query, error) {
8994
for _, v := range queries {
9095

9196
query, err := e.QueryParser.Parse(v.Model, e.DataSourceInfo)
9297
if err != nil {
93-
return "", err
94-
}
95-
96-
rawQuery, err := e.QueryBuilder.Build(query, context)
97-
if err != nil {
98-
return "", err
98+
return nil, err
9999
}
100100

101-
return rawQuery, nil
101+
return query, nil
102102
}
103103

104-
return "", fmt.Errorf("query request contains no queries")
104+
return nil, fmt.Errorf("query request contains no queries")
105105
}
106106

107107
func (e *InfluxDBExecutor) createRequest(query string) (*http.Request, error) {

pkg/tsdb/influxdb/model_parser.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type InfluxdbQueryParser struct{}
1212
func (qp *InfluxdbQueryParser) Parse(model *simplejson.Json, dsInfo *tsdb.DataSourceInfo) (*Query, error) {
1313
policy := model.Get("policy").MustString("default")
1414
rawQuery := model.Get("query").MustString("")
15+
alias := model.Get("alias").MustString("")
1516

1617
measurement := model.Get("measurement").MustString("")
1718

@@ -52,6 +53,7 @@ func (qp *InfluxdbQueryParser) Parse(model *simplejson.Json, dsInfo *tsdb.DataSo
5253
Selects: selects,
5354
RawQuery: rawQuery,
5455
Interval: interval,
56+
Alias: alias,
5557
}, nil
5658
}
5759

pkg/tsdb/influxdb/model_parser_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ func TestInfluxdbQueryParser(t *testing.T) {
9090
}
9191
]
9292
],
93+
"alias": "serie alias",
9394
"tags": [
9495
{
9596
"key": "datacenter",
@@ -115,6 +116,7 @@ func TestInfluxdbQueryParser(t *testing.T) {
115116
So(len(res.Selects), ShouldEqual, 3)
116117
So(len(res.Tags), ShouldEqual, 2)
117118
So(res.Interval, ShouldEqual, ">20s")
119+
So(res.Alias, ShouldEqual, "serie alias")
118120
})
119121

120122
Convey("can part raw query json model", func() {

pkg/tsdb/influxdb/models.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ type Query struct {
88
GroupBy []*QueryPart
99
Selects []*Select
1010
RawQuery string
11+
Alias string
1112

1213
Interval string
1314
}

pkg/tsdb/influxdb/response_parser.go

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package influxdb
33
import (
44
"encoding/json"
55
"fmt"
6+
"regexp"
7+
"strconv"
68
"strings"
79

810
"github.com/grafana/grafana/pkg/tsdb"
@@ -11,17 +13,25 @@ import (
1113

1214
type ResponseParser struct{}
1315

14-
func (rp *ResponseParser) Parse(response *Response) *tsdb.QueryResult {
16+
var (
17+
legendFormat *regexp.Regexp
18+
)
19+
20+
func init() {
21+
legendFormat = regexp.MustCompile(`\[\[(\w+?)*\]\]*|\$\s*(\w+?)*`)
22+
}
23+
24+
func (rp *ResponseParser) Parse(response *Response, query *Query) *tsdb.QueryResult {
1525
queryRes := tsdb.NewQueryResult()
1626

1727
for _, result := range response.Results {
18-
queryRes.Series = append(queryRes.Series, rp.transformRows(result.Series, queryRes)...)
28+
queryRes.Series = append(queryRes.Series, rp.transformRows(result.Series, queryRes, query)...)
1929
}
2030

2131
return queryRes
2232
}
2333

24-
func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResult) tsdb.TimeSeriesSlice {
34+
func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResult, query *Query) tsdb.TimeSeriesSlice {
2535
var result tsdb.TimeSeriesSlice
2636

2737
for _, row := range rows {
@@ -38,7 +48,7 @@ func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResul
3848
}
3949
}
4050
result = append(result, &tsdb.TimeSeries{
41-
Name: rp.formatSerieName(row, column),
51+
Name: rp.formatSerieName(row, column, query),
4252
Points: points,
4353
})
4454
}
@@ -47,7 +57,48 @@ func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResul
4757
return result
4858
}
4959

50-
func (rp *ResponseParser) formatSerieName(row Row, column string) string {
60+
func (rp *ResponseParser) formatSerieName(row Row, column string, query *Query) string {
61+
if query.Alias == "" {
62+
return rp.buildSerieNameFromQuery(row, column)
63+
}
64+
65+
nameSegment := strings.Split(row.Name, ".")
66+
67+
result := legendFormat.ReplaceAllFunc([]byte(query.Alias), func(in []byte) []byte {
68+
aliasFormat := string(in)
69+
aliasFormat = strings.Replace(aliasFormat, "[[", "", 1)
70+
aliasFormat = strings.Replace(aliasFormat, "]]", "", 1)
71+
aliasFormat = strings.Replace(aliasFormat, "$", "", 1)
72+
73+
if aliasFormat == "m" || aliasFormat == "measurement" {
74+
return []byte(query.Measurement)
75+
}
76+
if aliasFormat == "col" {
77+
return []byte(column)
78+
}
79+
80+
pos, err := strconv.Atoi(aliasFormat)
81+
if err == nil && len(nameSegment) >= pos {
82+
return []byte(nameSegment[pos])
83+
}
84+
85+
if !strings.HasPrefix(aliasFormat, "tag_") {
86+
return in
87+
}
88+
89+
tagKey := strings.Replace(aliasFormat, "tag_", "", 1)
90+
tagValue, exist := row.Tags[tagKey]
91+
if exist {
92+
return []byte(tagValue)
93+
}
94+
95+
return in
96+
})
97+
98+
return string(result)
99+
}
100+
101+
func (rp *ResponseParser) buildSerieNameFromQuery(row Row, column string) string {
51102
var tags []string
52103

53104
for k, v := range row.Tags {

pkg/tsdb/influxdb/response_parser_test.go

Lines changed: 138 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,56 +4,161 @@ import (
44
"encoding/json"
55
"testing"
66

7+
"github.com/grafana/grafana/pkg/setting"
78
. "github.com/smartystreets/goconvey/convey"
89
)
910

1011
func TestInfluxdbResponseParser(t *testing.T) {
1112
Convey("Influxdb response parser", t, func() {
13+
Convey("Response parser", func() {
14+
parser := &ResponseParser{}
1215

13-
parser := &ResponseParser{}
14-
15-
response := &Response{
16-
Results: []Result{
17-
Result{
18-
Series: []Row{
19-
{
20-
Name: "cpu",
21-
Columns: []string{"time", "mean", "sum"},
22-
Tags: map[string]string{"datacenter": "America"},
23-
Values: [][]interface{}{
24-
{json.Number("111"), json.Number("222"), json.Number("333")},
25-
{json.Number("111"), json.Number("222"), json.Number("333")},
26-
{json.Number("111"), json.Number("null"), json.Number("333")},
16+
setting.NewConfigContext(&setting.CommandLineArgs{
17+
HomePath: "../../../",
18+
})
19+
20+
response := &Response{
21+
Results: []Result{
22+
Result{
23+
Series: []Row{
24+
{
25+
Name: "cpu",
26+
Columns: []string{"time", "mean", "sum"},
27+
Tags: map[string]string{"datacenter": "America"},
28+
Values: [][]interface{}{
29+
{json.Number("111"), json.Number("222"), json.Number("333")},
30+
{json.Number("111"), json.Number("222"), json.Number("333")},
31+
{json.Number("111"), json.Number("null"), json.Number("333")},
32+
},
2733
},
2834
},
2935
},
3036
},
31-
},
32-
}
37+
}
3338

34-
result := parser.Parse(response)
39+
query := &Query{}
3540

36-
Convey("can parse all series", func() {
37-
So(len(result.Series), ShouldEqual, 2)
38-
})
41+
result := parser.Parse(response, query)
3942

40-
Convey("can parse all points", func() {
41-
So(len(result.Series[0].Points), ShouldEqual, 3)
42-
So(len(result.Series[1].Points), ShouldEqual, 3)
43-
})
43+
Convey("can parse all series", func() {
44+
So(len(result.Series), ShouldEqual, 2)
45+
})
4446

45-
Convey("can parse multi row result", func() {
46-
So(result.Series[0].Points[1][0].Float64, ShouldEqual, float64(222))
47-
So(result.Series[1].Points[1][0].Float64, ShouldEqual, float64(333))
48-
})
47+
Convey("can parse all points", func() {
48+
So(len(result.Series[0].Points), ShouldEqual, 3)
49+
So(len(result.Series[1].Points), ShouldEqual, 3)
50+
})
4951

50-
Convey("can parse null points", func() {
51-
So(result.Series[0].Points[2][0].Valid, ShouldBeFalse)
52+
Convey("can parse multi row result", func() {
53+
So(result.Series[0].Points[1][0].Float64, ShouldEqual, float64(222))
54+
So(result.Series[1].Points[1][0].Float64, ShouldEqual, float64(333))
55+
})
56+
57+
Convey("can parse null points", func() {
58+
So(result.Series[0].Points[2][0].Valid, ShouldBeFalse)
59+
})
60+
61+
Convey("can format serie names", func() {
62+
So(result.Series[0].Name, ShouldEqual, "cpu.mean { datacenter: America }")
63+
So(result.Series[1].Name, ShouldEqual, "cpu.sum { datacenter: America }")
64+
})
5265
})
5366

54-
Convey("can format serie names", func() {
55-
So(result.Series[0].Name, ShouldEqual, "cpu.mean { datacenter: America }")
56-
So(result.Series[1].Name, ShouldEqual, "cpu.sum { datacenter: America }")
67+
Convey("Response parser with alias", func() {
68+
parser := &ResponseParser{}
69+
70+
response := &Response{
71+
Results: []Result{
72+
Result{
73+
Series: []Row{
74+
{
75+
Name: "cpu.upc",
76+
Columns: []string{"time", "mean", "sum"},
77+
Tags: map[string]string{"datacenter": "America"},
78+
Values: [][]interface{}{
79+
{json.Number("111"), json.Number("222"), json.Number("333")},
80+
},
81+
},
82+
},
83+
},
84+
},
85+
}
86+
87+
Convey("$ alias", func() {
88+
Convey("simple alias", func() {
89+
query := &Query{Alias: "serie alias"}
90+
result := parser.Parse(response, query)
91+
92+
So(result.Series[0].Name, ShouldEqual, "serie alias")
93+
})
94+
95+
Convey("measurement alias", func() {
96+
query := &Query{Alias: "alias $m $measurement", Measurement: "10m"}
97+
result := parser.Parse(response, query)
98+
99+
So(result.Series[0].Name, ShouldEqual, "alias 10m 10m")
100+
})
101+
102+
Convey("column alias", func() {
103+
query := &Query{Alias: "alias $col", Measurement: "10m"}
104+
result := parser.Parse(response, query)
105+
106+
So(result.Series[0].Name, ShouldEqual, "alias mean")
107+
So(result.Series[1].Name, ShouldEqual, "alias sum")
108+
})
109+
110+
Convey("tag alias", func() {
111+
query := &Query{Alias: "alias $tag_datacenter"}
112+
result := parser.Parse(response, query)
113+
114+
So(result.Series[0].Name, ShouldEqual, "alias America")
115+
})
116+
117+
Convey("segment alias", func() {
118+
query := &Query{Alias: "alias $1"}
119+
result := parser.Parse(response, query)
120+
121+
So(result.Series[0].Name, ShouldEqual, "alias upc")
122+
})
123+
124+
Convey("segment position out of bound", func() {
125+
query := &Query{Alias: "alias $5"}
126+
result := parser.Parse(response, query)
127+
128+
So(result.Series[0].Name, ShouldEqual, "alias $5")
129+
})
130+
})
131+
132+
Convey("[[]] alias", func() {
133+
Convey("simple alias", func() {
134+
query := &Query{Alias: "serie alias"}
135+
result := parser.Parse(response, query)
136+
137+
So(result.Series[0].Name, ShouldEqual, "serie alias")
138+
})
139+
140+
Convey("measurement alias", func() {
141+
query := &Query{Alias: "alias [[m]] [[measurement]]", Measurement: "10m"}
142+
result := parser.Parse(response, query)
143+
144+
So(result.Series[0].Name, ShouldEqual, "alias 10m 10m")
145+
})
146+
147+
Convey("column alias", func() {
148+
query := &Query{Alias: "alias [[col]]", Measurement: "10m"}
149+
result := parser.Parse(response, query)
150+
151+
So(result.Series[0].Name, ShouldEqual, "alias mean")
152+
So(result.Series[1].Name, ShouldEqual, "alias sum")
153+
})
154+
155+
Convey("tag alias", func() {
156+
query := &Query{Alias: "alias [[tag_datacenter]]"}
157+
result := parser.Parse(response, query)
158+
159+
So(result.Series[0].Name, ShouldEqual, "alias America")
160+
})
161+
})
57162
})
58163
})
59164
}

0 commit comments

Comments
 (0)