feat(influxdb): add querypart renderers

This commit is contained in:
bergquist 2016-10-05 14:42:06 +02:00
parent d0e6a9559e
commit c7abd3ba4e
4 changed files with 242 additions and 115 deletions

View File

@ -15,11 +15,6 @@ type Tag struct {
Value string
}
type QueryPart struct {
Type string
Params []string
}
type Select []QueryPart
type InfluxDbSelect struct {

View File

@ -14,70 +14,84 @@ func TestInfluxdbQueryParser(t *testing.T) {
Convey("converting metric name", func() {
json := `
{
"dsType": "influxdb",
"groupBy": [
{
"params": [
"$interval"
],
"type": "time"
},
{
"type": "tag",
"params": [
"datacenter"
]
},
{
"params": [
"null"
],
"type": "fill"
}
],
"measurement": "logins.count",
"policy": "default",
"refId": "B",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
{
"dsType": "influxdb",
"groupBy": [
{
"params": [
"$interval"
],
"type": "time"
},
{
"params": [
"datacenter"
],
"type": "tag"
},
{
"params": [
"null"
],
"type": "fill"
}
],
"type": "field"
},
{
"params": [
"measurement": "logins.count",
"policy": "default",
"refId": "B",
"resultFormat": "time_series",
"select": [
[
{
"type": "field",
"params": [
"value"
]
},
{
"type": "count",
"params": []
}
],
[
{
"type": "field",
"params": [
"value"
]
},
{
"type": "mean",
"params": []
}
],
[
{
"type": "field",
"params": [
"value"
]
},
{
"type": "mean",
"params": []
},
{
"type": "math",
"params": [
" / 100"
]
}
]
],
"type": "count"
"tags": [
{
"key": "datacenter",
"operator": "=",
"value": "America"
}
]
}
],
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [
],
"type": "mean"
}
]
],
"tags": [
{
"key": "datacenter",
"operator": "=",
"value": "America"
}
]
}
`
modelJson, err := simplejson.NewJson([]byte(json))
@ -86,7 +100,7 @@ func TestInfluxdbQueryParser(t *testing.T) {
res, err := parser.Parse(modelJson)
So(err, ShouldBeNil)
So(len(res.GroupBy), ShouldEqual, 3)
So(len(res.Selects), ShouldEqual, 2)
So(len(res.Selects), ShouldEqual, 3)
So(len(res.Tags), ShouldEqual, 1)
})
})

View File

@ -1,5 +1,124 @@
package influxdb
type Selector interface {
Render(input string) string
import (
"fmt"
"strings"
)
var renders map[string]QueryDefinition
type QueryDefinition struct {
Renderer func(part *QueryPart, innerExpr string) string
}
func init() {
renders = make(map[string]QueryDefinition)
renders["field"] = QueryDefinition{Renderer: fieldRenderer}
renders["spread"] = QueryDefinition{Renderer: functionRenderer}
renders["count"] = QueryDefinition{Renderer: functionRenderer}
renders["distinct"] = QueryDefinition{Renderer: functionRenderer}
renders["integral"] = QueryDefinition{Renderer: functionRenderer}
renders["mean"] = QueryDefinition{Renderer: functionRenderer}
renders["median"] = QueryDefinition{Renderer: functionRenderer}
renders["sum"] = QueryDefinition{Renderer: functionRenderer}
renders["derivative"] = QueryDefinition{
Renderer: functionRenderer,
//params: [{ name: "duration", type: "interval", options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h']}],
}
renders["non_negative_derivative"] = QueryDefinition{
Renderer: functionRenderer,
//params: [{ name: "duration", type: "interval", options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h']}],
}
renders["difference"] = QueryDefinition{Renderer: functionRenderer}
renders["moving_average"] = QueryDefinition{
Renderer: functionRenderer,
//params: [{ name: "window", type: "number", options: [5, 10, 20, 30, 40]}]
}
renders["stddev"] = QueryDefinition{Renderer: functionRenderer}
renders["time"] = QueryDefinition{
Renderer: functionRenderer,
//params: [{ name: "interval", type: "time", options: ['auto', '1s', '10s', '1m', '5m', '10m', '15m', '1h'] }],
}
renders["fill"] = QueryDefinition{
Renderer: functionRenderer,
//params: [{ name: "fill", type: "string", options: ['none', 'null', '0', 'previous'] }],
}
renders["elapsed"] = QueryDefinition{
Renderer: functionRenderer,
//params: [{ name: "duration", type: "interval", options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h']}],
}
renders["bottom"] = QueryDefinition{
Renderer: functionRenderer,
//params: [{name: 'count', type: 'int'}],
}
renders["first"] = QueryDefinition{Renderer: functionRenderer}
renders["last"] = QueryDefinition{Renderer: functionRenderer}
renders["max"] = QueryDefinition{Renderer: functionRenderer}
renders["min"] = QueryDefinition{Renderer: functionRenderer}
renders["percentile"] = QueryDefinition{
Renderer: functionRenderer,
//params: [{name: 'nth', type: 'int'}],
}
renders["top"] = QueryDefinition{
Renderer: functionRenderer,
//params: [{name: 'count', type: 'int'}],
}
renders["tag"] = QueryDefinition{
Renderer: fieldRenderer,
//params: [{name: 'tag', type: 'string', dynamicLookup: true}],
}
renders["math"] = QueryDefinition{Renderer: suffixRenderer}
renders["alias"] = QueryDefinition{Renderer: aliasRenderer}
}
func fieldRenderer(part *QueryPart, innerExpr string) string {
if part.Params[0] == "*" {
return "*"
}
return fmt.Sprintf(`"%v"`, part.Params[0])
}
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)
}
return fmt.Sprintf("%s(%s)", part.Type, innerExpr)
}
func suffixRenderer(part *QueryPart, innerExpr string) string {
return fmt.Sprintf("%s %s", innerExpr, part.Params[0])
}
func aliasRenderer(part *QueryPart, innerExpr string) string {
return fmt.Sprintf(`%s AS "%s"`, innerExpr, part.Params[0])
}
func (r QueryDefinition) Render(part *QueryPart, innerExpr string) string {
return r.Renderer(part, innerExpr)
}
type QueryPartDefinition struct {
}
type QueryPart struct {
Type string
Params []string
}
func (qp *QueryPart) Render(expr string) (string, error) {
renderFn, exist := renders[qp.Type]
if !exist {
return "", fmt.Errorf("could not find render strategy %s", qp.Type)
}
return renderFn.Renderer(qp, expr), nil
}

View File

@ -9,55 +9,54 @@ import (
func TestInfluxdbQueryPart(t *testing.T) {
Convey("Influxdb query part builder", t, func() {
Convey("can build query", func() {
Convey("should handle field renderer parts", func() {
part := QueryPart{
Type: "field",
Params: []string{"value"},
}
res, _ := part.Render("value")
So(res, ShouldEqual, `"value"`)
})
Convey("empty queries should return error", func() {
Convey("should handle nested function parts", func() {
part := QueryPart{
Type: "derivative",
Params: []string{"10s"},
}
res, _ := part.Render("mean(value)")
So(res, ShouldEqual, "derivative(mean(value), 10s)")
})
Convey("should nest spread function", func() {
part := QueryPart{
Type: "spread",
}
res, err := part.Render("value")
So(err, ShouldBeNil)
So(res, ShouldEqual, "spread(value)")
})
Convey("should handle suffix parts", func() {
part := QueryPart{
Type: "math",
Params: []string{"/ 100"},
}
res, _ := part.Render("mean(value)")
So(res, ShouldEqual, "mean(value) / 100")
})
Convey("should handle alias parts", func() {
part := QueryPart{
Type: "alias",
Params: []string{"test"},
}
res, _ := part.Render("mean(value)")
So(res, ShouldEqual, `mean(value) AS "test"`)
})
})
}
/*
describe('series with mesurement only', () => {
it('should handle nested function parts', () => {
var part = queryPart.create({
type: 'derivative',
params: ['10s'],
});
expect(part.text).to.be('derivative(10s)');
expect(part.render('mean(value)')).to.be('derivative(mean(value), 10s)');
});
it('should nest spread function', () => {
var part = queryPart.create({
type: 'spread'
});
expect(part.text).to.be('spread()');
expect(part.render('value')).to.be('spread(value)');
});
it('should handle suffirx parts', () => {
var part = queryPart.create({
type: 'math',
params: ['/ 100'],
});
expect(part.text).to.be('math(/ 100)');
expect(part.render('mean(value)')).to.be('mean(value) / 100');
});
it('should handle alias parts', () => {
var part = queryPart.create({
type: 'alias',
params: ['test'],
});
expect(part.text).to.be('alias(test)');
expect(part.render('mean(value)')).to.be('mean(value) AS "test"');
});
});
*/