mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(influxdb): render select and groupby
This commit is contained in:
parent
9968fa5bc5
commit
4387d20222
@ -12,13 +12,15 @@ import (
|
|||||||
|
|
||||||
type InfluxDBExecutor struct {
|
type InfluxDBExecutor struct {
|
||||||
*tsdb.DataSourceInfo
|
*tsdb.DataSourceInfo
|
||||||
QueryParser *InfluxdbQueryParser
|
QueryParser *InfluxdbQueryParser
|
||||||
|
QueryBuilder *QueryBuild
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInfluxDBExecutor(dsInfo *tsdb.DataSourceInfo) tsdb.Executor {
|
func NewInfluxDBExecutor(dsInfo *tsdb.DataSourceInfo) tsdb.Executor {
|
||||||
return &InfluxDBExecutor{
|
return &InfluxDBExecutor{
|
||||||
DataSourceInfo: dsInfo,
|
DataSourceInfo: dsInfo,
|
||||||
QueryParser: &InfluxdbQueryParser{},
|
QueryParser: &InfluxdbQueryParser{},
|
||||||
|
QueryBuilder: &QueryBuild{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,12 +48,16 @@ func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice,
|
|||||||
for _, v := range queries {
|
for _, v := range queries {
|
||||||
|
|
||||||
query, err := e.QueryParser.Parse(v.Model)
|
query, err := e.QueryParser.Parse(v.Model)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.Error = err
|
result.Error = err
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.Info("Influxdb executor", "query", query)
|
glog.Info("Influxdb executor", "query", query)
|
||||||
|
|
||||||
|
rawQuery, err := e.QueryBuilder.Build(query)
|
||||||
|
|
||||||
|
glog.Info("Influxdb", "error", err, "rawQuery", rawQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -1,10 +1,65 @@
|
|||||||
package influxdb
|
package influxdb
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
type QueryBuild struct{}
|
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
|
||||||
}
|
}
|
||||||
|
@ -8,50 +8,40 @@ import (
|
|||||||
|
|
||||||
func TestInfluxdbQueryBuilder(t *testing.T) {
|
func TestInfluxdbQueryBuilder(t *testing.T) {
|
||||||
Convey("Influxdb query builder", t, func() {
|
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() {
|
Convey("can build query", func() {
|
||||||
//query := &Query{}
|
query := &Query{
|
||||||
//res, err := builder.Build(query)
|
Selects: []*Select{{*qp1, *qp2}},
|
||||||
//So(err, ShouldBeNil)
|
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() {
|
Convey("can asd query", func() {
|
||||||
query := &Query{}
|
query := &Query{
|
||||||
|
Selects: []*Select{{*qp1, *qp2}},
|
||||||
|
Measurement: "cpu",
|
||||||
|
GroupBy: []*QueryPart{groupBy1},
|
||||||
|
Tags: []*Tag{tag1, tag2},
|
||||||
|
}
|
||||||
|
|
||||||
res, err := builder.Build(query)
|
rawQuery, err := builder.Build(query)
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(res, ShouldEqual, "")
|
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)');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
@ -91,13 +91,13 @@ func fieldRenderer(part *QueryPart, innerExpr string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func functionRenderer(part *QueryPart, innerExpr string) string {
|
func functionRenderer(part *QueryPart, innerExpr string) string {
|
||||||
params := strings.Join(part.Params, ", ")
|
if innerExpr != "" {
|
||||||
|
part.Params = append([]string{innerExpr}, part.Params...)
|
||||||
if len(part.Params) > 0 {
|
|
||||||
return fmt.Sprintf("%s(%s, %s)", part.Type, innerExpr, 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 {
|
func suffixRenderer(part *QueryPart, innerExpr string) string {
|
||||||
|
Loading…
Reference in New Issue
Block a user