diff --git a/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filter_popover_content.tsx b/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filter_popover_content.tsx index ab52d56841612e..37d96a51d66d25 100644 --- a/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filter_popover_content.tsx +++ b/src/legacy/core_plugins/data/public/filter/apply_filters/apply_filter_popover_content.tsx @@ -32,8 +32,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component } from 'react'; import { IndexPattern } from '../../index_patterns'; import { FilterLabel } from '../filter_bar/filter_editor/lib/filter_label'; -import { mapAndFlattenFilters, esFilters } from '../../../../../../plugins/data/public'; -import { getDisplayValueFromFilter } from '../filter_bar/filter_editor/lib/get_display_value'; +import { mapAndFlattenFilters, esFilters, utils } from '../../../../../../plugins/data/public'; interface Props { filters: esFilters.Filter[]; @@ -58,7 +57,7 @@ export class ApplyFiltersPopoverContent extends Component { }; } private getLabel(filter: esFilters.Filter) { - const valueLabel = getDisplayValueFromFilter(filter, this.props.indexPatterns); + const valueLabel = utils.getDisplayValueFromFilter(filter, this.props.indexPatterns); return ; } diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx index 23c9c9ffc94bbe..e80bffb5e3c68e 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx @@ -21,18 +21,18 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiPopover } from '@elastic/ import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import classNames from 'classnames'; import React, { useState } from 'react'; -import { IndexPattern } from '../../index_patterns'; + import { FilterEditor } from './filter_editor'; import { FilterItem } from './filter_item'; import { FilterOptions } from './filter_options'; import { useKibana } from '../../../../../../plugins/kibana_react/public'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { IIndexPattern, esFilters } from '../../../../../../plugins/data/public'; interface Props { filters: esFilters.Filter[]; onFiltersUpdated?: (filters: esFilters.Filter[]) => void; className: string; - indexPatterns: IndexPattern[]; + indexPatterns: IIndexPattern[]; intl: InjectedIntl; } diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/index.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/index.tsx index 84da576e8205cd..4f9424f30f5163 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/index.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/index.tsx @@ -36,37 +36,36 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import { get } from 'lodash'; import React, { Component } from 'react'; -import { Field, IndexPattern } from '../../../index_patterns'; import { GenericComboBox, GenericComboBoxProps } from './generic_combo_box'; import { - buildCustomFilter, - buildFilter, getFieldFromFilter, getFilterableFields, - getFilterParams, - getIndexPatternFromFilter, getOperatorFromFilter, getOperatorOptions, - getQueryDslFromFilter, isFilterValid, } from './lib/filter_editor_utils'; import { Operator } from './lib/filter_operators'; import { PhraseValueInput } from './phrase_value_input'; import { PhrasesValuesInput } from './phrases_values_input'; import { RangeValueInput } from './range_value_input'; -import { esFilters } from '../../../../../../../plugins/data/public'; +import { + esFilters, + utils, + IIndexPattern, + IFieldType, +} from '../../../../../../../plugins/data/public'; interface Props { filter: esFilters.Filter; - indexPatterns: IndexPattern[]; + indexPatterns: IIndexPattern[]; onSubmit: (filter: esFilters.Filter) => void; onCancel: () => void; intl: InjectedIntl; } interface State { - selectedIndexPattern?: IndexPattern; - selectedField?: Field; + selectedIndexPattern?: IIndexPattern; + selectedField?: IFieldType; selectedOperator?: Operator; params: any; useCustomLabel: boolean; @@ -82,10 +81,10 @@ class FilterEditorUI extends Component { selectedIndexPattern: this.getIndexPatternFromFilter(), selectedField: this.getFieldFromFilter(), selectedOperator: this.getSelectedOperator(), - params: getFilterParams(props.filter), + params: esFilters.getFilterParams(props.filter), useCustomLabel: props.filter.meta.alias !== null, customLabel: props.filter.meta.alias, - queryDsl: JSON.stringify(getQueryDslFromFilter(props.filter), null, 2), + queryDsl: JSON.stringify(esFilters.cleanFilter(props.filter), null, 2), isCustomEditorOpen: this.isUnknownFilterType(), }; } @@ -377,7 +376,7 @@ class FilterEditorUI extends Component { } private getIndexPatternFromFilter() { - return getIndexPatternFromFilter(this.props.filter, this.props.indexPatterns); + return utils.getIndexPatternFromFilter(this.props.filter, this.props.indexPatterns); } private getFieldFromFilter() { @@ -412,14 +411,14 @@ class FilterEditorUI extends Component { return isFilterValid(indexPattern, field, operator, params); } - private onIndexPatternChange = ([selectedIndexPattern]: IndexPattern[]) => { + private onIndexPatternChange = ([selectedIndexPattern]: IIndexPattern[]) => { const selectedField = undefined; const selectedOperator = undefined; const params = undefined; this.setState({ selectedIndexPattern, selectedField, selectedOperator, params }); }; - private onFieldChange = ([selectedField]: Field[]) => { + private onFieldChange = ([selectedField]: IFieldType[]) => { const selectedOperator = undefined; const params = undefined; this.setState({ selectedField, selectedOperator, params }); @@ -475,13 +474,21 @@ class FilterEditorUI extends Component { const { index, disabled, negate } = this.props.filter.meta; const newIndex = index || this.props.indexPatterns[0].id!; const body = JSON.parse(queryDsl); - const filter = buildCustomFilter(newIndex, body, disabled, negate, alias, $state.store); + const filter = esFilters.buildCustomFilter( + newIndex, + body, + disabled, + negate, + alias, + $state.store + ); this.props.onSubmit(filter); } else if (indexPattern && field && operator) { - const filter = buildFilter( + const filter = esFilters.buildFilter( indexPattern, field, - operator, + operator.type, + operator.negate, this.props.filter.meta.disabled, params, alias, @@ -492,11 +499,11 @@ class FilterEditorUI extends Component { }; } -function IndexPatternComboBox(props: GenericComboBoxProps) { +function IndexPatternComboBox(props: GenericComboBoxProps) { return GenericComboBox(props); } -function FieldComboBox(props: GenericComboBoxProps) { +function FieldComboBox(props: GenericComboBoxProps) { return GenericComboBox(props); } diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.test.ts b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.test.ts index 577861db38faf9..6dc9bc2300e044 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.test.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.test.ts @@ -28,23 +28,15 @@ import { } from '../../../../../../../../plugins/data/public/stubs'; import { IndexPattern, Field } from '../../../../index'; import { - buildFilter, getFieldFromFilter, getFilterableFields, - getFilterParams, - getIndexPatternFromFilter, getOperatorFromFilter, getOperatorOptions, - getQueryDslFromFilter, isFilterValid, } from './filter_editor_utils'; -import { - doesNotExistOperator, - existsOperator, - isBetweenOperator, - isOneOfOperator, - isOperator, -} from './filter_operators'; + +import { existsOperator, isBetweenOperator, isOneOfOperator, isOperator } from './filter_operators'; + import { esFilters } from '../../../../../../../../plugins/data/public'; jest.mock('ui/new_platform'); @@ -53,21 +45,6 @@ const mockedFields = stubFields as Field[]; const mockedIndexPattern = stubIndexPattern as IndexPattern; describe('Filter editor utils', () => { - describe('getQueryDslFromFilter', () => { - it('should return query DSL without meta and $state', () => { - const queryDsl = getQueryDslFromFilter(phraseFilter); - expect(queryDsl).not.toHaveProperty('meta'); - expect(queryDsl).not.toHaveProperty('$state'); - }); - }); - - describe('getIndexPatternFromFilter', () => { - it('should return the index pattern from the filter', () => { - const indexPattern = getIndexPatternFromFilter(phraseFilter, [mockedIndexPattern]); - expect(indexPattern).toBe(mockedIndexPattern); - }); - }); - describe('getFieldFromFilter', () => { it('should return the field from the filter', () => { const field = getFieldFromFilter(phraseFilter, mockedIndexPattern); @@ -138,28 +115,6 @@ describe('Filter editor utils', () => { }); }); - describe('getFilterParams', () => { - it('should retrieve params from phrase filter', () => { - const params = getFilterParams(phraseFilter); - expect(params).toBe('ios'); - }); - - it('should retrieve params from phrases filter', () => { - const params = getFilterParams(phrasesFilter); - expect(params).toEqual(['win xp', 'osx']); - }); - - it('should retrieve params from range filter', () => { - const params = getFilterParams(rangeFilter); - expect(params).toEqual({ from: 0, to: 10 }); - }); - - it('should return undefined for exists filter', () => { - const params = getFilterParams(existsFilter); - expect(params).toBeUndefined(); - }); - }); - describe('getFilterableFields', () => { it('returns the list of fields from the given index pattern', () => { const fieldOptions = getFilterableFields(mockedIndexPattern); @@ -245,129 +200,4 @@ describe('Filter editor utils', () => { expect(isValid).toBe(true); }); }); - - describe('buildFilter', () => { - it('should build phrase filters', () => { - const params = 'foo'; - const alias = 'bar'; - const state = esFilters.FilterStateStore.APP_STATE; - const filter = buildFilter( - mockedIndexPattern, - mockedFields[0], - isOperator, - false, - params, - alias, - state - ); - expect(filter.meta.negate).toBe(isOperator.negate); - expect(filter.meta.alias).toBe(alias); - - expect(filter.$state).toBeDefined(); - if (filter.$state) { - expect(filter.$state.store).toBe(state); - } - }); - - it('should build phrases filters', () => { - const params = ['foo', 'bar']; - const alias = 'bar'; - const state = esFilters.FilterStateStore.APP_STATE; - const filter = buildFilter( - mockedIndexPattern, - mockedFields[0], - isOneOfOperator, - false, - params, - alias, - state - ); - expect(filter.meta.type).toBe(isOneOfOperator.type); - expect(filter.meta.negate).toBe(isOneOfOperator.negate); - expect(filter.meta.alias).toBe(alias); - expect(filter.$state).toBeDefined(); - if (filter.$state) { - expect(filter.$state.store).toBe(state); - } - }); - - it('should build range filters', () => { - const params = { from: 'foo', to: 'qux' }; - const alias = 'bar'; - const state = esFilters.FilterStateStore.APP_STATE; - const filter = buildFilter( - mockedIndexPattern, - mockedFields[0], - isBetweenOperator, - false, - params, - alias, - state - ); - expect(filter.meta.negate).toBe(isBetweenOperator.negate); - expect(filter.meta.alias).toBe(alias); - expect(filter.$state).toBeDefined(); - if (filter.$state) { - expect(filter.$state.store).toBe(state); - } - }); - - it('should build exists filters', () => { - const params = undefined; - const alias = 'bar'; - const state = esFilters.FilterStateStore.APP_STATE; - const filter = buildFilter( - mockedIndexPattern, - mockedFields[0], - existsOperator, - false, - params, - alias, - state - ); - expect(filter.meta.negate).toBe(existsOperator.negate); - expect(filter.meta.alias).toBe(alias); - expect(filter.$state).toBeDefined(); - if (filter.$state) { - expect(filter.$state.store).toBe(state); - } - }); - - it('should include disabled state', () => { - const params = undefined; - const alias = 'bar'; - const state = esFilters.FilterStateStore.APP_STATE; - const filter = buildFilter( - mockedIndexPattern, - mockedFields[0], - doesNotExistOperator, - true, - params, - alias, - state - ); - expect(filter.meta.disabled).toBe(true); - }); - - it('should negate based on operator', () => { - const params = undefined; - const alias = 'bar'; - const state = esFilters.FilterStateStore.APP_STATE; - const filter = buildFilter( - mockedIndexPattern, - mockedFields[0], - doesNotExistOperator, - false, - params, - alias, - state - ); - expect(filter.meta.negate).toBe(doesNotExistOperator.negate); - expect(filter.meta.alias).toBe(alias); - expect(filter.$state).toBeDefined(); - if (filter.$state) { - expect(filter.$state.store).toBe(state); - } - }); - }); }); diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.ts b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.ts index b7d20526a6b924..e4487af42beaf4 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_editor_utils.ts @@ -18,20 +18,16 @@ */ import dateMath from '@elastic/datemath'; -import { omit } from 'lodash'; import { Ipv4Address } from '../../../../../../../../plugins/kibana_utils/public'; -import { Field, IndexPattern, isFilterable } from '../../../../index_patterns'; import { FILTER_OPERATORS, Operator } from './filter_operators'; -import { esFilters } from '../../../../../../../../plugins/data/public'; +import { + esFilters, + IIndexPattern, + IFieldType, + isFilterable, +} from '../../../../../../../../plugins/data/public'; -export function getIndexPatternFromFilter( - filter: esFilters.Filter, - indexPatterns: IndexPattern[] -): IndexPattern | undefined { - return indexPatterns.find(indexPattern => indexPattern.id === filter.meta.index); -} - -export function getFieldFromFilter(filter: esFilters.FieldFilter, indexPattern: IndexPattern) { +export function getFieldFromFilter(filter: esFilters.FieldFilter, indexPattern: IIndexPattern) { return indexPattern.fields.find(field => field.name === filter.meta.key); } @@ -41,34 +37,16 @@ export function getOperatorFromFilter(filter: esFilters.Filter) { }); } -export function getQueryDslFromFilter(filter: esFilters.Filter) { - return omit(filter, ['$state', 'meta']); -} - -export function getFilterableFields(indexPattern: IndexPattern) { +export function getFilterableFields(indexPattern: IIndexPattern) { return indexPattern.fields.filter(isFilterable); } -export function getOperatorOptions(field: Field) { +export function getOperatorOptions(field: IFieldType) { return FILTER_OPERATORS.filter(operator => { return !operator.fieldTypes || operator.fieldTypes.includes(field.type); }); } -export function getFilterParams(filter: esFilters.Filter) { - switch (filter.meta.type) { - case 'phrase': - return (filter as esFilters.PhraseFilter).meta.params.query; - case 'phrases': - return (filter as esFilters.PhrasesFilter).meta.params; - case 'range': - return { - from: (filter as esFilters.RangeFilter).meta.params.gte, - to: (filter as esFilters.RangeFilter).meta.params.lt, - }; - } -} - export function validateParams(params: any, type: string) { switch (type) { case 'date': @@ -86,8 +64,8 @@ export function validateParams(params: any, type: string) { } export function isFilterValid( - indexPattern?: IndexPattern, - field?: Field, + indexPattern?: IIndexPattern, + field?: IFieldType, operator?: Operator, params?: any ) { @@ -113,55 +91,3 @@ export function isFilterValid( throw new Error(`Unknown operator type: ${operator.type}`); } } - -export function buildFilter( - indexPattern: IndexPattern, - field: Field, - operator: Operator, - disabled: boolean, - params: any, - alias: string | null, - store: esFilters.FilterStateStore -): esFilters.Filter { - const filter = buildBaseFilter(indexPattern, field, operator, params); - filter.meta.alias = alias; - filter.meta.negate = operator.negate; - filter.meta.disabled = disabled; - filter.$state = { store }; - return filter; -} - -function buildBaseFilter( - indexPattern: IndexPattern, - field: Field, - operator: Operator, - params: any -): esFilters.Filter { - switch (operator.type) { - case 'phrase': - return esFilters.buildPhraseFilter(field, params, indexPattern); - case 'phrases': - return esFilters.buildPhrasesFilter(field, params, indexPattern); - case 'range': - const newParams = { gte: params.from, lt: params.to }; - return esFilters.buildRangeFilter(field, newParams, indexPattern); - case 'exists': - return esFilters.buildExistsFilter(field, indexPattern); - default: - throw new Error(`Unknown operator type: ${operator.type}`); - } -} - -export function buildCustomFilter( - index: string, - queryDsl: any, - disabled: boolean, - negate: boolean, - alias: string | null, - store: esFilters.FilterStateStore -): esFilters.Filter { - const meta: esFilters.FilterMeta = { index, type: 'custom', disabled, negate, alias }; - const filter: esFilters.Filter = { ...queryDsl, meta }; - filter.$state = { store }; - return filter; -} diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_label.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_label.tsx index d16158226579c6..1b4bdb881116b2 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_label.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_label.tsx @@ -51,50 +51,43 @@ export function FilterLabel({ filter, valueLabel }: Props) { } switch (filter.meta.type) { - case 'exists': + case esFilters.FILTERS.EXISTS: return ( {prefix} {filter.meta.key} {existsOperator.message} ); - case 'geo_bounding_box': + case esFilters.FILTERS.GEO_BOUNDING_BOX: return ( {prefix} {filter.meta.key}: {valueLabel} ); - case 'geo_polygon': + case esFilters.FILTERS.GEO_POLYGON: return ( {prefix} {filter.meta.key}: {valueLabel} ); - case 'phrase': - return ( - - {prefix} - {filter.meta.key}: {valueLabel} - - ); - case 'phrases': + case esFilters.FILTERS.PHRASES: return ( {prefix} {filter.meta.key} {isOneOfOperator.message} {valueLabel} ); - case 'query_string': + case esFilters.FILTERS.QUERY_STRING: return ( {prefix} {valueLabel} ); - case 'range': - case 'phrase': + case esFilters.FILTERS.PHRASE: + case esFilters.FILTERS.RANGE: return ( {prefix} diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_operators.ts b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_operators.ts index 469f5355df106f..a3da03db71d6ea 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_operators.ts +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/filter_operators.ts @@ -18,10 +18,11 @@ */ import { i18n } from '@kbn/i18n'; +import { esFilters } from '../../../../../../../../plugins/data/public'; export interface Operator { message: string; - type: string; + type: esFilters.FILTERS; negate: boolean; fieldTypes?: string[]; } @@ -30,7 +31,7 @@ export const isOperator = { message: i18n.translate('data.filter.filterEditor.isOperatorOptionLabel', { defaultMessage: 'is', }), - type: 'phrase', + type: esFilters.FILTERS.PHRASE, negate: false, }; @@ -38,7 +39,7 @@ export const isNotOperator = { message: i18n.translate('data.filter.filterEditor.isNotOperatorOptionLabel', { defaultMessage: 'is not', }), - type: 'phrase', + type: esFilters.FILTERS.PHRASE, negate: true, }; @@ -46,7 +47,7 @@ export const isOneOfOperator = { message: i18n.translate('data.filter.filterEditor.isOneOfOperatorOptionLabel', { defaultMessage: 'is one of', }), - type: 'phrases', + type: esFilters.FILTERS.PHRASES, negate: false, fieldTypes: ['string', 'number', 'date', 'ip', 'geo_point', 'geo_shape'], }; @@ -55,7 +56,7 @@ export const isNotOneOfOperator = { message: i18n.translate('data.filter.filterEditor.isNotOneOfOperatorOptionLabel', { defaultMessage: 'is not one of', }), - type: 'phrases', + type: esFilters.FILTERS.PHRASES, negate: true, fieldTypes: ['string', 'number', 'date', 'ip', 'geo_point', 'geo_shape'], }; @@ -64,7 +65,7 @@ export const isBetweenOperator = { message: i18n.translate('data.filter.filterEditor.isBetweenOperatorOptionLabel', { defaultMessage: 'is between', }), - type: 'range', + type: esFilters.FILTERS.RANGE, negate: false, fieldTypes: ['number', 'date', 'ip'], }; @@ -73,7 +74,7 @@ export const isNotBetweenOperator = { message: i18n.translate('data.filter.filterEditor.isNotBetweenOperatorOptionLabel', { defaultMessage: 'is not between', }), - type: 'range', + type: esFilters.FILTERS.RANGE, negate: true, fieldTypes: ['number', 'date', 'ip'], }; @@ -82,7 +83,7 @@ export const existsOperator = { message: i18n.translate('data.filter.filterEditor.existsOperatorOptionLabel', { defaultMessage: 'exists', }), - type: 'exists', + type: esFilters.FILTERS.EXISTS, negate: false, }; @@ -90,7 +91,7 @@ export const doesNotExistOperator = { message: i18n.translate('data.filter.filterEditor.doesNotExistOperatorOptionLabel', { defaultMessage: 'does not exist', }), - type: 'exists', + type: esFilters.FILTERS.EXISTS, negate: true, }; diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/phrase_suggestor.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/phrase_suggestor.tsx index c8b36d84f440e1..092bf8daa8f2eb 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/phrase_suggestor.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/phrase_suggestor.tsx @@ -19,17 +19,21 @@ import { Component } from 'react'; import { debounce } from 'lodash'; -import { Field, IndexPattern } from '../../../index_patterns'; import { withKibana, KibanaReactContextValue, } from '../../../../../../../plugins/kibana_react/public'; -import { IDataPluginServices } from '../../../../../../../plugins/data/public'; + +import { + IDataPluginServices, + IIndexPattern, + IFieldType, +} from '../../../../../../../plugins/data/public'; export interface PhraseSuggestorProps { kibana: KibanaReactContextValue; - indexPattern: IndexPattern; - field?: Field; + indexPattern: IIndexPattern; + field?: IFieldType; } export interface PhraseSuggestorState { diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/range_value_input.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/range_value_input.tsx index 6a5229ac826cbd..3c39a770377a0e 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/range_value_input.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/range_value_input.tsx @@ -22,7 +22,7 @@ import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import { get } from 'lodash'; import React from 'react'; import { useKibana } from '../../../../../../../plugins/kibana_react/public'; -import { Field } from '../../../index_patterns'; +import { IFieldType } from '../../../../../../../plugins/data/public'; import { ValueInputType } from './value_input_type'; interface RangeParams { @@ -33,7 +33,7 @@ interface RangeParams { type RangeParamsPartial = Partial; interface Props { - field?: Field; + field?: IFieldType; value?: RangeParams; onChange: (params: RangeParamsPartial) => void; intl: InjectedIntl; diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_item.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_item.tsx index 0dbe92dcb0da67..27406232dd5d3a 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_item.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_item.tsx @@ -22,16 +22,14 @@ import { InjectedIntl, injectI18n } from '@kbn/i18n/react'; import classNames from 'classnames'; import React, { Component } from 'react'; import { UiSettingsClientContract } from 'src/core/public'; -import { IndexPattern } from '../../index_patterns'; import { FilterEditor } from './filter_editor'; import { FilterView } from './filter_view'; -import { getDisplayValueFromFilter } from './filter_editor/lib/get_display_value'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters, utils, IIndexPattern } from '../../../../../../plugins/data/public'; interface Props { id: string; filter: esFilters.Filter; - indexPatterns: IndexPattern[]; + indexPatterns: IIndexPattern[]; className?: string; onUpdate: (filter: esFilters.Filter) => void; onRemove: () => void; @@ -62,7 +60,7 @@ class FilterItemUI extends Component { this.props.className ); - const valueLabel = getDisplayValueFromFilter(filter, this.props.indexPatterns); + const valueLabel = utils.getDisplayValueFromFilter(filter, this.props.indexPatterns); const dataTestSubjKey = filter.meta.key ? `filter-key-${filter.meta.key}` : ''; const dataTestSubjValue = filter.meta.value ? `filter-value-${valueLabel}` : ''; const dataTestSubjDisabled = `filter-${ diff --git a/src/legacy/core_plugins/data/public/index.ts b/src/legacy/core_plugins/data/public/index.ts index 2412541e8c5c8f..ffce162cadde45 100644 --- a/src/legacy/core_plugins/data/public/index.ts +++ b/src/legacy/core_plugins/data/public/index.ts @@ -48,7 +48,6 @@ export { CONTAINS_SPACES, getFromSavedObject, getRoutes, - isFilterable, IndexPatternSelect, validateIndexPattern, ILLEGAL_CHARACTERS, diff --git a/src/legacy/core_plugins/data/public/index_patterns/index_patterns_service.ts b/src/legacy/core_plugins/data/public/index_patterns/index_patterns_service.ts index c9c52400b1f19c..f97246bc5a9bf0 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/index_patterns_service.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/index_patterns_service.ts @@ -99,7 +99,6 @@ export { ILLEGAL_CHARACTERS, INDEX_PATTERN_ILLEGAL_CHARACTERS, INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE, - isFilterable, validateIndexPattern, } from './utils'; diff --git a/src/legacy/core_plugins/data/public/index_patterns/utils.test.ts b/src/legacy/core_plugins/data/public/index_patterns/utils.test.ts index 1a186a65147633..cff48144489f05 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/utils.test.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/utils.test.ts @@ -21,19 +21,9 @@ import { CONTAINS_SPACES, ILLEGAL_CHARACTERS, INDEX_PATTERN_ILLEGAL_CHARACTERS_VISIBLE, - isFilterable, validateIndexPattern, } from './utils'; -import { Field } from './fields'; - -const mockField = { - name: 'foo', - scripted: false, - searchable: true, - type: 'string', -} as Field; - describe('Index Pattern Utils', () => { describe('Validation', () => { it('should not allow space in the pattern', () => { @@ -52,42 +42,4 @@ describe('Index Pattern Utils', () => { expect(validateIndexPattern('my-pattern-*')).toEqual({}); }); }); - - describe('isFilterable', () => { - describe('types', () => { - it('should return true for filterable types', () => { - ['string', 'number', 'date', 'ip', 'boolean'].forEach(type => { - expect(isFilterable({ ...mockField, type })).toBe(true); - }); - }); - - it('should return false for filterable types if the field is not searchable', () => { - ['string', 'number', 'date', 'ip', 'boolean'].forEach(type => { - expect(isFilterable({ ...mockField, type, searchable: false })).toBe(false); - }); - }); - - it('should return false for un-filterable types', () => { - [ - 'geo_point', - 'geo_shape', - 'attachment', - 'murmur3', - '_source', - 'unknown', - 'conflict', - ].forEach(type => { - expect(isFilterable({ ...mockField, type })).toBe(false); - }); - }); - }); - - it('should return true for scripted fields', () => { - expect(isFilterable({ ...mockField, scripted: true, searchable: false })).toBe(true); - }); - - it('should return true for the _id field', () => { - expect(isFilterable({ ...mockField, name: '_id' })).toBe(true); - }); - }); }); diff --git a/src/legacy/core_plugins/data/public/index_patterns/utils.ts b/src/legacy/core_plugins/data/public/index_patterns/utils.ts index 1c877f4f142513..8542c1dcce24d2 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/utils.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/utils.ts @@ -19,9 +19,6 @@ import { find, get } from 'lodash'; -import { Field } from './fields'; -import { getFilterableKbnTypeNames } from '../../../../../plugins/data/public'; - import { SavedObjectsClientContract, SimpleSavedObject } from '../../../../../core/public'; export const ILLEGAL_CHARACTERS = 'ILLEGAL_CHARACTERS'; @@ -107,16 +104,6 @@ export function validateIndexPattern(indexPattern: string) { return errors; } -const filterableTypes = getFilterableKbnTypeNames(); - -export function isFilterable(field: Field): boolean { - return ( - field.name === '_id' || - field.scripted || - Boolean(field.searchable && filterableTypes.includes(field.type)) - ); -} - export function getFromSavedObject(savedObject: any) { if (get(savedObject, 'attributes.fields') === undefined) { return; diff --git a/src/legacy/ui/public/index_patterns/__mocks__/index.ts b/src/legacy/ui/public/index_patterns/__mocks__/index.ts index 2dd3f370c6d6aa..f51ae86b5c9a78 100644 --- a/src/legacy/ui/public/index_patterns/__mocks__/index.ts +++ b/src/legacy/ui/public/index_patterns/__mocks__/index.ts @@ -35,7 +35,6 @@ export { CONTAINS_SPACES, getFromSavedObject, getRoutes, - isFilterable, IndexPatternSelect, validateIndexPattern, ILLEGAL_CHARACTERS, diff --git a/src/legacy/ui/public/index_patterns/index.ts b/src/legacy/ui/public/index_patterns/index.ts index 3b4952ac815192..690a9cffaa1388 100644 --- a/src/legacy/ui/public/index_patterns/index.ts +++ b/src/legacy/ui/public/index_patterns/index.ts @@ -38,7 +38,6 @@ export { CONTAINS_SPACES, getFromSavedObject, getRoutes, - isFilterable, validateIndexPattern, ILLEGAL_CHARACTERS, INDEX_PATTERN_ILLEGAL_CHARACTERS, diff --git a/src/plugins/data/common/es_query/filters/build_filter.test.ts b/src/plugins/data/common/es_query/filters/build_filter.test.ts new file mode 100644 index 00000000000000..22b44035d6ca85 --- /dev/null +++ b/src/plugins/data/common/es_query/filters/build_filter.test.ts @@ -0,0 +1,131 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { buildFilter, FilterStateStore, FILTERS } from '.'; +import { stubIndexPattern, stubFields } from '../../../public/stubs'; + +describe('buildFilter', () => { + it('should build phrase filters', () => { + const params = 'foo'; + const alias = 'bar'; + const state = FilterStateStore.APP_STATE; + const filter = buildFilter( + stubIndexPattern, + stubFields[0], + FILTERS.PHRASE, + false, + false, + params, + alias, + state + ); + expect(filter.meta.negate).toBe(false); + expect(filter.meta.alias).toBe(alias); + + expect(filter.$state).toBeDefined(); + if (filter.$state) { + expect(filter.$state.store).toBe(state); + } + }); + + it('should build phrases filters', () => { + const params = ['foo', 'bar']; + const alias = 'bar'; + const state = FilterStateStore.APP_STATE; + const filter = buildFilter( + stubIndexPattern, + stubFields[0], + FILTERS.PHRASES, + false, + false, + params, + alias, + state + ); + expect(filter.meta.type).toBe(FILTERS.PHRASES); + expect(filter.meta.negate).toBe(false); + expect(filter.meta.alias).toBe(alias); + expect(filter.$state).toBeDefined(); + if (filter.$state) { + expect(filter.$state.store).toBe(state); + } + }); + + it('should build range filters', () => { + const params = { from: 'foo', to: 'qux' }; + const alias = 'bar'; + const state = FilterStateStore.APP_STATE; + const filter = buildFilter( + stubIndexPattern, + stubFields[0], + FILTERS.RANGE, + false, + false, + params, + alias, + state + ); + expect(filter.meta.negate).toBe(false); + expect(filter.meta.alias).toBe(alias); + expect(filter.$state).toBeDefined(); + if (filter.$state) { + expect(filter.$state.store).toBe(state); + } + }); + + it('should build exists filters', () => { + const params = undefined; + const alias = 'bar'; + const state = FilterStateStore.APP_STATE; + const filter = buildFilter( + stubIndexPattern, + stubFields[0], + FILTERS.EXISTS, + false, + false, + params, + alias, + state + ); + expect(filter.meta.negate).toBe(false); + expect(filter.meta.alias).toBe(alias); + expect(filter.$state).toBeDefined(); + if (filter.$state) { + expect(filter.$state.store).toBe(state); + } + }); + + it('should include disabled state', () => { + const params = undefined; + const alias = 'bar'; + const state = FilterStateStore.APP_STATE; + const filter = buildFilter( + stubIndexPattern, + stubFields[0], + FILTERS.EXISTS, + true, + true, + params, + alias, + state + ); + expect(filter.meta.disabled).toBe(true); + expect(filter.meta.negate).toBe(true); + }); +}); diff --git a/src/plugins/data/common/es_query/filters/build_filters.ts b/src/plugins/data/common/es_query/filters/build_filters.ts new file mode 100644 index 00000000000000..affd213c295177 --- /dev/null +++ b/src/plugins/data/common/es_query/filters/build_filters.ts @@ -0,0 +1,80 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { esFilters, IIndexPattern, IFieldType } from '../..'; +import { FilterMeta, FilterStateStore } from '.'; + +export function buildFilter( + indexPattern: IIndexPattern, + field: IFieldType, + type: esFilters.FILTERS, + negate: boolean, + disabled: boolean, + params: any, + alias: string | null, + store: esFilters.FilterStateStore +): esFilters.Filter { + const filter = buildBaseFilter(indexPattern, field, type, params); + filter.meta.alias = alias; + filter.meta.negate = negate; + filter.meta.disabled = disabled; + filter.$state = { store }; + return filter; +} + +export function buildCustomFilter( + indexPatternString: string, + queryDsl: any, + disabled: boolean, + negate: boolean, + alias: string | null, + store: FilterStateStore +): esFilters.Filter { + const meta: FilterMeta = { + index: indexPatternString, + type: esFilters.FILTERS.CUSTOM, + disabled, + negate, + alias, + }; + const filter: esFilters.Filter = { ...queryDsl, meta }; + filter.$state = { store }; + return filter; +} + +function buildBaseFilter( + indexPattern: IIndexPattern, + field: IFieldType, + type: esFilters.FILTERS, + params: any +): esFilters.Filter { + switch (type) { + case 'phrase': + return esFilters.buildPhraseFilter(field, params, indexPattern); + case 'phrases': + return esFilters.buildPhrasesFilter(field, params, indexPattern); + case 'range': + const newParams = { gte: params.from, lt: params.to }; + return esFilters.buildRangeFilter(field, newParams, indexPattern); + case 'exists': + return esFilters.buildExistsFilter(field, indexPattern); + default: + throw new Error(`Unknown filter type: ${type}`); + } +} diff --git a/src/plugins/data/common/es_query/filters/get_filter_params.test.ts b/src/plugins/data/common/es_query/filters/get_filter_params.test.ts new file mode 100644 index 00000000000000..b0e992318327e1 --- /dev/null +++ b/src/plugins/data/common/es_query/filters/get_filter_params.test.ts @@ -0,0 +1,43 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { phraseFilter, phrasesFilter, rangeFilter, existsFilter } from './stubs'; +import { getFilterParams } from './get_filter_params'; + +describe('getFilterParams', () => { + it('should retrieve params from phrase filter', () => { + const params = getFilterParams(phraseFilter); + expect(params).toBe('ios'); + }); + + it('should retrieve params from phrases filter', () => { + const params = getFilterParams(phrasesFilter); + expect(params).toEqual(['win xp', 'osx']); + }); + + it('should retrieve params from range filter', () => { + const params = getFilterParams(rangeFilter); + expect(params).toEqual({ from: 0, to: 10 }); + }); + + it('should return undefined for exists filter', () => { + const params = getFilterParams(existsFilter); + expect(params).toBeUndefined(); + }); +}); diff --git a/src/plugins/data/common/es_query/filters/get_filter_params.ts b/src/plugins/data/common/es_query/filters/get_filter_params.ts new file mode 100644 index 00000000000000..2e90ff0fe06912 --- /dev/null +++ b/src/plugins/data/common/es_query/filters/get_filter_params.ts @@ -0,0 +1,34 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Filter, FILTERS, PhraseFilter, PhrasesFilter, RangeFilter } from '.'; + +export function getFilterParams(filter: Filter) { + switch (filter.meta.type) { + case FILTERS.PHRASE: + return (filter as PhraseFilter).meta.params.query; + case FILTERS.PHRASES: + return (filter as PhrasesFilter).meta.params; + case FILTERS.RANGE: + return { + from: (filter as RangeFilter).meta.params.gte, + to: (filter as RangeFilter).meta.params.lt, + }; + } +} diff --git a/src/plugins/data/common/es_query/filters/index.ts b/src/plugins/data/common/es_query/filters/index.ts index c19545eb83a060..1bd534bf74ff7c 100644 --- a/src/plugins/data/common/es_query/filters/index.ts +++ b/src/plugins/data/common/es_query/filters/index.ts @@ -20,6 +20,9 @@ import { omit, get } from 'lodash'; import { Filter } from './meta_filter'; +export * from './build_filters'; +export * from './get_filter_params'; + export * from './custom_filter'; export * from './exists_filter'; export * from './geo_bounding_box_filter'; diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/get_display_value.ts b/src/plugins/data/common/es_query/utils/get_display_value.ts similarity index 69% rename from src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/get_display_value.ts rename to src/plugins/data/common/es_query/utils/get_display_value.ts index d8af7b3e97ad23..4bf7e1c9c6ba70 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_editor/lib/get_display_value.ts +++ b/src/plugins/data/common/es_query/utils/get_display_value.ts @@ -18,25 +18,21 @@ */ import { get } from 'lodash'; -import { esFilters } from '../../../../../../../../plugins/data/public'; -import { IndexPattern } from '../../../../index_patterns/index_patterns'; -import { Field } from '../../../../index_patterns/fields'; -import { getIndexPatternFromFilter } from './filter_editor_utils'; +import { IIndexPattern, IFieldType } from '../..'; +import { getIndexPatternFromFilter } from './get_index_pattern_from_filter'; +import { Filter } from '../filters'; -function getValueFormatter(indexPattern?: IndexPattern, key?: string) { +function getValueFormatter(indexPattern?: IIndexPattern, key?: string) { if (!indexPattern || !key) return; let format = get(indexPattern, ['fields', 'byName', key, 'format']); - if (!format && indexPattern.fields.getByName) { + if (!format && (indexPattern.fields as any).getByName) { // TODO: Why is indexPatterns sometimes a map and sometimes an array? - format = (indexPattern.fields.getByName(key) as Field).format; + format = ((indexPattern.fields as any).getByName(key) as IFieldType).format; } return format; } -export function getDisplayValueFromFilter( - filter: esFilters.Filter, - indexPatterns: IndexPattern[] -): string { +export function getDisplayValueFromFilter(filter: Filter, indexPatterns: IIndexPattern[]): string { const indexPattern = getIndexPatternFromFilter(filter, indexPatterns); if (typeof filter.meta.value === 'function') { diff --git a/src/plugins/data/common/es_query/utils/get_index_pattern_from_filter.test.ts b/src/plugins/data/common/es_query/utils/get_index_pattern_from_filter.test.ts new file mode 100644 index 00000000000000..2f31fafcb74e48 --- /dev/null +++ b/src/plugins/data/common/es_query/utils/get_index_pattern_from_filter.test.ts @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { stubIndexPattern, phraseFilter } from 'src/plugins/data/public/stubs'; +import { getIndexPatternFromFilter } from './get_index_pattern_from_filter'; + +describe('getIndexPatternFromFilter', () => { + it('should return the index pattern from the filter', () => { + const indexPattern = getIndexPatternFromFilter(phraseFilter, [stubIndexPattern]); + expect(indexPattern).toBe(stubIndexPattern); + }); +}); diff --git a/src/plugins/data/common/es_query/utils/get_index_pattern_from_filter.ts b/src/plugins/data/common/es_query/utils/get_index_pattern_from_filter.ts new file mode 100644 index 00000000000000..43d4bdaf03bc1a --- /dev/null +++ b/src/plugins/data/common/es_query/utils/get_index_pattern_from_filter.ts @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Filter } from '../filters'; +import { IIndexPattern } from '../..'; + +export function getIndexPatternFromFilter( + filter: Filter, + indexPatterns: IIndexPattern[] +): IIndexPattern | undefined { + return indexPatterns.find(indexPattern => indexPattern.id === filter.meta.index); +} diff --git a/src/plugins/data/common/es_query/utils/index.ts b/src/plugins/data/common/es_query/utils/index.ts index 27f51c1f44cf2f..79856c9e0267ee 100644 --- a/src/plugins/data/common/es_query/utils/index.ts +++ b/src/plugins/data/common/es_query/utils/index.ts @@ -18,3 +18,5 @@ */ export * from './get_time_zone_from_settings'; +export * from './get_index_pattern_from_filter'; +export * from './get_display_value'; diff --git a/src/plugins/data/common/index_patterns/fields/index.ts b/src/plugins/data/common/index_patterns/fields/index.ts index d8f7b5091eb8f6..2b43dffa8c161e 100644 --- a/src/plugins/data/common/index_patterns/fields/index.ts +++ b/src/plugins/data/common/index_patterns/fields/index.ts @@ -18,3 +18,4 @@ */ export * from './types'; +export { isFilterable } from './utils'; diff --git a/src/plugins/data/common/index_patterns/fields/utils.ts b/src/plugins/data/common/index_patterns/fields/utils.ts new file mode 100644 index 00000000000000..c7bec5e5ad3476 --- /dev/null +++ b/src/plugins/data/common/index_patterns/fields/utils.ts @@ -0,0 +1,30 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { getFilterableKbnTypeNames, IFieldType } from '../..'; + +const filterableTypes = getFilterableKbnTypeNames(); + +export function isFilterable(field: IFieldType): boolean { + return ( + field.name === '_id' || + field.scripted || + Boolean(field.searchable && filterableTypes.includes(field.type)) + ); +} diff --git a/src/plugins/data/common/index_patterns/utils.test.ts b/src/plugins/data/common/index_patterns/utils.test.ts new file mode 100644 index 00000000000000..e2707d469a3171 --- /dev/null +++ b/src/plugins/data/common/index_patterns/utils.test.ts @@ -0,0 +1,60 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { isFilterable } from '.'; +import { IFieldType } from './fields'; + +const mockField = { + name: 'foo', + scripted: false, + searchable: true, + type: 'string', +} as IFieldType; + +describe('isFilterable', () => { + describe('types', () => { + it('should return true for filterable types', () => { + ['string', 'number', 'date', 'ip', 'boolean'].forEach(type => { + expect(isFilterable({ ...mockField, type })).toBe(true); + }); + }); + + it('should return false for filterable types if the field is not searchable', () => { + ['string', 'number', 'date', 'ip', 'boolean'].forEach(type => { + expect(isFilterable({ ...mockField, type, searchable: false })).toBe(false); + }); + }); + + it('should return false for un-filterable types', () => { + ['geo_point', 'geo_shape', 'attachment', 'murmur3', '_source', 'unknown', 'conflict'].forEach( + type => { + expect(isFilterable({ ...mockField, type })).toBe(false); + } + ); + }); + }); + + it('should return true for scripted fields', () => { + expect(isFilterable({ ...mockField, scripted: true, searchable: false })).toBe(true); + }); + + it('should return true for the _id field', () => { + expect(isFilterable({ ...mockField, name: '_id' })).toBe(true); + }); +}); diff --git a/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts b/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts index f2fd55af4f4180..b4d46ae9fb3cf7 100644 --- a/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts +++ b/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts @@ -95,16 +95,18 @@ export function generateFilters( } else { const tmpIndexPattern = { id: index } as IIndexPattern; - switch (fieldName) { - case '_exists_': - filter = esFilters.buildExistsFilter(fieldObj, tmpIndexPattern); - break; - default: - filter = esFilters.buildPhraseFilter(fieldObj, value, tmpIndexPattern); - break; - } - - filter.meta.negate = negate; + const filterType = + fieldName === '_exists_' ? esFilters.FILTERS.EXISTS : esFilters.FILTERS.PHRASE; + filter = esFilters.buildFilter( + tmpIndexPattern, + fieldObj, + filterType, + negate, + false, + value, + null, + esFilters.FilterStateStore.APP_STATE + ); } newFilters.push(filter); diff --git a/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/__tests__/field.js b/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/__tests__/field.js index 8a20337317cfb4..6eae233cf5deaa 100644 --- a/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/__tests__/field.js +++ b/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/__tests__/field.js @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { getSuggestionsProvider } from '../field'; import indexPatternResponse from '../__fixtures__/index_pattern_response.json'; -import { isFilterable } from 'ui/index_patterns'; +import { isFilterable } from '../../../../../../../src/plugins/data/public'; describe('Kuery field suggestions', function () { let indexPattern; diff --git a/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/field.js b/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/field.js index 3d7e1979d224bf..1a00c668833aa0 100644 --- a/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/field.js +++ b/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/field.js @@ -7,7 +7,7 @@ import React from 'react'; import { flatten } from 'lodash'; import { escapeKuery } from './escape_kuery'; import { sortPrefixFirst } from 'ui/utils/sort_prefix_first'; -import { isFilterable } from 'ui/index_patterns'; +import { isFilterable } from '../../../../../../src/plugins/data/public'; import { FormattedMessage } from '@kbn/i18n/react';