Skip to content

Commit

Permalink
KQL support in filter ratio in TSVB (elastic#75033)
Browse files Browse the repository at this point in the history
* KQL support in filter ratio in TSVB

Closes elastic#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>
  • Loading branch information
3 people committed Aug 27, 2020
1 parent 1c1dbe5 commit cecf3c9
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 42 deletions.
11 changes: 3 additions & 8 deletions src/plugins/vis_type_timeseries/common/vis_schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ export const metricsItems = schema.object({
field: stringOptionalNullable,
id: stringRequired,
metric_agg: stringOptionalNullable,
numerator: stringOptionalNullable,
denominator: stringOptionalNullable,
numerator: schema.maybe(queryObject),
denominator: schema.maybe(queryObject),
sigma: stringOptionalNullable,
unit: stringOptionalNullable,
model_type: stringOptionalNullable,
Expand Down Expand Up @@ -128,12 +128,7 @@ export const metricsItems = schema.object({
const splitFiltersItems = schema.object({
id: stringOptionalNullable,
color: stringOptionalNullable,
filter: schema.maybe(
schema.object({
language: schema.string(),
query: schema.string(),
})
),
filter: schema.maybe(queryObject),
label: stringOptionalNullable,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,25 @@
*/

import PropTypes from 'prop-types';
import React from 'react';
import React, { useCallback, useMemo } from 'react';
import { AggSelect } from './agg_select';
import { FieldSelect } from './field_select';
import { AggRow } from './agg_row';
import { createChangeHandler } from '../lib/create_change_handler';
import { createSelectHandler } from '../lib/create_select_handler';
import { createTextHandler } from '../lib/create_text_handler';
import {
htmlIdGenerator,
EuiFlexGroup,
EuiFlexItem,
EuiFormLabel,
EuiFieldText,
EuiSpacer,
EuiFormRow,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public';
import { getSupportedFieldsByMetricType } from '../lib/get_supported_fields_by_metric_type';
import { getDataStart } from '../../../services';
import { QueryBarWrapper } from '../query_bar_wrapper';

const isFieldHistogram = (fields, indexPattern, field) => {
const indexFields = fields[indexPattern];
Expand All @@ -49,15 +49,24 @@ const isFieldHistogram = (fields, indexPattern, field) => {
export const FilterRatioAgg = (props) => {
const { series, fields, panel } = props;

const handleChange = createChangeHandler(props.onChange, props.model);
const handleChange = useMemo(() => createChangeHandler(props.onChange, props.model), [
props.model,
props.onChange,
]);
const handleSelectChange = createSelectHandler(handleChange);
const handleTextChange = createTextHandler(handleChange);
const handleNumeratorQueryChange = useCallback((query) => handleChange({ numerator: query }), [
handleChange,
]);
const handleDenominatorQueryChange = useCallback(
(query) => handleChange({ denominator: query }),
[handleChange]
);
const indexPattern =
(series.override_index_pattern && series.series_index_pattern) || panel.index_pattern;

const defaults = {
numerator: '*',
denominator: '*',
numerator: getDataStart().query.queryString.getDefaultQuery(),
denominator: getDataStart().query.queryString.getDefaultQuery(),
metric_agg: 'count',
};

Expand Down Expand Up @@ -101,7 +110,11 @@ export const FilterRatioAgg = (props) => {
/>
}
>
<EuiFieldText onChange={handleTextChange('numerator')} value={model.numerator} />
<QueryBarWrapper
query={model.numerator}
onChange={handleNumeratorQueryChange}
indexPatterns={[indexPattern]}
/>
</EuiFormRow>
</EuiFlexItem>

Expand All @@ -115,7 +128,11 @@ export const FilterRatioAgg = (props) => {
/>
}
>
<EuiFieldText onChange={handleTextChange('denominator')} value={model.denominator} />
<QueryBarWrapper
query={model.denominator}
onChange={handleDenominatorQueryChange}
indexPatterns={[indexPattern]}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,16 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { FilterRatioAgg } from './filter_ratio';
import { FIELDS, METRIC, SERIES, PANEL } from '../../../test_utils';
import { EuiComboBox } from '@elastic/eui';
import { dataPluginMock } from '../../../../../data/public/mocks';
import { setDataStart } from '../../../services';

jest.mock('../query_bar_wrapper', () => ({
QueryBarWrapper: jest.fn(() => null),
}));

describe('TSVB Filter Ratio', () => {
beforeAll(() => setDataStart(dataPluginMock.createStartContract()));

const setup = (metric) => {
const series = { ...SERIES, metrics: [metric] };
const panel = { ...PANEL, series };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { Agg } from './agg';
import { FieldSelect } from './field_select';
import { FIELDS, METRIC, SERIES, PANEL } from '../../../test_utils';
import { setDataStart } from '../../../services';
import { dataPluginMock } from '../../../../../data/public/mocks';

jest.mock('../query_bar_wrapper', () => ({
QueryBarWrapper: jest.fn(() => null),
}));

const runTest = (aggType, name, test, additionalProps = {}) => {
describe(aggType, () => {
const metric = {
Expand Down Expand Up @@ -55,6 +62,8 @@ const runTest = (aggType, name, test, additionalProps = {}) => {
};

describe('Histogram Types', () => {
beforeAll(() => setDataStart(dataPluginMock.createStartContract()));

describe('supported', () => {
const shouldHaveHistogramSupport = (aggType, additionalProps = {}) => {
runTest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,22 @@
const filter = (metric) => metric.type === 'filter_ratio';
import { bucketTransform } from '../../helpers/bucket_transform';
import { overwrite } from '../../helpers';
import { esQuery } from '../../../../../../data/server';

export function ratios(req, panel, series) {
export function ratios(req, panel, series, esQueryConfig, indexPatternObject) {
return (next) => (doc) => {
if (series.metrics.some(filter)) {
series.metrics.filter(filter).forEach((metric) => {
overwrite(doc, `aggs.${series.id}.aggs.timeseries.aggs.${metric.id}-numerator.filter`, {
query_string: { query: metric.numerator || '*', analyze_wildcard: true },
});
overwrite(doc, `aggs.${series.id}.aggs.timeseries.aggs.${metric.id}-denominator.filter`, {
query_string: { query: metric.denominator || '*', analyze_wildcard: true },
});
overwrite(
doc,
`aggs.${series.id}.aggs.timeseries.aggs.${metric.id}-numerator.filter`,
esQuery.buildEsQuery(indexPatternObject, metric.numerator, [], esQueryConfig)
);
overwrite(
doc,
`aggs.${series.id}.aggs.timeseries.aggs.${metric.id}-denominator.filter`,
esQuery.buildEsQuery(indexPatternObject, metric.denominator, [], esQueryConfig)
);

let numeratorPath = `${metric.id}-numerator>_count`;
let denominatorPath = `${metric.id}-denominator>_count`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@

import { ratios } from './filter_ratios';

describe('ratios(req, panel, series)', () => {
describe('ratios(req, panel, series, esQueryConfig, indexPatternObject)', () => {
let panel;
let series;
let req;
let esQueryConfig;
let indexPatternObject;
beforeEach(() => {
panel = {
time_field: 'timestamp',
Expand All @@ -36,8 +38,8 @@ describe('ratios(req, panel, series)', () => {
{
id: 'metric-1',
type: 'filter_ratio',
numerator: 'errors',
denominator: '*',
numerator: { query: 'errors', language: 'lucene' },
denominator: { query: 'warnings', language: 'lucene' },
metric_agg: 'avg',
field: 'cpu',
},
Expand All @@ -51,17 +53,23 @@ describe('ratios(req, panel, series)', () => {
},
},
};
esQueryConfig = {
allowLeadingWildcards: true,
queryStringOptions: { analyze_wildcard: true },
ignoreFilterIfFieldNotInIndex: false,
};
indexPatternObject = {};
});

test('calls next when finished', () => {
const next = jest.fn();
ratios(req, panel, series)(next)({});
ratios(req, panel, series, esQueryConfig, indexPatternObject)(next)({});
expect(next.mock.calls.length).toEqual(1);
});

test('returns filter ratio aggs', () => {
const next = (doc) => doc;
const doc = ratios(req, panel, series)(next)({});
const doc = ratios(req, panel, series, esQueryConfig, indexPatternObject)(next)({});
expect(doc).toEqual({
aggs: {
test: {
Expand All @@ -88,9 +96,18 @@ describe('ratios(req, panel, series)', () => {
},
},
filter: {
query_string: {
analyze_wildcard: true,
query: '*',
bool: {
must: [
{
query_string: {
query: 'warnings',
analyze_wildcard: true,
},
},
],
filter: [],
should: [],
must_not: [],
},
},
},
Expand All @@ -103,9 +120,18 @@ describe('ratios(req, panel, series)', () => {
},
},
filter: {
query_string: {
analyze_wildcard: true,
query: 'errors',
bool: {
must: [
{
query_string: {
query: 'errors',
analyze_wildcard: true,
},
},
],
filter: [],
should: [],
must_not: [],
},
},
},
Expand All @@ -120,7 +146,7 @@ describe('ratios(req, panel, series)', () => {
test('returns empty object when field is not set', () => {
delete series.metrics[0].field;
const next = (doc) => doc;
const doc = ratios(req, panel, series)(next)({});
const doc = ratios(req, panel, series, esQueryConfig, indexPatternObject)(next)({});
expect(doc).toEqual({
aggs: {
test: {
Expand All @@ -141,18 +167,36 @@ describe('ratios(req, panel, series)', () => {
'metric-1-denominator': {
aggs: { metric: {} },
filter: {
query_string: {
analyze_wildcard: true,
query: '*',
bool: {
must: [
{
query_string: {
query: 'warnings',
analyze_wildcard: true,
},
},
],
filter: [],
should: [],
must_not: [],
},
},
},
'metric-1-numerator': {
aggs: { metric: {} },
filter: {
query_string: {
analyze_wildcard: true,
query: 'errors',
bool: {
must: [
{
query_string: {
analyze_wildcard: true,
query: 'errors',
},
},
],
filter: [],
should: [],
must_not: [],
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1544,4 +1544,38 @@ describe('migration visualization', () => {
expect(series[0].split_color_mode).toBeUndefined();
});
});

describe('7.10.0 tsvb filter_ratio migration', () => {
const migrate = (doc: any) =>
visualizationSavedObjectTypeMigrations['7.10.0'](
doc as Parameters<SavedObjectMigrationFn>[0],
savedObjectMigrationContext
);

const testDoc1 = {
attributes: {
title: 'My Vis',
description: 'This is my super cool vis.',
visState: `{"type":"metrics","params":{"id":"61ca57f0-469d-11e7-af02-69e470af7417","type":"timeseries",
"series":[{"id":"61ca57f1-469d-11e7-af02-69e470af7417","metrics":[{"id":"61ca57f2-469d-11e7-af02-69e470af7417",
"type":"filter_ratio","numerator":"Filter Bytes Test:>1000","denominator":"Filter Bytes Test:<1000"}]}]}}`,
},
};

it('should replace numerator string with a query object', () => {
const migratedTestDoc1 = migrate(testDoc1);
const metric = JSON.parse(migratedTestDoc1.attributes.visState).params.series[0].metrics[0];

expect(metric.numerator).toHaveProperty('query');
expect(metric.numerator).toHaveProperty('language');
});

it('should replace denominator string with a query object', () => {
const migratedTestDoc1 = migrate(testDoc1);
const metric = JSON.parse(migratedTestDoc1.attributes.visState).params.series[0].metrics[0];

expect(metric.denominator).toHaveProperty('query');
expect(metric.denominator).toHaveProperty('language');
});
});
});
Loading

0 comments on commit cecf3c9

Please sign in to comment.