-
Notifications
You must be signed in to change notification settings - Fork 256
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add auto group by functionality using :interval: template variable #1609
Conversation
This adds support for dynamic template variables that compute something about themselves given some additional context.
In order for automatic group by to be remotely useful, we need to parse out the selected duration of time from the query itself. The problem with doing this is that using the existing machinery for parsing InfluxQL requires having valid InfluxQL, which InfluxQL+Template Variables is not. To break this chicken-and-egg problem, the duration is directly extracted from the query using regular string processing.
In order for :autoGroupBy: and :dashboardTime: to co-exist in a query, it's necessary to introduce template variable precedence to the backend. This is done by adding a `Precedence()` method to the TemplateVariable interface that returns an ordinal indicating the precedence level of the template variable. Precedence starts from 0 (highest) proceeding to the maximum that a `uint` can represent. A template variable at a given precedence level can expect that all template variables with higher precedence will have already been replaced in the query that is passed to its `Exec` call. For example, :autoGroupBy: has lower precedence than :dashboardTime: because it needs to know the selected time range for the query. When the `Exec` method of `GroupByVar` is invoked, it will see the query after :dashboardTime: has already been replaced, allowing it to extract the duration successfully.
In order to properly parse time ranges including `days` and `weeks` like `time > now() - 180d`, we need to use the `influxql.ParseDuration` function
This adds a bounds check to make sure that we don't overrun the end of the string when searching for a where clause.
There's some follow-on work to be done here to determine an appropriate value for the reporting interval, but for now this lets the client supply it.
Where clauses generated by the query builder have "WHERE" capitalized, and supply template variables with the ":" bracing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nhaugo wants :autoGroupBy:
to be renamed :interval:
. Other than that, only minor comments.
We'll wait to merge this until it's gone through a round of testing.
if (resolution) { | ||
return {...temp, resolution} | ||
} | ||
return {...temp, resolution: 1000} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of if (resolution) {
, let's set a default value on line 108:
const {resolution = 1000} = this.state
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though I just noticed DashboardPage.js
L266 -- can't we assume that it will exist?
chronograf.go
Outdated
Range *Range `json:"range,omitempty"` // Range is the default Y-Axis range for the data | ||
} | ||
|
||
// TemplateVars are a hetergenous collection of different TemplateVariables |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
heterogenous?
chronograf.go
Outdated
TemplateVars TemplateVars `json:"tempVars,omitempty"` // TemplateVars are template variables to replace within an InfluxQL query | ||
Wheres []string `json:"wheres,omitempty"` // Wheres restricts the query to certain attributes | ||
GroupBys []string `json:"groupbys,omitempty"` // GroupBys collate the query by these tags | ||
Resolution uint `json:"resolution,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should mention in a comment that this is basically maximum resolution for the chart
chronograf.go
Outdated
@@ -149,17 +174,80 @@ func (t TemplateVar) String() string { | |||
return `'` + t.Values[0].Value + `'` | |||
case "csv", "constant": | |||
return t.Values[0].Value | |||
case "autoGroupBy": | |||
return "group by time(1555s)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this anymore now that GroupByVar is its own type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, that was mocked out before I had GroupByVar
. Good catch.
This adds an "auto" option to the group by dropdown which interpolates "GROUP BY :autoGroupBy:" to the query.
This was present for testing in development and is no longer needed.
This naming is more consistent with user's expectations from other similar visualization tools. Also, the usage of the variable now requires the words "GROUP BY" to be present. e.g. `GROUP BY :interval:`
This perpetuates the hacks that we added for :dashboardTime: so that they will also work for :interval:. We should really find a better way to do this.
cbbdd86
to
6a91690
Compare
The Data Explorer doesn't work with the :interval: template variable, so it makes no sense to have an 'auto' option in the group by time dropdown.
This makes sure that a group by time is applied whenever a function is applied to a field within the query builder.
For the Data Explorer, the appropriate default group by interval is 10s, where in the CEO it should be :interval:, since template variables are supported there.
022a2da
to
44723cf
Compare
Improve spellingz and document what "resolution" actually means in the context of a query
We've changed The Query Builder is now able to appropriately apply the correct default group by time to both the Data Explorer (which does not support |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you also add tests for the changes to query.go's Convert function?
Also, would you update server/swagger.json?
// TemplateValue is a value use to replace a template in an InfluxQL query | ||
type TemplateValue struct { | ||
type BasicTemplateValue struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@timraymond Is it possible to move these interface implementations into the influx package?
} | ||
|
||
// Exec is responsible for extracting the Duration from the query | ||
func (g *GroupByVar) Exec(query string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you move the functions into the influx package? That way you can change them to functions.
TemplateVars TemplateVars `json:"tempVars,omitempty"` // TemplateVars are template variables to replace within an InfluxQL query | ||
Wheres []string `json:"wheres,omitempty"` // Wheres restricts the query to certain attributes | ||
GroupBys []string `json:"groupbys,omitempty"` // GroupBys collate the query by these tags | ||
Resolution uint `json:"resolution,omitempty"` // Resolution is the available screen resolution to render query results |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you specify the units of the resolution in the comments? Probably width in pixels?
// TemplateVars are a heterogeneous collection of different TemplateVariables | ||
// with the capability to decode arbitrary JSON into the appropriate template | ||
// variable type | ||
type TemplateVars []TemplateVariable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you place TemplateVars
into the server package? Only the server packages "cares" about unmarshaling.
// variable type | ||
type TemplateVars []TemplateVariable | ||
|
||
func (t *TemplateVars) UnmarshalJSON(text []byte) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you describe why there is a custom unmarshal function? Perhaps a comment. This must be some custom format, and, I'd like to understand the reasoning.
@@ -11,6 +11,12 @@ import ( | |||
// Convert changes an InfluxQL query to a QueryConfig | |||
func Convert(influxQL string) (chronograf.QueryConfig, error) { | |||
itsDashboardTime := false | |||
intervalTime := false | |||
if strings.Contains(influxQL, ":interval:") { | |||
influxQL = strings.Replace(influxQL, ":interval:", "time(1234s)", 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this left over from a test?
@@ -25,6 +31,10 @@ func Convert(influxQL string) (chronograf.QueryConfig, error) { | |||
influxQL = strings.Replace(influxQL, "now() - 15m", ":dashboardTime:", 1) | |||
} | |||
|
|||
if intervalTime { | |||
influxQL = strings.Replace(influxQL, "time(1234s)", ":interval:", 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this extra as well?
@@ -104,7 +114,11 @@ func Convert(influxQL string) (chronograf.QueryConfig, error) { | |||
if !ok { | |||
return raw, nil | |||
} | |||
qc.GroupBy.Time = lit.String() | |||
if intervalTime { | |||
qc.GroupBy.Time = "auto" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why auto rather than ":interval:"?
} | ||
|
||
replaced := query | ||
for prc := uint(0); prc <= maxPrecedence; prc++ { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This algorithm is pretty specific. Would you comment it? Mostly, I'm interested in you commenting why you are checking for an empty newVal, and why you are handling ExecutableVar differently.
TemplateVars []chronograf.TemplateVar `json:"tempVars,omitempty"` | ||
ID string `json:"id"` | ||
Query string `json:"query"` | ||
TemplateVars chronograf.TemplateVars `json:"tempVars,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be your unmarshal-able TemplateVars type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worried about the parsing for autoGroupby mentioned in your comments. That limits the influxql parsing significantly.
What other options did you think about for this? Could requiring a default value work?
I'm merging this so I can test on the HEAD of master. Does not invalidate Chris' latest review: @goller @timraymond |
Changelog updated in master retroactively. |
Connect #1471
The problem
It's far too easy to select more data than your monitor can actually display.
The Solution
This introduces a new template variable,
:autoGroupBy:
, which calculates a group by time based off of the available resolution for rendering a graph (per @goller 's math in #829). This is supplied by Dygraphs along with the query.This introduces another "type" of template variable, a
GroupByVar
, which handles this calculation and is instantiated by a customUnmarshalJSON
, capable of determining Template Variable types. Also, Template Variables now have varying precedence, asGroupByVar
must be applied after all template variables so that it can see the selected time range in the query.Caveats / Known Grossness
:autoGroupBy:
need to parse the query before they can do their job. Unfortunately, the InfluxQL parsing functions need a legal query to parse, which InfluxQL + Template Variables is not. We ran into this circular dependency with:dashboardTime:
as well. To break this dependency, I instead manually parsed out the duration, which limits the types ofwhere
expressions that can be understood by:autoGroupBy:
. Ideally, template variables and template macros (which:autoGroupBy:
and:dashboardTime:
really are) would be supported directly by InfluxQL.