Merge pull request #2663 from influxdata/feature/migrate-time-interval
Change :interval: to be a duration instead of time wrapped durationpull/2886/head
commit
935186984c
|
@ -12,6 +12,10 @@ import (
|
||||||
// SortTemplates the templates by size, then type, then value.
|
// SortTemplates the templates by size, then type, then value.
|
||||||
func SortTemplates(ts []chronograf.TemplateVar) []chronograf.TemplateVar {
|
func SortTemplates(ts []chronograf.TemplateVar) []chronograf.TemplateVar {
|
||||||
sort.Slice(ts, func(i, j int) bool {
|
sort.Slice(ts, func(i, j int) bool {
|
||||||
|
if ts[i].Var == ":interval:" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if len(ts[i].Values) != len(ts[j].Values) {
|
if len(ts[i].Values) != len(ts[j].Values) {
|
||||||
return len(ts[i].Values) < len(ts[j].Values)
|
return len(ts[i].Values) < len(ts[j].Values)
|
||||||
}
|
}
|
||||||
|
@ -59,6 +63,20 @@ func RenderTemplate(query string, t chronograf.TemplateVar, now time.Time) (stri
|
||||||
tv[t.Values[i].Type] = t.Values[i].Value
|
tv[t.Values[i].Type] = t.Values[i].Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pts, ok := tv["points"]; ok {
|
||||||
|
points, err := strconv.ParseInt(pts, 0, 64)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
dur, err := ParseTime(query, now)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
interval := AutoInterval(points, dur)
|
||||||
|
return strings.Replace(query, t.Var, interval, -1), nil
|
||||||
|
}
|
||||||
|
|
||||||
if res, ok := tv["resolution"]; ok {
|
if res, ok := tv["resolution"]; ok {
|
||||||
resolution, err := strconv.ParseInt(res, 0, 64)
|
resolution, err := strconv.ParseInt(res, 0, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -83,6 +101,22 @@ func RenderTemplate(query string, t chronograf.TemplateVar, now time.Time) (stri
|
||||||
return query, nil
|
return query, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AutoInterval(points int64, duration time.Duration) string {
|
||||||
|
// The function is: ((total_seconds * millisecond_converstion) / group_by) = pixels / 3
|
||||||
|
// Number of points given the pixels
|
||||||
|
pixels := float64(points)
|
||||||
|
msPerPixel := float64(duration/time.Millisecond) / pixels
|
||||||
|
secPerPixel := float64(duration/time.Second) / pixels
|
||||||
|
if secPerPixel < 1.0 {
|
||||||
|
if msPerPixel < 1.0 {
|
||||||
|
msPerPixel = 1.0
|
||||||
|
}
|
||||||
|
return strconv.FormatInt(int64(msPerPixel), 10) + "ms"
|
||||||
|
}
|
||||||
|
// If groupby is more than 1 second round to the second
|
||||||
|
return strconv.FormatInt(int64(secPerPixel), 10) + "s"
|
||||||
|
}
|
||||||
|
|
||||||
// AutoGroupBy generates the time to group by in order to decimate the number of
|
// AutoGroupBy generates the time to group by in order to decimate the number of
|
||||||
// points returned in a query
|
// points returned in a query
|
||||||
func AutoGroupBy(resolution, pixelsPerPoint int64, duration time.Duration) string {
|
func AutoGroupBy(resolution, pixelsPerPoint int64, duration time.Duration) string {
|
||||||
|
|
|
@ -125,6 +125,38 @@ func TestTemplateReplace(t *testing.T) {
|
||||||
},
|
},
|
||||||
want: `SELECT :field: FROM "cpu"`,
|
want: `SELECT :field: FROM "cpu"`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "auto interval",
|
||||||
|
query: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(:interval:)`,
|
||||||
|
vars: []chronograf.TemplateVar{
|
||||||
|
{
|
||||||
|
Var: ":interval:",
|
||||||
|
Values: []chronograf.TemplateValue{
|
||||||
|
{
|
||||||
|
Value: "333",
|
||||||
|
Type: "points",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(46702s)`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "auto interval",
|
||||||
|
query: `SELECT derivative(mean(usage_idle),:interval:) from "cpu" where time > now() - 4320h group by time(:interval:)`,
|
||||||
|
vars: []chronograf.TemplateVar{
|
||||||
|
{
|
||||||
|
Var: ":interval:",
|
||||||
|
Values: []chronograf.TemplateValue{
|
||||||
|
{
|
||||||
|
Value: "333",
|
||||||
|
Type: "points",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: `SELECT derivative(mean(usage_idle),46702s) from "cpu" where time > now() - 4320h group by time(46702s)`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "auto group by",
|
name: "auto group by",
|
||||||
query: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by :interval:`,
|
query: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by :interval:`,
|
||||||
|
@ -133,7 +165,7 @@ func TestTemplateReplace(t *testing.T) {
|
||||||
Var: ":interval:",
|
Var: ":interval:",
|
||||||
Values: []chronograf.TemplateValue{
|
Values: []chronograf.TemplateValue{
|
||||||
{
|
{
|
||||||
Value: "1000",
|
Value: "999",
|
||||||
Type: "resolution",
|
Type: "resolution",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -143,7 +175,7 @@ func TestTemplateReplace(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(46655s)`,
|
want: `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(46702s)`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto group by without duration",
|
name: "auto group by without duration",
|
||||||
|
@ -153,7 +185,7 @@ func TestTemplateReplace(t *testing.T) {
|
||||||
Var: ":interval:",
|
Var: ":interval:",
|
||||||
Values: []chronograf.TemplateValue{
|
Values: []chronograf.TemplateValue{
|
||||||
{
|
{
|
||||||
Value: "1000",
|
Value: "999",
|
||||||
Type: "resolution",
|
Type: "resolution",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -163,7 +195,7 @@ func TestTemplateReplace(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 4320h group by time(46655s)`,
|
want: `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 4320h group by time(46702s)`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "auto group by with :dashboardTime:",
|
name: "auto group by with :dashboardTime:",
|
||||||
|
|
|
@ -32,7 +32,7 @@ import {
|
||||||
templateControlBarVisibilityToggled as templateControlBarVisibilityToggledAction,
|
templateControlBarVisibilityToggled as templateControlBarVisibilityToggledAction,
|
||||||
} from 'shared/actions/app'
|
} from 'shared/actions/app'
|
||||||
import {presentationButtonDispatcher} from 'shared/dispatchers'
|
import {presentationButtonDispatcher} from 'shared/dispatchers'
|
||||||
import {DASHBOARD_LAYOUT_ROW_HEIGHT} from 'shared/constants'
|
import {interval, DASHBOARD_LAYOUT_ROW_HEIGHT} from 'shared/constants'
|
||||||
|
|
||||||
const FORMAT_INFLUXQL = 'influxql'
|
const FORMAT_INFLUXQL = 'influxql'
|
||||||
const defaultTimeRange = {
|
const defaultTimeRange = {
|
||||||
|
@ -336,25 +336,6 @@ class DashboardPage extends Component {
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
const interval = {
|
|
||||||
id: 'interval',
|
|
||||||
type: 'autoGroupBy',
|
|
||||||
tempVar: ':interval:',
|
|
||||||
label: 'automatically determine the best group by time',
|
|
||||||
values: [
|
|
||||||
{
|
|
||||||
value: '1000', // pixels
|
|
||||||
type: 'resolution',
|
|
||||||
selected: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: '3',
|
|
||||||
type: 'pointsPerPixel',
|
|
||||||
selected: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
let templatesIncludingDashTime
|
let templatesIncludingDashTime
|
||||||
if (dashboard) {
|
if (dashboard) {
|
||||||
templatesIncludingDashTime = [
|
templatesIncludingDashTime = [
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {Table, Column, Cell} from 'fixed-data-table'
|
||||||
import Dropdown from 'shared/components/Dropdown'
|
import Dropdown from 'shared/components/Dropdown'
|
||||||
import CustomCell from 'src/data_explorer/components/CustomCell'
|
import CustomCell from 'src/data_explorer/components/CustomCell'
|
||||||
import TabItem from 'src/data_explorer/components/TableTabItem'
|
import TabItem from 'src/data_explorer/components/TableTabItem'
|
||||||
import {TEMPLATES} from 'src/data_explorer/constants'
|
import {TEMPLATES} from 'src/shared/constants'
|
||||||
|
|
||||||
import {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
|
import {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import _ from 'lodash'
|
||||||
import {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
|
import {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
|
||||||
import {resultsToCSV} from 'src/shared/parsing/resultsToCSV.js'
|
import {resultsToCSV} from 'src/shared/parsing/resultsToCSV.js'
|
||||||
import download from 'src/external/download.js'
|
import download from 'src/external/download.js'
|
||||||
import {TEMPLATES} from 'src/data_explorer/constants'
|
import {TEMPLATES} from 'src/shared/constants'
|
||||||
|
|
||||||
const getCSV = (query, errorThrown) => async () => {
|
const getCSV = (query, errorThrown) => async () => {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -81,16 +81,3 @@ export const QUERY_TEMPLATES = [
|
||||||
{text: 'Show Stats', query: 'SHOW STATS'},
|
{text: 'Show Stats', query: 'SHOW STATS'},
|
||||||
{text: 'Show Diagnostics', query: 'SHOW DIAGNOSTICS'},
|
{text: 'Show Diagnostics', query: 'SHOW DIAGNOSTICS'},
|
||||||
]
|
]
|
||||||
|
|
||||||
const interval = {
|
|
||||||
id: 'interval',
|
|
||||||
type: 'autoGroupBy',
|
|
||||||
tempVar: ':interval:',
|
|
||||||
label: 'automatically determine the best group by time',
|
|
||||||
values: [
|
|
||||||
{value: '1000', type: 'resolution', selected: true},
|
|
||||||
{value: '3', type: 'pointsPerPixel', selected: true},
|
|
||||||
],
|
|
||||||
} // pixels
|
|
||||||
|
|
||||||
export const TEMPLATES = [interval]
|
|
||||||
|
|
|
@ -6,16 +6,16 @@ import queryString from 'query-string'
|
||||||
|
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
import QueryMaker from '../components/QueryMaker'
|
import QueryMaker from 'src/data_explorer/components/QueryMaker'
|
||||||
import Visualization from '../components/Visualization'
|
import Visualization from 'src/data_explorer/components/Visualization'
|
||||||
import WriteDataForm from 'src/data_explorer/components/WriteDataForm'
|
import WriteDataForm from 'src/data_explorer/components/WriteDataForm'
|
||||||
import Header from '../containers/Header'
|
import Header from 'src/data_explorer/containers/Header'
|
||||||
import ResizeContainer from 'shared/components/ResizeContainer'
|
import ResizeContainer from 'src/shared/components/ResizeContainer'
|
||||||
import OverlayTechnologies from 'shared/components/OverlayTechnologies'
|
import OverlayTechnologies from 'src/shared/components/OverlayTechnologies'
|
||||||
import ManualRefresh from 'src/shared/components/ManualRefresh'
|
import ManualRefresh from 'src/shared/components/ManualRefresh'
|
||||||
|
|
||||||
import {VIS_VIEWS, AUTO_GROUP_BY} from 'shared/constants'
|
import {VIS_VIEWS, AUTO_GROUP_BY, TEMPLATES} from 'src/shared/constants'
|
||||||
import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS, TEMPLATES} from '../constants'
|
import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS} from 'src/data_explorer/constants'
|
||||||
import {errorThrown} from 'shared/actions/errors'
|
import {errorThrown} from 'shared/actions/errors'
|
||||||
import {setAutoRefresh} from 'shared/actions/app'
|
import {setAutoRefresh} from 'shared/actions/app'
|
||||||
import * as dataExplorerActionCreators from 'src/data_explorer/actions/view'
|
import * as dataExplorerActionCreators from 'src/data_explorer/actions/view'
|
||||||
|
|
|
@ -3,6 +3,7 @@ import _ from 'lodash'
|
||||||
|
|
||||||
import {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
|
import {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
|
||||||
import {removeUnselectedTemplateValues} from 'src/dashboards/constants'
|
import {removeUnselectedTemplateValues} from 'src/dashboards/constants'
|
||||||
|
import {intervalValuesPoints} from 'src/shared/constants'
|
||||||
|
|
||||||
const AutoRefresh = ComposedComponent => {
|
const AutoRefresh = ComposedComponent => {
|
||||||
class wrapper extends Component {
|
class wrapper extends Component {
|
||||||
|
@ -96,31 +97,38 @@ const AutoRefresh = ComposedComponent => {
|
||||||
const timeSeriesPromises = queries.map(query => {
|
const timeSeriesPromises = queries.map(query => {
|
||||||
const {host, database, rp} = query
|
const {host, database, rp} = query
|
||||||
|
|
||||||
const templatesWithResolution = templates.map(temp => {
|
const templatesWithIntervalVals = templates.map(temp => {
|
||||||
if (temp.tempVar === ':interval:') {
|
if (temp.tempVar === ':interval:') {
|
||||||
if (resolution) {
|
if (resolution) {
|
||||||
|
// resize event
|
||||||
return {
|
return {
|
||||||
...temp,
|
...temp,
|
||||||
values: temp.values.map(
|
values: temp.values.map(v => {
|
||||||
v => (temp.type === 'resolution' ? {...v, resolution} : v)
|
if (v.type === 'resolution') {
|
||||||
),
|
return {...v, value: `${resolution}`}
|
||||||
|
}
|
||||||
|
if (v.type === 'points') {
|
||||||
|
return {
|
||||||
|
...v,
|
||||||
|
value: `${_.toInteger(Number(resolution) / 3)}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...temp,
|
...temp,
|
||||||
values: [
|
values: intervalValuesPoints,
|
||||||
...temp.values,
|
|
||||||
{value: '1000', type: 'resolution', selected: true},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return temp
|
return temp
|
||||||
})
|
})
|
||||||
|
|
||||||
const tempVars = removeUnselectedTemplateValues(templatesWithResolution)
|
const tempVars = removeUnselectedTemplateValues(
|
||||||
|
templatesWithIntervalVals
|
||||||
|
)
|
||||||
return fetchTimeSeriesAsync(
|
return fetchTimeSeriesAsync(
|
||||||
{
|
{
|
||||||
source: host,
|
source: host,
|
||||||
|
|
|
@ -430,6 +430,20 @@ export const DEFAULT_SOURCE = {
|
||||||
metaUrl: '',
|
metaUrl: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const intervalValuesPoints = [
|
||||||
|
{value: '333', type: 'points', selected: true},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const interval = {
|
||||||
|
id: 'interval',
|
||||||
|
type: 'autoGroupBy',
|
||||||
|
tempVar: ':interval:',
|
||||||
|
label: 'automatically determine the best group by time',
|
||||||
|
values: intervalValuesPoints,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TEMPLATES = [interval]
|
||||||
|
|
||||||
export const IS_STATIC_LEGEND = legend =>
|
export const IS_STATIC_LEGEND = legend =>
|
||||||
_.get(legend, 'type', false) === 'static'
|
_.get(legend, 'type', false) === 'static'
|
||||||
|
|
||||||
|
|
|
@ -157,9 +157,9 @@ function _buildGroupByTime(groupBy) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return ` GROUP BY ${groupBy.time === AUTO_GROUP_BY
|
return ` GROUP BY time(${groupBy.time === AUTO_GROUP_BY
|
||||||
? TEMP_VAR_INTERVAL
|
? TEMP_VAR_INTERVAL
|
||||||
: `time(${groupBy.time})`}`
|
: `${groupBy.time}`})`
|
||||||
}
|
}
|
||||||
|
|
||||||
function _buildGroupByTags(groupBy) {
|
function _buildGroupByTags(groupBy) {
|
||||||
|
|
Loading…
Reference in New Issue