Skip to content

Commit 0d49655

Browse files
committed
Rollup support for TSVB (elastic#28762)
* Added a feature of rollup search on the UI side Signed-off-by: Alexey Antonov <[email protected]> * Rollup Feature - initial commit * Revert "Added a feature of rollup search on the UI side" This reverts commit 9568b0970b16f5102f50b748bb4d691a8612c2c2. # Conflicts: # src/legacy/core_plugins/metrics/public/components/index_pattern.js * Remove the 'label' property from the search strategies * Changed search by strategy from the last * add single search request * rollup_search_strategy add base implementation of isViable method * rollup_search_strategy add base implementation of isViable method -fix * Changed requests due to search request type * refactoring of import Base classes / remove '../../../../../../ * remove extra await * rollup_search_strategy. Refactoring of isRollupJobExists method * remove question * Add support of annotations and table data * skeleton for adding Search Strategy restrictions * Add rollup search capabilities * apply search strategy for annotations request * set fields capabilities for rollup strategy * add timezone, interval into SearchCapabilities * Add fields from capabilities * add timezone, interval into SearchCapabilities * fix default timezone * Merging of two Rollup Jobs was removed * move getFieldsForWildcard to searchStrategy * Fix TSVB search requests should have a timeout # Conflicts: # src/legacy/core_plugins/metrics/server/lib/vis_data/get_annotations.js # src/legacy/core_plugins/metrics/server/lib/vis_data/series/get_request_params.js * Add unit test * apply getEsShardTimeout for annorations/get_request_params, series/get_request_params * rename metrics -> tsvb * search_strategies_register refactoring: move 'add' method from class * Add merge rollup capabilities with fields * Add merge rollup capabilities with fields - small fixes * Add support of 'Everything' aggregation for Rollup Search * Return back metrics plugin * remove 'metrics' from the X-pack\rollup require * Fix test cases * fix broken test: fail: "apis InfraOps GraphQL Endpoints metrics should basically work" * rollup search - split by terms is not working * Add count metric * /get_bucket_size.js. Add support of 'auto' interval, Add support of gte intervals e.g.: >=1m * fix build_request_body test * [Rollup] [Phase 1] Error handling - rollup search errors should be more user friendly * [Rollup] [Phase 1] Table View - research the query to ES - sorting is not wokring * Merge elastic#26006 into rollup # Conflicts: # src/legacy/core_plugins/metrics/server/lib/vis_data/annorations/build_request_body.js # src/legacy/core_plugins/metrics/server/lib/vis_data/get_annotations.js # src/legacy/core_plugins/metrics/server/lib/vis_data/get_series_data.js # src/legacy/core_plugins/metrics/server/lib/vis_data/get_table_data.js # src/legacy/core_plugins/metrics/server/lib/vis_data/request_processors/annotations/query.js # src/legacy/core_plugins/metrics/server/lib/vis_data/series/__tests__/build_request_body.js # src/legacy/core_plugins/metrics/server/lib/vis_data/series/build_request_body.js # src/legacy/core_plugins/metrics/server/lib/vis_data/series/get_request_params.js * Add table view support * fix broken build * fix broken build * [Rollup] [Phase 1] - write new tests (rollup_search_request, rollup_search_strategy) * [Rollup] [Phase 1] - write tests for rollup_search_capabilities * Add test on default_search_capabilities, abstract_search_strategy, search_strategies_register * Add test cases for search_requests folder * [Rollup] [Phase 1] - write tests for rollup_search_strategy * FIx broken build * remove todo * fix calculation of interval value for rollup search * add unit tests * Remove default exports * fix PR comments * fix calendar intervals
1 parent 7f7f48b commit 0d49655

File tree

73 files changed

+2149
-362
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+2149
-362
lines changed

src/legacy/core_plugins/metrics/index.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ import { resolve } from 'path';
2121

2222
import fieldsRoutes from './server/routes/fields';
2323
import visDataRoutes from './server/routes/vis';
24+
import { SearchStrategiesRegister } from './server/lib/search_strategies/search_strategies_register';
2425

2526
export default function (kibana) {
2627
return new kibana.Plugin({
2728
require: ['kibana', 'elasticsearch'],
2829

2930
uiExports: {
3031
visTypes: [
31-
'plugins/metrics/kbn_vis_types'
32+
'plugins/metrics/kbn_vis_types',
3233
],
3334
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
3435
},
@@ -37,16 +38,15 @@ export default function (kibana) {
3738
return Joi.object({
3839
enabled: Joi.boolean().default(true),
3940
chartResolution: Joi.number().default(150),
40-
minimumBucketSize: Joi.number().default(10)
41+
minimumBucketSize: Joi.number().default(10),
4142
}).default();
4243
},
4344

44-
4545
init(server) {
4646
fieldsRoutes(server);
4747
visDataRoutes(server);
48-
}
49-
5048

49+
SearchStrategiesRegister.init(server);
50+
},
5151
});
5252
}
Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
.tvbError__additional,
2-
.tvbError__stack {
3-
margin-top: $euiSizeS;
4-
}
5-
6-
// EUITODO: Convert to EuiCodeBlock
7-
.tvbError__stack {
8-
padding: $euiSizeS;
9-
background: $euiCodeBlockBackgroundColor;
10-
color: $euiCodeBlockColor;
11-
line-height: $euiLineHeight;
12-
font-family: $euiCodeFontFamily;
13-
font-weight: $euiFontWeightRegular;
14-
white-space: pre-wrap;
15-
}
1+
.tvbError__title,
2+
.tvbError__additional,
3+
.tvbError__stack {
4+
margin-top: $euiSizeS;
5+
}
6+
7+
// EUITODO: Convert to EuiCodeBlock
8+
.tvbError__stack {
9+
padding: $euiSizeS;
10+
background: $euiCodeBlockBackgroundColor;
11+
color: $euiCodeBlockColor;
12+
line-height: $euiLineHeight;
13+
font-family: $euiCodeFontFamily;
14+
font-weight: $euiFontWeightRegular;
15+
white-space: pre-wrap;
16+
}

src/legacy/core_plugins/metrics/public/components/error.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,23 @@ import React from 'react';
2323
import _ from 'lodash';
2424
import { FormattedMessage } from '@kbn/i18n/react';
2525

26+
const guidPattern = /\[[[a-f\d-\\]{36}\]/g;
27+
2628
function ErrorComponent(props) {
2729
const { error } = props;
2830
let additionalInfo;
29-
const type = _.get(error, 'error.caused_by.type');
31+
const type = _.get(error, 'error.caused_by.type') || _.get(error, 'error.type');
3032
let reason = _.get(error, 'error.caused_by.reason');
3133
const title = _.get(error, 'error.caused_by.title');
3234

3335
if (!reason) {
3436
reason = _.get(error, 'message');
3537
}
3638

39+
if (['runtime_exception', 'illegal_argument_exception'].includes(type)) {
40+
reason = _.get(error, 'error.reason').replace(guidPattern, ``);
41+
}
42+
3743
if (type === 'script_exception') {
3844
const scriptStack = _.get(error, 'error.caused_by.script_stack');
3945
reason = _.get(error, 'error.caused_by.caused_by.reason');

src/legacy/core_plugins/metrics/public/components/visualization.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import topN from './vis_types/top_n/vis';
2727
import table from './vis_types/table/vis';
2828
import gauge from './vis_types/gauge/vis';
2929
import markdown from './vis_types/markdown/vis';
30-
import Error from './error';
30+
import ErrorComponent from './error';
3131
import NoData from './no_data';
3232

3333
const types = {
@@ -46,7 +46,7 @@ function Visualization(props) {
4646
if (error) {
4747
return (
4848
<div className={props.className}>
49-
<Error error={error} />
49+
<ErrorComponent error={error} />
5050
</div>
5151
);
5252
}

src/legacy/core_plugins/metrics/server/lib/get_fields.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19-
19+
import { SearchStrategiesRegister } from './search_strategies/search_strategies_register';
2020
import { uniq } from 'lodash';
2121

2222
export async function getFields(req) {
23-
const { indexPatternsService } = req.pre;
24-
const index = req.query.index || '*';
25-
const resp = await indexPatternsService.getFieldsForWildcard({ pattern: index });
26-
const fields = resp.filter(field => field.aggregatable);
23+
const indexPattern = req.query.index || '*';
24+
const { searchStrategy, capabilities } = await SearchStrategiesRegister.getViableStrategy(req, indexPattern);
25+
26+
const fields = (await searchStrategy
27+
.getFieldsForWildcard(req, indexPattern, capabilities))
28+
.filter(field => field.aggregatable);
29+
2730
return uniq(fields, field => field.name);
2831
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
import { convertIntervalToUnit, parseInterval } from '../vis_data/helpers/unit_to_seconds';
20+
21+
const getTimezoneFromRequest = request => {
22+
return request.payload.timerange.timezone;
23+
};
24+
25+
export class DefaultSearchCapabilities {
26+
constructor(request, batchRequestsSupport, fieldsCapabilities = {}) {
27+
this.request = request;
28+
this.batchRequestsSupport = batchRequestsSupport;
29+
this.fieldsCapabilities = fieldsCapabilities;
30+
}
31+
32+
get defaultTimeInterval() {
33+
return null;
34+
}
35+
36+
get searchTimezone() {
37+
return getTimezoneFromRequest(this.request);
38+
}
39+
40+
parseInterval(interval) {
41+
return parseInterval(interval);
42+
}
43+
44+
convertIntervalToUnit(intervalString, unit) {
45+
const parsedInterval = this.parseInterval(intervalString);
46+
47+
if (parsedInterval.unit !== unit) {
48+
return convertIntervalToUnit(intervalString, unit);
49+
}
50+
51+
return parsedInterval;
52+
}
53+
54+
getValidTimeInterval(intervalString) {
55+
// Default search capabilities doesn't have any restrictions for the interval string
56+
return intervalString;
57+
}
58+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
import { DefaultSearchCapabilities } from './default_search_capabilities';
20+
21+
describe('DefaultSearchCapabilities', () => {
22+
let defaultSearchCapabilities;
23+
let batchRequestsSupport;
24+
let req;
25+
26+
beforeEach(() => {
27+
req = {};
28+
batchRequestsSupport = true;
29+
defaultSearchCapabilities = new DefaultSearchCapabilities(req, batchRequestsSupport);
30+
});
31+
32+
test('should init default search capabilities', () => {
33+
expect(defaultSearchCapabilities.request).toBe(req);
34+
expect(defaultSearchCapabilities.batchRequestsSupport).toBe(batchRequestsSupport);
35+
expect(defaultSearchCapabilities.fieldsCapabilities).toEqual({});
36+
});
37+
38+
test('should return defaultTimeInterval', () => {
39+
expect(defaultSearchCapabilities.defaultTimeInterval).toBe(null);
40+
});
41+
42+
test('should return Search Timezone', () => {
43+
defaultSearchCapabilities.request = {
44+
payload: {
45+
timerange: {
46+
timezone: 'UTC'
47+
}
48+
}
49+
};
50+
51+
expect(defaultSearchCapabilities.searchTimezone).toEqual('UTC');
52+
});
53+
54+
test('should return a valid time interval', () => {
55+
expect(defaultSearchCapabilities.getValidTimeInterval('20m')).toBe('20m');
56+
});
57+
58+
test('should parse interval', () => {
59+
expect(defaultSearchCapabilities.parseInterval('120s')).toEqual({
60+
value: 120,
61+
unit: 's'
62+
});
63+
64+
expect(defaultSearchCapabilities.parseInterval('20m')).toEqual({
65+
value: 20,
66+
unit: 'm'
67+
});
68+
69+
expect(defaultSearchCapabilities.parseInterval('1y')).toEqual({
70+
value: 1,
71+
unit: 'y'
72+
});
73+
});
74+
75+
test('should convert interval string into different unit', () => {
76+
expect(defaultSearchCapabilities.convertIntervalToUnit('120s', 's')).toEqual({
77+
value: 120,
78+
unit: 's'
79+
});
80+
81+
expect(defaultSearchCapabilities.convertIntervalToUnit('60m', 'h')).toEqual({
82+
value: 1,
83+
unit: 'h'
84+
});
85+
86+
expect(defaultSearchCapabilities.convertIntervalToUnit('4w', 'M')).toEqual({
87+
value: 1,
88+
unit: 'M'
89+
});
90+
91+
expect(defaultSearchCapabilities.convertIntervalToUnit('1y', 'w')).toEqual({
92+
value: 48,
93+
unit: 'w'
94+
});
95+
96+
expect(defaultSearchCapabilities.convertIntervalToUnit('60s', 'm')).toEqual({
97+
value: 1,
98+
unit: 'm'
99+
});
100+
101+
expect(defaultSearchCapabilities.convertIntervalToUnit('1s', 'ms')).toEqual({
102+
value: 1000,
103+
unit: 'ms'
104+
});
105+
});
106+
});
Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,4 @@
1717
* under the License.
1818
*/
1919

20-
export default [
21-
'std_deviation',
22-
'variance',
23-
'sum_of_squares'
24-
];
25-
26-
20+
export { SearchStrategiesRegister } from './search_strategies_register';
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
import { AbstractSearchStrategy } from './strategies/abstract_search_strategy';
20+
import { AbstractSearchRequest } from './searh_requests/abstract_request';
21+
import { DefaultSearchStrategy } from './strategies/default_search_strategy';
22+
import { DefaultSearchCapabilities } from './default_search_capabilities';
23+
24+
const strategies = [];
25+
26+
const addStrategy = searchStrategy => {
27+
if (searchStrategy instanceof AbstractSearchStrategy) {
28+
strategies.unshift(searchStrategy);
29+
}
30+
return strategies;
31+
};
32+
33+
export class SearchStrategiesRegister {
34+
static init(server) {
35+
server.expose('AbstractSearchStrategy', AbstractSearchStrategy);
36+
server.expose('AbstractSearchRequest', AbstractSearchRequest);
37+
server.expose('DefaultSearchCapabilities', DefaultSearchCapabilities);
38+
server.expose('addSearchStrategy', searchStrategy => addStrategy(searchStrategy));
39+
40+
addStrategy(new DefaultSearchStrategy(server));
41+
}
42+
43+
static async getViableStrategy(req, indexPattern) {
44+
for (const searchStrategy of strategies) {
45+
const { isViable, capabilities } = await searchStrategy.checkForViability(req, indexPattern);
46+
47+
if (isViable) {
48+
return {
49+
searchStrategy,
50+
capabilities,
51+
};
52+
}
53+
}
54+
}
55+
}

0 commit comments

Comments
 (0)