From 4387d202225a297296c779e5b47cf939d9bcd5ad Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 5 Oct 2016 20:36:05 +0200 Subject: [PATCH] feat(influxdb): render select and groupby --- pkg/tsdb/influxdb/influxdb.go | 10 +++- pkg/tsdb/influxdb/query_builder.go | 61 +++++++++++++++++++++-- pkg/tsdb/influxdb/query_builder_test.go | 66 +++++++++++-------------- pkg/tsdb/influxdb/query_part.go | 10 ++-- 4 files changed, 99 insertions(+), 48 deletions(-) diff --git a/pkg/tsdb/influxdb/influxdb.go b/pkg/tsdb/influxdb/influxdb.go index b0775ebf9dd..8f45e8d2522 100644 --- a/pkg/tsdb/influxdb/influxdb.go +++ b/pkg/tsdb/influxdb/influxdb.go @@ -12,13 +12,15 @@ import ( type InfluxDBExecutor struct { *tsdb.DataSourceInfo - QueryParser *InfluxdbQueryParser + QueryParser *InfluxdbQueryParser + QueryBuilder *QueryBuild } func NewInfluxDBExecutor(dsInfo *tsdb.DataSourceInfo) tsdb.Executor { return &InfluxDBExecutor{ DataSourceInfo: dsInfo, QueryParser: &InfluxdbQueryParser{}, + QueryBuilder: &QueryBuild{}, } } @@ -46,12 +48,16 @@ func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, for _, v := range queries { query, err := e.QueryParser.Parse(v.Model) - if err != nil { result.Error = err return result } + glog.Info("Influxdb executor", "query", query) + + rawQuery, err := e.QueryBuilder.Build(query) + + glog.Info("Influxdb", "error", err, "rawQuery", rawQuery) } return result diff --git a/pkg/tsdb/influxdb/query_builder.go b/pkg/tsdb/influxdb/query_builder.go index 86d4981133e..487193584d8 100644 --- a/pkg/tsdb/influxdb/query_builder.go +++ b/pkg/tsdb/influxdb/query_builder.go @@ -1,10 +1,65 @@ package influxdb -import "fmt" +import ( + "fmt" + "strings" +) type QueryBuild struct{} -func (*QueryBuild) Build(query *Query) (string, error) { +func renderTags(query *Query) []string { + var res []string + for i, tag := range query.Tags { + str := "" - return "", fmt.Errorf("query is not valid") + if i > 0 { + if tag.Condition == "" { + str += "AND" + } else { + str += tag.Condition + } + str += " " + } + + res = append(res, fmt.Sprintf(`%s"%s" %s '%s'`, str, tag.Key, tag.Operator, tag.Value)) + } + + return res +} + +func (*QueryBuild) Build(query *Query) (string, error) { + res := "SELECT " + + var selectors []string + for _, sel := range query.Selects { + + stk := "" + for _, s := range *sel { + stk = s.Render(stk) + } + selectors = append(selectors, stk) + } + res += strings.Join(selectors, ", ") + + res += fmt.Sprintf(` FROM "%s"`, query.Measurement) + + res += " WHERE " + conditions := renderTags(query) + res += strings.Join(conditions, " ") + if len(conditions) > 0 { + res += " AND " + } + + res += "$timeFilter" + + var groupBy []string + for _, group := range query.GroupBy { + groupBy = append(groupBy, group.Render("")) + } + + if len(groupBy) > 0 { + res += " GROUP BY " + strings.Join(groupBy, " ") + } + + return res, nil } diff --git a/pkg/tsdb/influxdb/query_builder_test.go b/pkg/tsdb/influxdb/query_builder_test.go index 7ec80d389dd..0e5a25eca5f 100644 --- a/pkg/tsdb/influxdb/query_builder_test.go +++ b/pkg/tsdb/influxdb/query_builder_test.go @@ -8,50 +8,40 @@ import ( func TestInfluxdbQueryBuilder(t *testing.T) { Convey("Influxdb query builder", t, func() { + builder := QueryBuild{} - builder := &QueryBuild{} + qp1, _ := NewQueryPart("field", []string{"value"}) + qp2, _ := NewQueryPart("mean", []string{}) + + groupBy1, _ := NewQueryPart("time", []string{"$interval"}) + groupBy2, _ := NewQueryPart("fill", []string{"null"}) + + tag1 := &Tag{Key: "hostname", Value: "server1", Operator: "="} + tag2 := &Tag{Key: "hostname", Value: "server2", Operator: "=", Condition: "OR"} Convey("can build query", func() { - //query := &Query{} - //res, err := builder.Build(query) - //So(err, ShouldBeNil) + query := &Query{ + Selects: []*Select{{*qp1, *qp2}}, + Measurement: "cpu", + GroupBy: []*QueryPart{groupBy1, groupBy2}, + } + + rawQuery, err := builder.Build(query) + So(err, ShouldBeNil) + So(rawQuery, ShouldEqual, `SELECT mean("value") FROM "cpu" WHERE $timeFilter GROUP BY time($interval) fill(null)`) }) - Convey("empty queries should return error", func() { - query := &Query{} + Convey("can asd query", func() { + query := &Query{ + Selects: []*Select{{*qp1, *qp2}}, + Measurement: "cpu", + GroupBy: []*QueryPart{groupBy1}, + Tags: []*Tag{tag1, tag2}, + } - res, err := builder.Build(query) - So(err, ShouldNotBeNil) - So(res, ShouldEqual, "") + rawQuery, err := builder.Build(query) + So(err, ShouldBeNil) + So(rawQuery, ShouldEqual, `SELECT mean("value") FROM "cpu" WHERE "hostname" = 'server1' OR "hostname" = 'server2' AND $timeFilter GROUP BY time($interval)`) }) }) } - -/* - describe('render series with mesurement only', function() { - it('should generate correct query', function() { - var query = new InfluxQuery({ - measurement: 'cpu', - }, templateSrv, {}); - - var queryText = query.render(); - expect(queryText).to.be('SELECT mean("value") FROM "cpu" WHERE $timeFilter GROUP BY time($interval) fill(null)'); - }); - }); -*/ - -/* - describe('series with tags OR condition', function() { - it('should generate correct query', function() { - var query = new InfluxQuery({ - measurement: 'cpu', - groupBy: [{type: 'time', params: ['auto']}], - tags: [{key: 'hostname', value: 'server1'}, {key: 'hostname', value: 'server2', condition: "OR"}] - }, templateSrv, {}); - - var queryText = query.render(); - expect(queryText).to.be('SELECT mean("value") FROM "cpu" WHERE "hostname" = \'server1\' OR "hostname" = \'server2\' AND ' + - '$timeFilter GROUP BY time($interval)'); - }); - }); -*/ diff --git a/pkg/tsdb/influxdb/query_part.go b/pkg/tsdb/influxdb/query_part.go index ff352f5e048..32ba6ba4d4a 100644 --- a/pkg/tsdb/influxdb/query_part.go +++ b/pkg/tsdb/influxdb/query_part.go @@ -91,13 +91,13 @@ func fieldRenderer(part *QueryPart, innerExpr string) string { } func functionRenderer(part *QueryPart, innerExpr string) string { - params := strings.Join(part.Params, ", ") - - if len(part.Params) > 0 { - return fmt.Sprintf("%s(%s, %s)", part.Type, innerExpr, params) + if innerExpr != "" { + part.Params = append([]string{innerExpr}, part.Params...) } - return fmt.Sprintf("%s(%s)", part.Type, innerExpr) + params := strings.Join(part.Params, ", ") + + return fmt.Sprintf("%s(%s)", part.Type, params) } func suffixRenderer(part *QueryPart, innerExpr string) string {