Skip to content

Commit

Permalink
Use selected time range for metaqueries in DE
Browse files Browse the repository at this point in the history
  • Loading branch information
chnn committed Mar 20, 2019
1 parent 2eb312e commit e062185
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 60 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
1. [12678](https://github.com/influxdata/influxdb/pull/12678): Enable the use of variables in the Data Explorer and Cell Editor Overlay
1. [12655](https://github.com/influxdata/influxdb/pull/12655): Add a variable control bar to dashboards to select values for variables.
1. [12706](https://github.com/influxdata/influxdb/pull/12706): Add ability to add variable to script from the side menu.
1. [12791](https://github.com/influxdata/influxdb/pull/12791): Use time range for metaqueries in Data Explorer and Cell Editor Overlay

### Bug Fixes

Expand Down
6 changes: 5 additions & 1 deletion ui/src/timeMachine/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import {saveAndExecuteQueries} from 'src/timeMachine/actions/queries'
// Types
import {Dispatch} from 'redux-thunk'
import {TimeMachineState} from 'src/timeMachine/reducers'
import {Action as QueryBuilderAction} from 'src/timeMachine/actions/queryBuilder'
import {
reloadTagSelectors,
Action as QueryBuilderAction,
} from 'src/timeMachine/actions/queryBuilder'
import {Action as QueryResultsAction} from 'src/timeMachine/actions/queries'
import {TimeRange, ViewType} from 'src/types/v2'
import {
Expand Down Expand Up @@ -114,6 +117,7 @@ const setTimeRangeSync = (timeRange: TimeRange): SetTimeRangeAction => ({
export const setTimeRange = (timeRange: TimeRange) => dispatch => {
dispatch(setTimeRangeSync(timeRange))
dispatch(saveAndExecuteQueries())
dispatch(reloadTagSelectors())
}

interface SetTypeAction {
Expand Down
22 changes: 22 additions & 0 deletions ui/src/timeMachine/actions/queryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type Action =
| SelectFunctionAction
| SetValuesSearchTermAction
| SetKeysSearchTermAction
| SetBuilderTagsStatusAction

interface SetBuilderBucketsStatusAction {
type: 'SET_BUILDER_BUCKETS_STATUS'
Expand Down Expand Up @@ -64,6 +65,18 @@ const setBuilderBucket = (
payload: {bucket, resetSelections},
})

interface SetBuilderTagsStatusAction {
type: 'SET_BUILDER_TAGS_STATUS'
payload: {status: RemoteDataState}
}

export const setBuilderTagsStatus = (
status: RemoteDataState
): SetBuilderTagsStatusAction => ({
type: 'SET_BUILDER_TAGS_STATUS',
payload: {status},
})

interface SetBuilderTagKeysAction {
type: 'SET_BUILDER_TAG_KEYS'
payload: {index: number; keys: string[]}
Expand Down Expand Up @@ -255,6 +268,7 @@ export const loadTagSelector = (index: number) => async (
dispatch(setBuilderTagKeysStatus(index, RemoteDataState.Loading))

try {
const timeRange = getActiveTimeMachine(getState()).timeRange
const searchTerm = getActiveTimeMachine(getState()).queryBuilder.tags[index]
.keysSearchTerm

Expand All @@ -264,6 +278,7 @@ export const loadTagSelector = (index: number) => async (
bucket: buckets[0],
tagsSelections,
searchTerm,
timeRange,
})

const {key} = tags[index]
Expand Down Expand Up @@ -309,6 +324,7 @@ const loadTagSelectorValues = (index: number) => async (
dispatch(setBuilderTagValuesStatus(index, RemoteDataState.Loading))

try {
const timeRange = getActiveTimeMachine(getState()).timeRange
const key = getActiveQuery(getState()).builderConfig.tags[index].key
const searchTerm = getActiveTimeMachine(getState()).queryBuilder.tags[index]
.valuesSearchTerm
Expand All @@ -320,6 +336,7 @@ const loadTagSelectorValues = (index: number) => async (
tagsSelections,
key,
searchTerm,
timeRange,
})

const {values: selectedValues} = tags[index]
Expand Down Expand Up @@ -407,3 +424,8 @@ export const removeTagSelector = (index: number) => async (
dispatch(removeTagSelectorSync(index))
dispatch(loadTagSelector(index))
}

export const reloadTagSelectors = () => async (dispatch: Dispatch<Action>) => {
dispatch(setBuilderTagsStatus(RemoteDataState.Loading))
dispatch(loadTagSelector(0))
}
66 changes: 47 additions & 19 deletions ui/src/timeMachine/apis/queryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ import {get} from 'lodash'
import {executeQuery, ExecuteFluxQueryResult} from 'src/shared/apis/query'
import {parseResponse} from 'src/shared/parsing/flux/response'

// Utils
import {getTimeRangeVars} from 'src/variables/utils/getTimeRangeVars'
import {formatExpression} from 'src/variables/utils/formatExpression'

// Types
import {BuilderConfig} from 'src/types/v2'
import {TimeRange} from 'src/types'
import {WrappedCancelablePromise} from 'src/types/promises'

export const SEARCH_DURATION = '30d'
export const LIMIT = 200
const DEFAULT_TIME_RANGE: TimeRange = {lower: 'now() - 30d'}
const DEFAULT_LIMIT = 200

type CancelableQuery = WrappedCancelablePromise<string[]>

Expand All @@ -22,7 +27,7 @@ export interface FindBucketsOptions {
export function findBuckets({url, orgID}: FindBucketsOptions): CancelableQuery {
const query = `buckets()
|> sort(columns: ["name"])
|> limit(n: ${LIMIT})`
|> limit(n: ${DEFAULT_LIMIT})`

const {promise, cancel} = executeQuery(url, orgID, query)

Expand All @@ -38,6 +43,8 @@ export interface FindKeysOptions {
bucket: string
tagsSelections: BuilderConfig['tags']
searchTerm?: string
timeRange?: TimeRange
limit?: number
}

export function findKeys({
Expand All @@ -46,22 +53,25 @@ export function findKeys({
bucket,
tagsSelections,
searchTerm = '',
timeRange = DEFAULT_TIME_RANGE,
limit = DEFAULT_LIMIT,
}: FindKeysOptions): CancelableQuery {
const tagFilters = formatTagFilterPredicate(tagsSelections)
const searchFilter = formatSearchFilterCall(searchTerm)
const previousKeyFilter = formatTagKeyFilterCall(tagsSelections)

const query = `import "influxdata/influxdb/v1"
v1.tagKeys(bucket: "${bucket}", predicate: ${tagFilters}, start: -${SEARCH_DURATION})${searchFilter}${previousKeyFilter}
|> filter(fn: (r) =>
r._value != "_time" and
r._value != "_start" and
r._value != "_stop" and
r._value != "_value")
const timeRangeArguments = formatTimeRangeArguments(timeRange)

// TODO: Use the `v1.tagKeys` function from the Flux standard library once
// this issue is resolved: https://github.com/influxdata/flux/issues/1071
const query = `from(bucket: "${bucket}")
|> range(${timeRangeArguments})
|> filter(fn: ${tagFilters})
|> keys()
|> keep(columns: ["_value"])${searchFilter}${previousKeyFilter}
|> filter(fn: (r) => r._value != "_time" and r._value != "_start" and r._value != "_stop" and r._value != "_value")
|> distinct()
|> sort()
|> limit(n: ${LIMIT})`
|> limit(n: ${limit})`

const {promise, cancel} = executeQuery(url, orgID, query)

Expand All @@ -77,7 +87,9 @@ export interface FindValuesOptions {
bucket: string
tagsSelections: BuilderConfig['tags']
key: string
searchTerm: string
searchTerm?: string
timeRange?: TimeRange
limit?: number
}

export function findValues({
Expand All @@ -87,14 +99,22 @@ export function findValues({
tagsSelections,
key,
searchTerm = '',
timeRange = DEFAULT_TIME_RANGE,
limit = DEFAULT_LIMIT,
}: FindValuesOptions): CancelableQuery {
const tagFilters = formatTagFilterPredicate(tagsSelections)
const searchFilter = formatSearchFilterCall(searchTerm)

const query = `import "influxdata/influxdb/v1"
v1.tagValues(bucket: "${bucket}", tag: "${key}", predicate: ${tagFilters}, start: -${SEARCH_DURATION})${searchFilter}
|> limit(n: ${LIMIT})
const timeRangeArguments = formatTimeRangeArguments(timeRange)

// TODO: Use the `v1.tagValues` function from the Flux standard library once
// this issue is resolved: https://github.com/influxdata/flux/issues/1071
const query = `from(bucket: "${bucket}")
|> range(${timeRangeArguments})
|> filter(fn: ${tagFilters})
|> group(columns: ["${key}"])
|> distinct(column: "${key}")
|> keep(columns: ["_value"])${searchFilter}
|> limit(n: ${limit})
|> sort()`

const {promise, cancel} = executeQuery(url, orgID, query)
Expand Down Expand Up @@ -172,3 +192,11 @@ export function formatSearchFilterCall(searchTerm: string) {

return `\n |> filter(fn: (r) => r._value =~ /(?i:${searchTerm})/)`
}

export function formatTimeRangeArguments(timeRange: TimeRange): string {
const [start, stop] = getTimeRangeVars(timeRange).map(assignment =>
formatExpression(assignment.init)
)

return `start: ${start}, stop: ${stop}`
}
12 changes: 10 additions & 2 deletions ui/src/timeMachine/components/TagSelector.scss
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,20 @@ button.tag-selector--remove,
pointer-events: none;
flex: 1 1 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 12px 12px 36px 12px;
color: $g8-storm;
font-weight: 500;
font-size: 16px;
text-transform: uppercase;
text-align: center;
padding: 12px 12px 36px 12px;
text-transform: uppercase;

small {
display: block;
font-size: 14px;
text-transform: none;
margin-top: 10px;
}
}
8 changes: 6 additions & 2 deletions ui/src/timeMachine/components/TagSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class TagSelector extends PureComponent<Props> {
<>
<div className="tag-selector--top">{this.removeButton}</div>
<div className="tag-selector--empty" data-testid="empty-tag-keys">
No more tag keys found
No tag keys found <small>in the current time range</small>
</div>
</>
)
Expand Down Expand Up @@ -181,7 +181,11 @@ class TagSelector extends PureComponent<Props> {
}

if (valuesStatus === RemoteDataState.Done && !values.length) {
return <div className="tag-selector--empty">Nothing found</div>
return (
<div className="tag-selector--empty">
No values found <small>in the current time range</small>
</div>
)
}

return (
Expand Down
12 changes: 12 additions & 0 deletions ui/src/timeMachine/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,18 @@ export const timeMachineReducer = (
})
}

case 'SET_BUILDER_TAGS_STATUS': {
return produce(state, draftState => {
const {status} = action.payload
const tags = draftState.queryBuilder.tags

for (const tag of tags) {
tag.keysStatus = status
tag.valuesStatus = status
}
})
}

case 'SET_BUILDER_TAG_KEYS': {
return produce(state, draftState => {
const {index, keys} = action.payload
Expand Down
36 changes: 36 additions & 0 deletions ui/src/variables/utils/formatExpression.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {Expression} from 'src/types/ast'

export const formatExpression = (expr: Expression): string => {
switch (expr.type) {
case 'DateTimeLiteral':
case 'BooleanLiteral':
case 'UnsignedIntegerLiteral':
case 'IntegerLiteral':
return String(expr.value)
case 'StringLiteral':
return `"${expr.value}"`
case 'DurationLiteral':
return expr.values.reduce(
(acc, {magnitude, unit}) => `${acc}${magnitude}${unit}`,
''
)
case 'FloatLiteral':
return String(expr.value).includes('.')
? String(expr.value)
: expr.value.toFixed(1)
case 'UnaryExpression':
return `${expr.operator}${formatExpression(expr.argument)}`
case 'BinaryExpression':
return `${formatExpression(expr.left)} ${
expr.operator
} ${formatExpression(expr.right)}`
case 'CallExpression':
// This doesn't handle formatting a call expression with arguments, or
// with any other sort of callee except an `Identifier`
return `${formatExpression(expr.callee)}()`
case 'Identifier':
return expr.name
default:
throw new Error(`cant format expression of type ${expr.type}`)
}
}
38 changes: 2 additions & 36 deletions ui/src/variables/utils/formatVarsOption.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {OPTION_NAME} from 'src/variables/constants/index'
import {VariableAssignment, Expression} from 'src/types/ast'
import {formatExpression} from 'src/variables/utils/formatExpression'
import {VariableAssignment} from 'src/types/ast'

export const formatVarsOption = (variables: VariableAssignment[]): string => {
if (!variables.length) {
Expand All @@ -14,38 +15,3 @@ export const formatVarsOption = (variables: VariableAssignment[]): string => {

return option
}

const formatExpression = (expr: Expression): string => {
switch (expr.type) {
case 'DateTimeLiteral':
case 'BooleanLiteral':
case 'UnsignedIntegerLiteral':
case 'IntegerLiteral':
return String(expr.value)
case 'StringLiteral':
return `"${expr.value}"`
case 'DurationLiteral':
return expr.values.reduce(
(acc, {magnitude, unit}) => `${acc}${magnitude}${unit}`,
''
)
case 'FloatLiteral':
return String(expr.value).includes('.')
? String(expr.value)
: expr.value.toFixed(1)
case 'UnaryExpression':
return `${expr.operator}${formatExpression(expr.argument)}`
case 'BinaryExpression':
return `${formatExpression(expr.left)} ${
expr.operator
} ${formatExpression(expr.right)}`
case 'CallExpression':
// This doesn't handle formatting a call expression with arguments, or
// with any other sort of callee except an `Identifier`
return `${formatExpression(expr.callee)}()`
case 'Identifier':
return expr.name
default:
throw new Error(`cant format expression of type ${expr.type}`)
}
}

0 comments on commit e062185

Please sign in to comment.