Allow tags to be filtered with regex

pull/10616/head
Brandon Farmer 2018-06-06 15:10:58 -07:00
parent f8e7cfa92e
commit bae5b110a4
6 changed files with 60 additions and 80 deletions

View File

@ -51,7 +51,7 @@ export enum ActionTypes {
SetSearchTerm = 'LOGS_SET_SEARCH_TERM',
AddFilter = 'LOGS_ADD_FILTER',
RemoveFilter = 'LOGS_REMOVE_FILTER',
SetFilterOperator = 'LOGS_SET_FILTER_OPERATOR',
ChangeFilter = 'LOGS_CHANGE_FILTER',
}
export interface AddFilterAction {
@ -61,11 +61,12 @@ export interface AddFilterAction {
}
}
export interface SetFilterOperatorAction {
type: ActionTypes.SetFilterOperator
export interface ChangeFilterAction {
type: ActionTypes.ChangeFilter
payload: {
id: string
operator: string
value: string
}
}
@ -159,7 +160,7 @@ export type Action =
| SetSearchTerm
| AddFilterAction
| RemoveFilterAction
| SetFilterOperatorAction
| ChangeFilterAction
const getTimeRange = (state: State): TimeRange | null =>
getDeep<TimeRange | null>(state, 'logs.timeRange', null)
@ -182,9 +183,9 @@ const getSearchTerm = (state: State): string | null =>
const getFilters = (state: State): Filter[] =>
getDeep<Filter[]>(state, 'logs.filters', [])
export const setFilterOperator = (id: string, operator: string) => ({
type: ActionTypes.SetFilterOperator,
payload: {id, operator},
export const changeFilter = (id: string, operator: string, value: string) => ({
type: ActionTypes.ChangeFilter,
payload: {id, operator, value},
})
export const setSource = (source: Source): SetSourceAction => ({

View File

@ -7,12 +7,13 @@ import {ClickOutside} from 'src/shared/components/ClickOutside'
interface Props {
filter: Filter
onDelete: (id: string) => void
onChangeOperator: (id: string, newOperator: string) => void
onChangeValue: (id: string, newValue: string) => void
onChangeFilter: (id: string, newOperator: string, newValue: string) => void
}
interface State {
editing: boolean
value: string
operator: string
}
class LogsFilter extends PureComponent<Props, State> {
@ -21,14 +22,12 @@ class LogsFilter extends PureComponent<Props, State> {
this.state = {
editing: false,
value: this.props.filter.value,
operator: this.props.filter.operator,
}
}
public render() {
const {
filter: {id},
onDelete,
} = this.props
const {editing} = this.state
return (
@ -45,7 +44,7 @@ class LogsFilter extends PureComponent<Props, State> {
}
private handleClickOutside = (): void => {
this.setState({editing: false})
this.stopEditing()
}
private handleStartEdit = (): void => {
@ -76,8 +75,9 @@ class LogsFilter extends PureComponent<Props, State> {
}
private get renderEditor(): JSX.Element {
const {operator, value} = this.state
const {
filter: {key, operator, value},
filter: {key},
} = this.props
return (
@ -106,56 +106,36 @@ class LogsFilter extends PureComponent<Props, State> {
}
private handleOperatorInput = (e: ChangeEvent<HTMLInputElement>): void => {
const {
filter: {id},
onChangeOperator,
} = this.props
const operator = getDeep(e, 'target.value', '').trim()
const cleanValue = this.enforceOperatorChars(e.target.value)
onChangeOperator(id, cleanValue)
this.setState({operator})
}
private handleValueInput = (e: ChangeEvent<HTMLInputElement>): void => {
const {
filter: {id},
onChangeValue,
} = this.props
onChangeValue(id, e.target.value)
}
private enforceOperatorChars = text => {
return text
.split('')
.filter(t => ['!', '~', `=`].includes(t))
.join('')
const value = getDeep(e, 'target.value', '').trim()
this.setState({value})
}
private handleKeyDown = (e: KeyboardEvent<HTMLInputElement>): void => {
if (e.key === 'Enter') {
e.preventDefault()
this.setState({editing: false})
this.stopEditing()
}
}
private handleToggleOperator = () => {
private stopEditing(): void {
const id = getDeep(this.props, 'filter.id', '')
const {operator, value} = this.state
let nextOperator = '=='
if (this.operator === '==') {
nextOperator = '!='
let state = {}
if (['!=', '==', '=~'].includes(operator) && value !== '') {
this.props.onChangeFilter(id, operator, value)
} else {
const {filter} = this.props
state = {operator: filter.operator, value: filter.value}
}
this.props.onChangeOperator(id, nextOperator)
}
private get toggleOperatorText(): string {
return this.operator === '==' ? '!=' : '=='
}
private get operator(): string {
return getDeep(this.props, 'filter.operator', '')
this.setState({...state, editing: false})
}
}

View File

@ -6,7 +6,7 @@ interface Props {
numResults: number
filters: Filter[]
onDelete: (id: string) => void
onFilterOperatorChange: (id: string, operator: string) => void
onFilterChange: (id: string, operator: string, value: string) => void
}
class LogsFilters extends PureComponent<Props> {
@ -31,22 +31,10 @@ class LogsFilters extends PureComponent<Props> {
key={filter.id}
filter={filter}
onDelete={this.props.onDelete}
onChangeOperator={this.props.onFilterOperatorChange}
onChangeValue={this.handleChangeFilterValue}
onChangeFilter={this.props.onFilterChange}
/>
))
}
private handleChangeFilterValue = (id: string, value: string): void => {
// const {filters, onUpdateFilters} = this.props
// const filteredFilters = filters.map(filter => {
// if (filter.id === id) {
// return {...filter, value}
// }
// return filter
// })
// onUpdateFilters(filteredFilters)
}
}
export default LogsFilters

View File

@ -10,7 +10,7 @@ import {
setSearchTermAsync,
addFilter,
removeFilter,
setFilterOperator,
changeFilter,
} from 'src/logs/actions'
import {getSourcesAsync} from 'src/shared/actions/sources'
import LogViewerHeader from 'src/logs/components/LogViewerHeader'
@ -38,7 +38,7 @@ interface Props {
setSearchTermAsync: (searchTerm: string) => void
addFilter: (filter: Filter) => void
removeFilter: (id: string) => void
setFilterOperator: (id: string, operator: string) => void
changeFilter: (id: string, operator: string, value: string) => void
timeRange: TimeRange
histogramData: object[]
tableData: {
@ -105,7 +105,7 @@ class LogsPage extends PureComponent<Props, State> {
numResults={count}
filters={filters || []}
onDelete={this.handleFilterDelete}
onFilterOperatorChange={this.handleFilterOperatorChange}
onFilterChange={this.handleFilterChange}
/>
<LogsTable
data={this.props.tableData}
@ -147,7 +147,6 @@ class LogsPage extends PureComponent<Props, State> {
id: uuid.v4(),
key: selection.key,
value: selection.tag,
enabled: true,
operator: '==',
})
this.props.executeQueriesAsync()
@ -215,8 +214,12 @@ class LogsPage extends PureComponent<Props, State> {
this.props.executeQueriesAsync()
}
private handleFilterOperatorChange = (id: string, operator: string) => {
this.props.setFilterOperator(id, operator)
private handleFilterChange = (
id: string,
operator: string,
value: string
) => {
this.props.changeFilter(id, operator, value)
this.props.executeQueriesAsync()
}
@ -274,7 +277,7 @@ const mapDispatchToProps = {
setSearchTermAsync,
addFilter,
removeFilter,
setFilterOperator,
changeFilter,
}
export default connect(mapStateToProps, mapDispatchToProps)(LogsPage)

View File

@ -4,7 +4,7 @@ import {
Action,
RemoveFilterAction,
AddFilterAction,
SetFilterOperatorAction,
ChangeFilterAction,
} from 'src/logs/actions'
import {LogsState} from 'src/types/logs'
@ -40,15 +40,15 @@ const addFilter = (state: LogsState, action: AddFilterAction): LogsState => {
return {...state, filters: [..._.get(state, 'filters', []), filter]}
}
const setFilterOperator = (
const changeFilter = (
state: LogsState,
action: SetFilterOperatorAction
action: ChangeFilterAction
): LogsState => {
const {id, operator} = action.payload
const {id, operator, value} = action.payload
const mappedFilters = _.map(_.get(state, 'filters', []), f => {
if (f.id === id) {
return {...f, operator}
return {...f, operator, value}
}
return f
})
@ -84,8 +84,8 @@ export default (state: LogsState = defaultState, action: Action) => {
return addFilter(state, action)
case ActionTypes.RemoveFilter:
return removeFilter(state, action)
case ActionTypes.SetFilterOperator:
return setFilterOperator(state, action)
case ActionTypes.ChangeFilter:
return changeFilter(state, action)
default:
return state
}

View File

@ -99,13 +99,21 @@ const operatorMapping = (operator: string): string => {
}
}
const valueMapping = (operator: string, value): string => {
if (operator === '=~') {
return `${new RegExp(value)}`
} else {
return `'${value}'`
}
}
export const filtersClause = (filters: Filter[]): string => {
return _.map(
filters,
(filter: Filter) =>
`"${keyMapping(filter.key)}" ${operatorMapping(filter.operator)} '${
filter.value
}'`
`"${keyMapping(filter.key)}" ${operatorMapping(
filter.operator
)} ${valueMapping(filter.operator, filter.value)}`
).join(' AND ')
}