Skip to content

Commit

Permalink
KQL in TSVB (#36784)
Browse files Browse the repository at this point in the history
* Squashed commit of the following:

commit 0cff8243a99ff23421c3cbb15d62b26332a31e21
Author: Christiane Heiligers <christiane.heiligers@elastic.co>
Date:   Mon May 20 16:16:15 2019 -0700

    Adds ignore_lobal_filter YesNo component back

    Deletes fixtures file

commit e54e1690c9c46e2de5e747d14030a7e27e74c138
Author: Christiane Heiligers <christiane.heiligers@elastic.co>
Date:   Mon May 20 14:39:21 2019 -0700

    squash-merges adding KQL with the full QueryBar and SavedObject migrations

Debugging

Adds filter defaults

Updates index pattern for the QueryBar on componentDidUpdate

squash-merges adding KQL with the full QueryBar and SavedObject migrations required

Adds ignore_lobal_filter YesNo component back

Deletes fixtures file

Updates migration test number

* Changes QueryBar to QueryBarInput in TimeseriesPanelConfigUi

Replaces QueryBar with QueryBarInput in Timeseries

Changes QueryBar to QueryBarInput in TSVB Metric

Changes QueryBar to QueryBarInput in other TSVB visualisation editors

Removes async from componentWillMount

changes indexPattern to indexPatternObject in series and table query request processors

* Squashed commit of the following:

commit 2c3107963cff20b2d24323fddfaf80c848b86848
Author: Christiane Heiligers <christiane.heiligers@elastic.co>
Date:   Thu May 23 15:15:06 2019 -0700

    Cleans up comments

commit 943150d4b9b71183e9d5b26c95449ea4519aad4c
Author: Christiane Heiligers <christiane.heiligers@elastic.co>
Date:   Thu May 23 14:09:53 2019 -0700

    Cleans up parser check more

commit d31de88525595720556440ae71515f5772774eaa
Author: Christiane Heiligers <christiane.heiligers@elastic.co>
Date:   Thu May 23 14:01:02 2019 -0700

    Cleans up parser check

commit b592071b1d939ec2df9b9c921d57a22dac54e703
Author: Christiane Heiligers <christiane.heiligers@elastic.co>
Date:   Thu May 23 13:16:55 2019 -0700

    skips server call on invalid kuery queries

Cleans up

Deletes duplicate migration function

Adds 's' to split_by_filters method

Resolves issues after rebase

* Removes quotes around index pattern strings passed into fetch_index_patterns, adds comments on reasoning

* queryBar to queryBarInput

Adds transformFilterStringToQueryObject back to visualizations migrations that was lost during rebase

* Deletes unused code

* Fixes migrations test

* compiles visualizations migrations for version 7.3.0

* temporary change to query_bar_input fetch_index_patterns file

formatting

Includes code from PR#37413

Clean up

* Formatting

* Uses series index pattern when override_index_pattern is selected and an index pattern is provided

* Removes empty object as alternative to localStorage

* Removes handelQueryChange from where it is not needed

* extracts retrieval of the default query language from uiSettings into a helper method

* Resolves some PR comments

* Handles Query Error from an invalid syntax query

* Converts string queries into objects with kuery as the language in saved_objects data for ecommerce, flights and logs TSVB visualizations

* Deletes unused translation items

* Adds optional id prop to the query_bar_input and makes the screenTitle optional

* Updates [eCommerce] Sold Products per Day

* Removes screenTitle from component QueryBarInput

* Wraps index pattern strings in a double quote to allow for names such as 'logstash-*'
  • Loading branch information
TinaHeiligers authored Jun 5, 2019
1 parent 02ff1ad commit 84b595e
Show file tree
Hide file tree
Showing 47 changed files with 614 additions and 224 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,6 @@ describe('QueryBarInput', () => {
intl={null as any}
/>
);

expect(mockFetchIndexPatterns).toHaveBeenCalledWith(['logstash-*']);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ interface Props {
intl: InjectedIntl;
query: Query;
appName: string;
id?: string;
disableAutoFocus?: boolean;
screenTitle: string;
screenTitle?: string;
prepend?: any;
store: Storage;
persistedLog?: PersistedLog;
Expand Down Expand Up @@ -434,6 +435,7 @@ export class QueryBarInputUI extends Component<Props, State> {
<div role="search">
<div className="kuiLocalSearchAssistedInput">
<EuiFieldText
id={this.props.id}
placeholder={this.props.intl.formatMessage({
id: 'data.query.queryBar.searchInputPlaceholder',
defaultMessage: 'Search',
Expand All @@ -452,17 +454,21 @@ export class QueryBarInputUI extends Component<Props, State> {
}}
autoComplete="off"
spellCheck={false}
aria-label={this.props.intl.formatMessage(
{
id: 'data.query.queryBar.searchInputAriaLabel',
defaultMessage:
'You are on search box of {previouslyTranslatedPageTitle} page. Start typing to search and filter the {pageType}',
},
{
previouslyTranslatedPageTitle: this.props.screenTitle,
pageType: this.props.appName,
}
)}
aria-label={
this.props.screenTitle
? this.props.intl.formatMessage(
{
id: 'data.query.queryBar.searchInputAriaLabel',
defaultMessage:
'You are on search box of {previouslyTranslatedPageTitle} page. Start typing to search and filter the {pageType}',
},
{
previouslyTranslatedPageTitle: this.props.screenTitle,
pageType: this.props.appName,
}
)
: undefined
}
type="text"
data-test-subj="queryInput"
aria-autocomplete="list"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export async function fetchIndexPatterns(indexPatternStrings: string[]) {
return [];
}

const searchString = indexPatternStrings.join(' | ');
const searchString = indexPatternStrings.map(string => `"${string}"`).join(' | ');
const indexPatternsFromSavedObjects = await chrome.getSavedObjectsClient().find({
type: 'index-pattern',
fields: ['title', 'fields'],
Expand Down
97 changes: 94 additions & 3 deletions src/legacy/core_plugins/kibana/migrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,6 @@ const migrateDateHistogramAggregation = doc => {
return doc;
};

const executeMigrations720 = flow(migratePercentileRankAggregation, migrateDateHistogramAggregation);

function removeDateHistogramTimeZones(doc) {
const visStateJSON = get(doc, 'attributes.visState');
if (visStateJSON) {
Expand Down Expand Up @@ -189,6 +187,99 @@ function migrateGaugeVerticalSplitToAlignment(doc) {
}
return doc;
}
// Migrate filters (string -> { query: string, language: lucene })
/*
Enabling KQL in TSVB causes problems with savedObject visualizations when these are saved with filters.
In a visualisation type of saved object, if the visState param is of type metric, the filter is saved as a string that is not interpretted correctly as a lucene query in the visualization itself.
We need to transform the filter string into an object containing the original string as a query and specify the query language as lucene.
For Metrics visualizations (param.type === "metric"), filters can be applied to each series object in the series array within the SavedObject.visState.params object.
Path to the series array is thus:
attributes.visState.
*/
function transformFilterStringToQueryObject(doc) {
// Migrate filters
// If any filters exist and they are a string, we assume it to be lucene and transform the filter into an object accordingly
const newDoc = cloneDeep(doc);
const visStateJSON = get(doc, 'attributes.visState');
if (visStateJSON) {
let visState;
try {
visState = JSON.parse(visStateJSON);
} catch (e) {
// let it go, the data is invalid and we'll leave it as is
}
if (visState) {
const visType = get(visState, 'params.type');
const tsvbTypes = ['metric', 'markdown', 'top_n', 'gauge', 'table', 'timeseries'];
if (tsvbTypes.indexOf(visType) === -1) {
// skip
return doc;
}
// migrate the params fitler
const params = get(visState, 'params');
if (params.filter && typeof params.filter === 'string') {
const paramsFilterObject = {
query: params.filter,
language: 'lucene',
};
params.filter = paramsFilterObject;
}

// migrate the annotations query string:
const annotations = get(visState, 'params.annotations') || [];
annotations.forEach((item) => {
if (!item.query_string) {
// we don't need to transform anything if there isn't a filter at all
return;
}
if (typeof item.query_string === 'string') {
const itemQueryStringObject = {
query: item.query_string,
language: 'lucene',
};
item.query_string = itemQueryStringObject;
}
});
// migrate the series filters
const series = get(visState, 'params.series') || [];
series.forEach((item) => {
if (!item.filter) {
// we don't need to transform anything if there isn't a filter at all
return;
}
// series item filter
if (typeof item.filter === 'string') {
const itemfilterObject = {
query: item.filter,
language: 'lucene',
};
item.filter = itemfilterObject;
}
// series item split filters filter
if (item.split_filters) {
const splitFilters = get(item, 'split_filters') || [];
splitFilters.forEach((filter) => {
if (!filter.filter) {
// we don't need to transform anything if there isn't a filter at all
return;
}
if (typeof filter.filter === 'string') {
const filterfilterObject = {
query: filter.filter,
language: 'lucene',
};
filter.filter = filterfilterObject;
}
});
}
});
newDoc.attributes.visState = JSON.stringify(visState);
}
}
return newDoc;
}
const executeMigrations720 = flow(migratePercentileRankAggregation, migrateDateHistogramAggregation);
const executeMigrations730 = flow(migrateGaugeVerticalSplitToAlignment, transformFilterStringToQueryObject);

export const migrations = {
'index-pattern': {
Expand Down Expand Up @@ -292,7 +383,7 @@ export const migrations = {
},
'7.0.1': removeDateHistogramTimeZones,
'7.2.0': doc => executeMigrations720(doc),
'7.3.0': migrateGaugeVerticalSplitToAlignment
'7.3.0': executeMigrations730
},
dashboard: {
'7.0.0': (doc) => {
Expand Down
73 changes: 73 additions & 0 deletions src/legacy/core_plugins/kibana/migrations.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,79 @@ Object {
`);
});
});
describe('7.3.0 tsvb', () => {
const migrate = doc => migrations.visualization['7.3.0'](doc);
const generateDoc = ({ params }) => ({
attributes: {
title: 'My Vis',
description: 'This is my super cool vis.',
visState: JSON.stringify({ params }),
uiStateJSON: '{}',
version: 1,
kibanaSavedObjectMeta: {
searchSourceJSON: '{}',
},
},
});
it('should change series item filters from a string into an object', () => {
const params = { type: 'metric', series: [{ filter: 'Filter Bytes Test:>1000' }] };
const testDoc1 = generateDoc({ params });
const migratedTestDoc1 = migrate(testDoc1);
const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series;
expect(series[0].filter).toHaveProperty('query');
expect(series[0].filter).toHaveProperty('language');
});
it('should not change a series item filter string in the object after migration', () => {
const markdownParams = {
type: 'markdown',
series: [
{
filter: 'Filter Bytes Test:>1000',
split_filters: [{ filter: 'bytes:>1000' }],
}
]
};
const markdownDoc = generateDoc({ params: markdownParams });
const migratedMarkdownDoc = migrate(markdownDoc);
const markdownSeries = JSON.parse(migratedMarkdownDoc.attributes.visState).params.series;
expect(markdownSeries[0].filter.query).toBe(JSON.parse(markdownDoc.attributes.visState).params.series[0].filter);
expect(markdownSeries[0].split_filters[0].filter.query)
.toBe(JSON.parse(markdownDoc.attributes.visState).params.series[0].split_filters[0].filter);
});
it('should change series item filters from a string into an object for all filters', () => {
const params = {
type: 'timeseries',
filter: 'bytes:>1000',
series: [
{
filter: 'Filter Bytes Test:>1000',
split_filters: [{ filter: 'bytes:>1000' }],
}
],
annotations: [{ query_string: 'bytes:>1000' }],
};
const timeSeriesDoc = generateDoc({ params: params });
const migratedtimeSeriesDoc = migrate(timeSeriesDoc);
const timeSeriesParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params;
expect(Object.keys(timeSeriesParams.series[0].filter)).toEqual(expect.arrayContaining(['query', 'language']));
expect(Object.keys(timeSeriesParams.series[0].split_filters[0].filter)).toEqual(expect.arrayContaining(['query', 'language']));
expect(Object.keys(timeSeriesParams.annotations[0].query_string)).toEqual(expect.arrayContaining(['query', 'language']));
});
it('should not fail on a metric visualization without a filter in a series item', () => {
const params = { type: 'metric', series: [{}, {}, {}] };
const testDoc1 = generateDoc({ params });
const migratedTestDoc1 = migrate(testDoc1);
const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series;
expect(series[2]).not.toHaveProperty('filter.query');
});
it('should not migrate a visualization of unknown type', () => {
const params = { type: 'unknown', series: [{ filter: 'foo:bar' }] };
const doc = generateDoc({ params });
const migratedDoc = migrate(doc);
const series = JSON.parse(migratedDoc.attributes.visState).params.series;
expect(series[0].filter).toEqual(params.series[0].filter);
});
});
});

describe('dashboard', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import { FieldSelect } from './aggs/field_select';
import uuid from 'uuid';
import { IconSelect } from './icon_select';
import { YesNo } from './yes_no';
import { Storage } from 'ui/storage';
import { data } from 'plugins/data';
const { QueryBarInput } = data.query.ui;
import { getDefaultQueryLanguage } from './lib/get_default_query_language';

import {
htmlIdGenerator,
Expand Down Expand Up @@ -58,8 +62,8 @@ function newAnnotation() {

const RESTRICT_FIELDS = [ES_TYPES.DATE];

const localStorage = new Storage(window.localStorage);
export class AnnotationsEditor extends Component {

constructor(props) {
super(props);
this.renderRow = this.renderRow.bind(this);
Expand All @@ -73,9 +77,20 @@ export class AnnotationsEditor extends Component {
handleChange(_.assign({}, item, part));
};
}

handleQueryChange = (model, filter) => {
const part = { query_string: filter };
collectionActions.handleChange(this.props, {
...model,
...part
});
};
renderRow(row) {
const defaults = { fields: '', template: '', index_pattern: '*', query_string: '' };
const defaults = {
fields: '',
template: '',
index_pattern: '*',
query_string: { query: '', language: getDefaultQueryLanguage() }
};
const model = { ...defaults, ...row };
const handleChange = (part) => {
const fn = collectionActions.handleChange.bind(null, this.props);
Expand Down Expand Up @@ -154,10 +169,16 @@ export class AnnotationsEditor extends Component {
/>)}
fullWidth
>
<EuiFieldText
onChange={this.handleChange(model, 'query_string')}
value={model.query_string}
fullWidth
<QueryBarInput
query={{
language: model.query_string.language || getDefaultQueryLanguage(),
query: model.query_string.query || '',
}}
onChange={query => this.handleQueryChange(model, query)}
appName={'VisEditor'}
indexPatterns={[model.index_pattern]}
store={localStorage}
showDatePicker={false}
/>
</EuiFormRow>
</EuiFlexItem>
Expand Down Expand Up @@ -250,7 +271,6 @@ export class AnnotationsEditor extends Component {
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>

</EuiFlexItem>

<EuiFlexItem grow={false}>
Expand Down Expand Up @@ -312,7 +332,6 @@ export class AnnotationsEditor extends Component {
</div>
);
}

}

AnnotationsEditor.defaultProps = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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 chrome from 'ui/chrome';

export function getDefaultQueryLanguage() {
return chrome.getUiSettingsClient().get('search:queryLanguage');
}
Loading

0 comments on commit 84b595e

Please sign in to comment.