Skip to content

Commit 8e2cb56

Browse files
DianaDerevyankinaalexwizpelasticmachine
authored
KQL support in filter ratio in TSVB (#75033)
* KQL support in filter ratio in TSVB Closes #67503 * Fix filter_ratio and filter_ratios tests * fix JEST * Refactor some code in filter_ratio, filter_ratios, filter_ratios.test * Edit query value in filter_ratio and filter_ratios.test * Refacor some code in filter_ratio.js and visualization_migrations.ts * Remove duplications in vis_schema and refactor filter_ratio * Refactor filter_ratio.js * Update default query with getDefaultQuery() * Fix filter_ratio and histogram_support tests Co-authored-by: Alexey Antonov <alexwizp@gmail.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
1 parent 37ec1e1 commit 8e2cb56

File tree

8 files changed

+194
-42
lines changed

8 files changed

+194
-42
lines changed

src/plugins/vis_type_timeseries/common/vis_schema.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ export const metricsItems = schema.object({
8080
field: stringOptionalNullable,
8181
id: stringRequired,
8282
metric_agg: stringOptionalNullable,
83-
numerator: stringOptionalNullable,
84-
denominator: stringOptionalNullable,
83+
numerator: schema.maybe(queryObject),
84+
denominator: schema.maybe(queryObject),
8585
sigma: stringOptionalNullable,
8686
unit: stringOptionalNullable,
8787
model_type: stringOptionalNullable,
@@ -128,12 +128,7 @@ export const metricsItems = schema.object({
128128
const splitFiltersItems = schema.object({
129129
id: stringOptionalNullable,
130130
color: stringOptionalNullable,
131-
filter: schema.maybe(
132-
schema.object({
133-
language: schema.string(),
134-
query: schema.string(),
135-
})
136-
),
131+
filter: schema.maybe(queryObject),
137132
label: stringOptionalNullable,
138133
});
139134

src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.js

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,25 @@
1818
*/
1919

2020
import PropTypes from 'prop-types';
21-
import React from 'react';
21+
import React, { useCallback, useMemo } from 'react';
2222
import { AggSelect } from './agg_select';
2323
import { FieldSelect } from './field_select';
2424
import { AggRow } from './agg_row';
2525
import { createChangeHandler } from '../lib/create_change_handler';
2626
import { createSelectHandler } from '../lib/create_select_handler';
27-
import { createTextHandler } from '../lib/create_text_handler';
2827
import {
2928
htmlIdGenerator,
3029
EuiFlexGroup,
3130
EuiFlexItem,
3231
EuiFormLabel,
33-
EuiFieldText,
3432
EuiSpacer,
3533
EuiFormRow,
3634
} from '@elastic/eui';
3735
import { FormattedMessage } from '@kbn/i18n/react';
3836
import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public';
3937
import { getSupportedFieldsByMetricType } from '../lib/get_supported_fields_by_metric_type';
38+
import { getDataStart } from '../../../services';
39+
import { QueryBarWrapper } from '../query_bar_wrapper';
4040

4141
const isFieldHistogram = (fields, indexPattern, field) => {
4242
const indexFields = fields[indexPattern];
@@ -49,15 +49,24 @@ const isFieldHistogram = (fields, indexPattern, field) => {
4949
export const FilterRatioAgg = (props) => {
5050
const { series, fields, panel } = props;
5151

52-
const handleChange = createChangeHandler(props.onChange, props.model);
52+
const handleChange = useMemo(() => createChangeHandler(props.onChange, props.model), [
53+
props.model,
54+
props.onChange,
55+
]);
5356
const handleSelectChange = createSelectHandler(handleChange);
54-
const handleTextChange = createTextHandler(handleChange);
57+
const handleNumeratorQueryChange = useCallback((query) => handleChange({ numerator: query }), [
58+
handleChange,
59+
]);
60+
const handleDenominatorQueryChange = useCallback(
61+
(query) => handleChange({ denominator: query }),
62+
[handleChange]
63+
);
5564
const indexPattern =
5665
(series.override_index_pattern && series.series_index_pattern) || panel.index_pattern;
5766

5867
const defaults = {
59-
numerator: '*',
60-
denominator: '*',
68+
numerator: getDataStart().query.queryString.getDefaultQuery(),
69+
denominator: getDataStart().query.queryString.getDefaultQuery(),
6170
metric_agg: 'count',
6271
};
6372

@@ -101,7 +110,11 @@ export const FilterRatioAgg = (props) => {
101110
/>
102111
}
103112
>
104-
<EuiFieldText onChange={handleTextChange('numerator')} value={model.numerator} />
113+
<QueryBarWrapper
114+
query={model.numerator}
115+
onChange={handleNumeratorQueryChange}
116+
indexPatterns={[indexPattern]}
117+
/>
105118
</EuiFormRow>
106119
</EuiFlexItem>
107120

@@ -115,7 +128,11 @@ export const FilterRatioAgg = (props) => {
115128
/>
116129
}
117130
>
118-
<EuiFieldText onChange={handleTextChange('denominator')} value={model.denominator} />
131+
<QueryBarWrapper
132+
query={model.denominator}
133+
onChange={handleDenominatorQueryChange}
134+
indexPatterns={[indexPattern]}
135+
/>
119136
</EuiFormRow>
120137
</EuiFlexItem>
121138
</EuiFlexGroup>

src/plugins/vis_type_timeseries/public/application/components/aggs/filter_ratio.test.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,16 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers';
2222
import { FilterRatioAgg } from './filter_ratio';
2323
import { FIELDS, METRIC, SERIES, PANEL } from '../../../test_utils';
2424
import { EuiComboBox } from '@elastic/eui';
25+
import { dataPluginMock } from '../../../../../data/public/mocks';
26+
import { setDataStart } from '../../../services';
27+
28+
jest.mock('../query_bar_wrapper', () => ({
29+
QueryBarWrapper: jest.fn(() => null),
30+
}));
2531

2632
describe('TSVB Filter Ratio', () => {
33+
beforeAll(() => setDataStart(dataPluginMock.createStartContract()));
34+
2735
const setup = (metric) => {
2836
const series = { ...SERIES, metrics: [metric] };
2937
const panel = { ...PANEL, series };

src/plugins/vis_type_timeseries/public/application/components/aggs/histogram_support.test.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers';
2222
import { Agg } from './agg';
2323
import { FieldSelect } from './field_select';
2424
import { FIELDS, METRIC, SERIES, PANEL } from '../../../test_utils';
25+
import { setDataStart } from '../../../services';
26+
import { dataPluginMock } from '../../../../../data/public/mocks';
27+
28+
jest.mock('../query_bar_wrapper', () => ({
29+
QueryBarWrapper: jest.fn(() => null),
30+
}));
31+
2532
const runTest = (aggType, name, test, additionalProps = {}) => {
2633
describe(aggType, () => {
2734
const metric = {
@@ -55,6 +62,8 @@ const runTest = (aggType, name, test, additionalProps = {}) => {
5562
};
5663

5764
describe('Histogram Types', () => {
65+
beforeAll(() => setDataStart(dataPluginMock.createStartContract()));
66+
5867
describe('supported', () => {
5968
const shouldHaveHistogramSupport = (aggType, additionalProps = {}) => {
6069
runTest(

src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,22 @@
2020
const filter = (metric) => metric.type === 'filter_ratio';
2121
import { bucketTransform } from '../../helpers/bucket_transform';
2222
import { overwrite } from '../../helpers';
23+
import { esQuery } from '../../../../../../data/server';
2324

24-
export function ratios(req, panel, series) {
25+
export function ratios(req, panel, series, esQueryConfig, indexPatternObject) {
2526
return (next) => (doc) => {
2627
if (series.metrics.some(filter)) {
2728
series.metrics.filter(filter).forEach((metric) => {
28-
overwrite(doc, `aggs.${series.id}.aggs.timeseries.aggs.${metric.id}-numerator.filter`, {
29-
query_string: { query: metric.numerator || '*', analyze_wildcard: true },
30-
});
31-
overwrite(doc, `aggs.${series.id}.aggs.timeseries.aggs.${metric.id}-denominator.filter`, {
32-
query_string: { query: metric.denominator || '*', analyze_wildcard: true },
33-
});
29+
overwrite(
30+
doc,
31+
`aggs.${series.id}.aggs.timeseries.aggs.${metric.id}-numerator.filter`,
32+
esQuery.buildEsQuery(indexPatternObject, metric.numerator, [], esQueryConfig)
33+
);
34+
overwrite(
35+
doc,
36+
`aggs.${series.id}.aggs.timeseries.aggs.${metric.id}-denominator.filter`,
37+
esQuery.buildEsQuery(indexPatternObject, metric.denominator, [], esQueryConfig)
38+
);
3439

3540
let numeratorPath = `${metric.id}-numerator>_count`;
3641
let denominatorPath = `${metric.id}-denominator>_count`;

src/plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.test.js

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919

2020
import { ratios } from './filter_ratios';
2121

22-
describe('ratios(req, panel, series)', () => {
22+
describe('ratios(req, panel, series, esQueryConfig, indexPatternObject)', () => {
2323
let panel;
2424
let series;
2525
let req;
26+
let esQueryConfig;
27+
let indexPatternObject;
2628
beforeEach(() => {
2729
panel = {
2830
time_field: 'timestamp',
@@ -36,8 +38,8 @@ describe('ratios(req, panel, series)', () => {
3638
{
3739
id: 'metric-1',
3840
type: 'filter_ratio',
39-
numerator: 'errors',
40-
denominator: '*',
41+
numerator: { query: 'errors', language: 'lucene' },
42+
denominator: { query: 'warnings', language: 'lucene' },
4143
metric_agg: 'avg',
4244
field: 'cpu',
4345
},
@@ -51,17 +53,23 @@ describe('ratios(req, panel, series)', () => {
5153
},
5254
},
5355
};
56+
esQueryConfig = {
57+
allowLeadingWildcards: true,
58+
queryStringOptions: { analyze_wildcard: true },
59+
ignoreFilterIfFieldNotInIndex: false,
60+
};
61+
indexPatternObject = {};
5462
});
5563

5664
test('calls next when finished', () => {
5765
const next = jest.fn();
58-
ratios(req, panel, series)(next)({});
66+
ratios(req, panel, series, esQueryConfig, indexPatternObject)(next)({});
5967
expect(next.mock.calls.length).toEqual(1);
6068
});
6169

6270
test('returns filter ratio aggs', () => {
6371
const next = (doc) => doc;
64-
const doc = ratios(req, panel, series)(next)({});
72+
const doc = ratios(req, panel, series, esQueryConfig, indexPatternObject)(next)({});
6573
expect(doc).toEqual({
6674
aggs: {
6775
test: {
@@ -88,9 +96,18 @@ describe('ratios(req, panel, series)', () => {
8896
},
8997
},
9098
filter: {
91-
query_string: {
92-
analyze_wildcard: true,
93-
query: '*',
99+
bool: {
100+
must: [
101+
{
102+
query_string: {
103+
query: 'warnings',
104+
analyze_wildcard: true,
105+
},
106+
},
107+
],
108+
filter: [],
109+
should: [],
110+
must_not: [],
94111
},
95112
},
96113
},
@@ -103,9 +120,18 @@ describe('ratios(req, panel, series)', () => {
103120
},
104121
},
105122
filter: {
106-
query_string: {
107-
analyze_wildcard: true,
108-
query: 'errors',
123+
bool: {
124+
must: [
125+
{
126+
query_string: {
127+
query: 'errors',
128+
analyze_wildcard: true,
129+
},
130+
},
131+
],
132+
filter: [],
133+
should: [],
134+
must_not: [],
109135
},
110136
},
111137
},
@@ -120,7 +146,7 @@ describe('ratios(req, panel, series)', () => {
120146
test('returns empty object when field is not set', () => {
121147
delete series.metrics[0].field;
122148
const next = (doc) => doc;
123-
const doc = ratios(req, panel, series)(next)({});
149+
const doc = ratios(req, panel, series, esQueryConfig, indexPatternObject)(next)({});
124150
expect(doc).toEqual({
125151
aggs: {
126152
test: {
@@ -141,18 +167,36 @@ describe('ratios(req, panel, series)', () => {
141167
'metric-1-denominator': {
142168
aggs: { metric: {} },
143169
filter: {
144-
query_string: {
145-
analyze_wildcard: true,
146-
query: '*',
170+
bool: {
171+
must: [
172+
{
173+
query_string: {
174+
query: 'warnings',
175+
analyze_wildcard: true,
176+
},
177+
},
178+
],
179+
filter: [],
180+
should: [],
181+
must_not: [],
147182
},
148183
},
149184
},
150185
'metric-1-numerator': {
151186
aggs: { metric: {} },
152187
filter: {
153-
query_string: {
154-
analyze_wildcard: true,
155-
query: 'errors',
188+
bool: {
189+
must: [
190+
{
191+
query_string: {
192+
analyze_wildcard: true,
193+
query: 'errors',
194+
},
195+
},
196+
],
197+
filter: [],
198+
should: [],
199+
must_not: [],
156200
},
157201
},
158202
},

src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,4 +1544,38 @@ describe('migration visualization', () => {
15441544
expect(series[0].split_color_mode).toBeUndefined();
15451545
});
15461546
});
1547+
1548+
describe('7.10.0 tsvb filter_ratio migration', () => {
1549+
const migrate = (doc: any) =>
1550+
visualizationSavedObjectTypeMigrations['7.10.0'](
1551+
doc as Parameters<SavedObjectMigrationFn>[0],
1552+
savedObjectMigrationContext
1553+
);
1554+
1555+
const testDoc1 = {
1556+
attributes: {
1557+
title: 'My Vis',
1558+
description: 'This is my super cool vis.',
1559+
visState: `{"type":"metrics","params":{"id":"61ca57f0-469d-11e7-af02-69e470af7417","type":"timeseries",
1560+
"series":[{"id":"61ca57f1-469d-11e7-af02-69e470af7417","metrics":[{"id":"61ca57f2-469d-11e7-af02-69e470af7417",
1561+
"type":"filter_ratio","numerator":"Filter Bytes Test:>1000","denominator":"Filter Bytes Test:<1000"}]}]}}`,
1562+
},
1563+
};
1564+
1565+
it('should replace numerator string with a query object', () => {
1566+
const migratedTestDoc1 = migrate(testDoc1);
1567+
const metric = JSON.parse(migratedTestDoc1.attributes.visState).params.series[0].metrics[0];
1568+
1569+
expect(metric.numerator).toHaveProperty('query');
1570+
expect(metric.numerator).toHaveProperty('language');
1571+
});
1572+
1573+
it('should replace denominator string with a query object', () => {
1574+
const migratedTestDoc1 = migrate(testDoc1);
1575+
const metric = JSON.parse(migratedTestDoc1.attributes.visState).params.series[0].metrics[0];
1576+
1577+
expect(metric.denominator).toHaveProperty('query');
1578+
expect(metric.denominator).toHaveProperty('language');
1579+
});
1580+
});
15471581
});

0 commit comments

Comments
 (0)