mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Filters] Add support for combined filter query DSL editing (#148590)
## Summary Resolves https://github.com/elastic/kibana/issues/144601. Enables "Edit as Query DSL" for combined filters (those created with complex AND/OR relationships). ### Checklist - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
04f27948ac
commit
46a37101e2
6 changed files with 51 additions and 56 deletions
|
@ -51,6 +51,7 @@ export type {
|
|||
export {
|
||||
buildEsQuery,
|
||||
buildQueryFromFilters,
|
||||
filterToQueryDsl,
|
||||
decorateQuery,
|
||||
luceneStringToDsl,
|
||||
migrateFilter,
|
||||
|
|
|
@ -71,32 +71,18 @@ export const buildQueryFromFilters = (
|
|||
ignoreFilterIfFieldNotInIndex: false,
|
||||
}
|
||||
): BoolQuery => {
|
||||
const { ignoreFilterIfFieldNotInIndex = false, nestedIgnoreUnmapped } = options;
|
||||
const { ignoreFilterIfFieldNotInIndex = false } = options;
|
||||
const filters = inputFilters.filter((filter) => filter && !isFilterDisabled(filter));
|
||||
const indexPatterns = Array.isArray(inputDataViews) ? inputDataViews : [inputDataViews];
|
||||
|
||||
const findIndexPattern = (id: string | undefined) => {
|
||||
return indexPatterns.find((index) => index?.id === id) || indexPatterns[0];
|
||||
};
|
||||
|
||||
const filtersToESQueries = (negate: boolean) => {
|
||||
return filters
|
||||
.filter((f) => !!f)
|
||||
.filter(filterNegate(negate))
|
||||
.filter((filter) => {
|
||||
const indexPattern = findIndexPattern(filter.meta?.index);
|
||||
const indexPattern = findIndexPattern(inputDataViews, filter.meta?.index);
|
||||
return !ignoreFilterIfFieldNotInIndex || filterMatchesIndex(filter, indexPattern);
|
||||
})
|
||||
.map((filter) => {
|
||||
const indexPattern = findIndexPattern(filter.meta?.index);
|
||||
const migratedFilter = migrateFilter(filter, indexPattern);
|
||||
return fromNestedFilter(migratedFilter, indexPattern, {
|
||||
ignoreUnmapped: nestedIgnoreUnmapped,
|
||||
});
|
||||
})
|
||||
.map((filter) => fromCombinedFilter(filter, inputDataViews, options))
|
||||
.map(cleanFilter)
|
||||
.map(translateToQuery);
|
||||
.map((filter) => filterToQueryDsl(filter, inputDataViews, options));
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -106,3 +92,24 @@ export const buildQueryFromFilters = (
|
|||
must_not: filtersToESQueries(true),
|
||||
};
|
||||
};
|
||||
|
||||
function findIndexPattern(
|
||||
inputDataViews: DataViewBase | DataViewBase[] | undefined,
|
||||
id: string | undefined
|
||||
) {
|
||||
const dataViews = Array.isArray(inputDataViews) ? inputDataViews : [inputDataViews];
|
||||
return dataViews.find((index) => index?.id === id) ?? dataViews[0];
|
||||
}
|
||||
|
||||
export function filterToQueryDsl(
|
||||
filter: Filter,
|
||||
inputDataViews: DataViewBase | DataViewBase[] | undefined,
|
||||
options: EsQueryFiltersConfig = {}
|
||||
) {
|
||||
const indexPattern = findIndexPattern(inputDataViews, filter.meta?.index);
|
||||
const migratedFilter = migrateFilter(filter, indexPattern);
|
||||
const nestedFilter = fromNestedFilter(migratedFilter, indexPattern, options);
|
||||
const combinedFilter = fromCombinedFilter(nestedFilter, inputDataViews, options);
|
||||
const cleanedFilter = cleanFilter(combinedFilter);
|
||||
return translateToQuery(cleanedFilter);
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ describe('fromNestedFilter', function () {
|
|||
it('should allow to configure ignore_unmapped', () => {
|
||||
const field = getField('nestedField.child');
|
||||
const filter = buildPhraseFilter(field!, 'foo', indexPattern);
|
||||
const result = fromNestedFilter(filter, indexPattern, { ignoreUnmapped: true });
|
||||
const result = fromNestedFilter(filter, indexPattern, { nestedIgnoreUnmapped: true });
|
||||
expect(result).toEqual({
|
||||
meta: {
|
||||
index: 'logstash-*',
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { EsQueryFiltersConfig } from '../..';
|
||||
import { getFilterField, cleanFilter, Filter } from '../filters';
|
||||
import { DataViewBase } from './types';
|
||||
import { getDataViewFieldSubtypeNested } from '../utils';
|
||||
|
@ -14,7 +15,7 @@ import { getDataViewFieldSubtypeNested } from '../utils';
|
|||
export const fromNestedFilter = (
|
||||
filter: Filter,
|
||||
indexPattern?: DataViewBase,
|
||||
config: { ignoreUnmapped?: boolean } = {}
|
||||
config: EsQueryFiltersConfig = {}
|
||||
) => {
|
||||
if (!indexPattern) return filter;
|
||||
|
||||
|
@ -40,8 +41,8 @@ export const fromNestedFilter = (
|
|||
nested: {
|
||||
path: subTypeNested.nested.path,
|
||||
query: query.query || query,
|
||||
...(typeof config.ignoreUnmapped === 'boolean' && {
|
||||
ignore_unmapped: config.ignoreUnmapped,
|
||||
...(typeof config.nestedIgnoreUnmapped === 'boolean' && {
|
||||
ignore_unmapped: config.nestedIgnoreUnmapped,
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -10,7 +10,7 @@ export { migrateFilter } from './migrate_filter';
|
|||
export type { EsQueryFiltersConfig } from './from_filters';
|
||||
export type { EsQueryConfig } from './build_es_query';
|
||||
export { buildEsQuery } from './build_es_query';
|
||||
export { buildQueryFromFilters } from './from_filters';
|
||||
export { buildQueryFromFilters, filterToQueryDsl } from './from_filters';
|
||||
export { luceneStringToDsl } from './lucene_string_to_dsl';
|
||||
export { decorateQuery } from './decorate_query';
|
||||
export {
|
||||
|
|
|
@ -25,14 +25,13 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import {
|
||||
type Filter,
|
||||
BooleanRelation,
|
||||
buildCombinedFilter,
|
||||
buildCustomFilter,
|
||||
buildEmptyFilter,
|
||||
cleanFilter,
|
||||
Filter,
|
||||
filterToQueryDsl,
|
||||
getFilterParams,
|
||||
isCombinedFilter,
|
||||
} from '@kbn/es-query';
|
||||
import { merge } from 'lodash';
|
||||
import React, { Component } from 'react';
|
||||
|
@ -76,10 +75,6 @@ export const strings = {
|
|||
i18n.translate('unifiedSearch.filter.filterEditor.updateButtonLabel', {
|
||||
defaultMessage: 'Update filter',
|
||||
}),
|
||||
getDisableToggleModeTooltip: () =>
|
||||
i18n.translate('unifiedSearch.filter.filterEditor.disableToggleModeTooltip', {
|
||||
defaultMessage: '"Edit as Query DSL" operation is not supported for combined filters',
|
||||
}),
|
||||
getSelectDataViewToolTip: () =>
|
||||
i18n.translate('unifiedSearch.filter.filterEditor.chooseDataViewFirstToolTip', {
|
||||
defaultMessage: 'You need to select a data view first',
|
||||
|
@ -159,13 +154,11 @@ class FilterEditorComponent extends Component<FilterEditorProps, State> {
|
|||
}
|
||||
|
||||
private parseFilterToQueryDsl(filter: Filter) {
|
||||
return JSON.stringify(cleanFilter(filter), null, 2);
|
||||
const dsl = filterToQueryDsl(filter, this.props.indexPatterns);
|
||||
return JSON.stringify(dsl, null, 2);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { localFilter } = this.state;
|
||||
const shouldDisableToggle = isCombinedFilter(localFilter);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<EuiPopoverTitle paddingSize="s">
|
||||
|
@ -180,30 +173,23 @@ class FilterEditorComponent extends Component<FilterEditorProps, State> {
|
|||
</EuiFlexGroup>
|
||||
<EuiFlexItem grow={false} className="filterEditor__hiddenItem" />
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
content={shouldDisableToggle ? strings.getDisableToggleModeTooltip() : null}
|
||||
display="block"
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
data-test-subj="editQueryDSL"
|
||||
onClick={this.toggleCustomEditor}
|
||||
>
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
data-test-subj="editQueryDSL"
|
||||
disabled={shouldDisableToggle}
|
||||
onClick={this.toggleCustomEditor}
|
||||
>
|
||||
{this.state.isCustomEditorOpen ? (
|
||||
<FormattedMessage
|
||||
id="unifiedSearch.filter.filterEditor.editFilterValuesButtonLabel"
|
||||
defaultMessage="Edit filter values"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="unifiedSearch.filter.filterEditor.editQueryDslButtonLabel"
|
||||
defaultMessage="Edit as Query DSL"
|
||||
/>
|
||||
)}
|
||||
</EuiButtonEmpty>
|
||||
</EuiToolTip>
|
||||
{this.state.isCustomEditorOpen ? (
|
||||
<FormattedMessage
|
||||
id="unifiedSearch.filter.filterEditor.editFilterValuesButtonLabel"
|
||||
defaultMessage="Edit filter values"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="unifiedSearch.filter.filterEditor.editQueryDslButtonLabel"
|
||||
defaultMessage="Edit as Query DSL"
|
||||
/>
|
||||
)}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPopoverTitle>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue